2015-01-09 21:43:13 +00:00
|
|
|
|
#pragma once
|
|
|
|
|
|
|
|
|
|
#include <dlfcn.h>
|
|
|
|
|
|
|
|
|
|
#include <string>
|
|
|
|
|
#include <mutex>
|
|
|
|
|
#include <functional>
|
|
|
|
|
#include <unordered_set>
|
|
|
|
|
#include <unordered_map>
|
|
|
|
|
|
|
|
|
|
#include <Yandex/logger_useful.h>
|
|
|
|
|
#include <statdaemons/threadpool.hpp>
|
|
|
|
|
|
|
|
|
|
#include <DB/Core/Types.h>
|
|
|
|
|
#include <DB/Core/Exception.h>
|
|
|
|
|
#include <DB/Common/UInt128.h>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
namespace DB
|
|
|
|
|
{
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/** Позволяет открыть динамическую библиотеку и получить из неё указатель на функцию.
|
|
|
|
|
*/
|
|
|
|
|
class SharedLibrary : private boost::noncopyable
|
|
|
|
|
{
|
|
|
|
|
public:
|
|
|
|
|
SharedLibrary(const std::string & path)
|
|
|
|
|
{
|
|
|
|
|
handle = dlopen(path.c_str(), RTLD_LAZY);
|
|
|
|
|
if (!handle)
|
|
|
|
|
throw Exception(std::string("Cannot dlopen: ") + dlerror());
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
~SharedLibrary()
|
|
|
|
|
{
|
|
|
|
|
if (handle && dlclose(handle))
|
|
|
|
|
throw Exception("Cannot dlclose");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
template <typename Func>
|
|
|
|
|
Func get(const std::string & name)
|
|
|
|
|
{
|
|
|
|
|
dlerror();
|
|
|
|
|
|
|
|
|
|
Func res = reinterpret_cast<Func>(dlsym(handle, name.c_str()));
|
|
|
|
|
|
|
|
|
|
if (char * error = dlerror())
|
|
|
|
|
throw Exception(std::string("Cannot dlsym: ") + error);
|
|
|
|
|
|
|
|
|
|
return res;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private:
|
|
|
|
|
void * handle;
|
|
|
|
|
};
|
|
|
|
|
|
2015-01-11 02:00:26 +00:00
|
|
|
|
using SharedLibraryPtr = std::shared_ptr<SharedLibrary>;
|
|
|
|
|
|
2015-01-09 21:43:13 +00:00
|
|
|
|
|
|
|
|
|
/** Позволяет скомпилировать кусок кода, использующий заголовочные файлы сервера, в динамическую библиотеку.
|
|
|
|
|
* Ведёт статистику вызовов, и инициирует компиляцию только на N-ый по счёту вызов для одного ключа.
|
|
|
|
|
* Компиляция выполняется асинхронно, в отдельных потоках, если есть свободные потоки.
|
|
|
|
|
* NOTE: Нет очистки устаревших и ненужных результатов.
|
|
|
|
|
*/
|
|
|
|
|
class Compiler
|
|
|
|
|
{
|
|
|
|
|
public:
|
|
|
|
|
/** path - путь к директории с временными файлами - результатами компиляции.
|
|
|
|
|
* Результаты компиляции сохраняются при перезапуске сервера,
|
|
|
|
|
* но используют в качестве части ключа номер ревизии. То есть, устаревают при обновлении сервера.
|
|
|
|
|
*/
|
2015-01-10 02:30:03 +00:00
|
|
|
|
Compiler(const std::string & path_, size_t threads);
|
|
|
|
|
~Compiler();
|
2015-01-09 21:43:13 +00:00
|
|
|
|
|
2015-01-10 02:30:03 +00:00
|
|
|
|
using HashedKey = UInt128;
|
2015-01-11 02:00:26 +00:00
|
|
|
|
|
|
|
|
|
using CodeGenerator = std::function<std::string()>;
|
|
|
|
|
using ReadyCallback = std::function<void(SharedLibraryPtr&)>;
|
2015-01-09 21:43:13 +00:00
|
|
|
|
|
|
|
|
|
/** Увеличить счётчик для заданного ключа key на единицу.
|
|
|
|
|
* Если результат компиляции уже есть (уже открыт, или есть файл с библиотекой),
|
|
|
|
|
* то вернуть готовую SharedLibrary.
|
2015-01-18 01:18:39 +00:00
|
|
|
|
* Иначе, если min_count_to_compile == 0, то инициировать компиляцию в том же потоке, дождаться её, и вернуть результат.
|
2015-01-09 21:43:13 +00:00
|
|
|
|
* Иначе, если счётчик достиг min_count_to_compile,
|
|
|
|
|
* инициировать компиляцию в отдельном потоке, если есть свободные потоки, и вернуть nullptr.
|
|
|
|
|
* Иначе вернуть nullptr.
|
|
|
|
|
*/
|
2015-01-10 02:30:03 +00:00
|
|
|
|
SharedLibraryPtr getOrCount(
|
2015-01-09 21:43:13 +00:00
|
|
|
|
const std::string & key,
|
|
|
|
|
UInt32 min_count_to_compile,
|
2015-01-18 01:18:39 +00:00
|
|
|
|
const std::string & additional_compiler_flags,
|
2015-01-11 02:00:26 +00:00
|
|
|
|
CodeGenerator get_code,
|
|
|
|
|
ReadyCallback on_ready);
|
2015-01-09 21:43:13 +00:00
|
|
|
|
|
|
|
|
|
private:
|
|
|
|
|
using Counts = std::unordered_map<HashedKey, UInt32, UInt128Hash>;
|
2015-01-10 02:30:03 +00:00
|
|
|
|
using Libraries = std::unordered_map<HashedKey, SharedLibraryPtr, UInt128Hash>;
|
2015-01-09 21:43:13 +00:00
|
|
|
|
using Files = std::unordered_set<std::string>;
|
|
|
|
|
|
|
|
|
|
const std::string path;
|
|
|
|
|
boost::threadpool::pool pool;
|
|
|
|
|
|
|
|
|
|
/// Количество вызовов функции getOrCount.
|
|
|
|
|
Counts counts;
|
|
|
|
|
|
|
|
|
|
/// Скомпилированные и открытые библиотеки. Или nullptr для библиотек в процессе компиляции.
|
|
|
|
|
Libraries libraries;
|
|
|
|
|
|
|
|
|
|
/// Скомпилированные файлы, оставшиеся от предыдущих запусков, но ещё не открытые.
|
|
|
|
|
Files files;
|
|
|
|
|
|
|
|
|
|
std::mutex mutex;
|
|
|
|
|
|
|
|
|
|
Logger * log = &Logger::get("Compiler");
|
|
|
|
|
|
|
|
|
|
|
2015-01-18 01:18:39 +00:00
|
|
|
|
void compile(
|
|
|
|
|
HashedKey hashed_key,
|
|
|
|
|
std::string file_name,
|
|
|
|
|
const std::string & additional_compiler_flags,
|
|
|
|
|
CodeGenerator get_code,
|
|
|
|
|
ReadyCallback on_ready);
|
2015-01-09 21:43:13 +00:00
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
}
|