Я реализую механизм перезагрузки живого кода для программы в C, и у меня есть такая функция:
#include <sys/types.h>
#include <sys/stat.h>
#include <dlfcn.h>
void module_load(mod_t *mod) {
struct stat statbuf;
if (stat(mod->path, &statbuf) != 0) {
// ...
}
if (statbuf.st_mtime != mod->time) {
if (mod->code != NULL) { // THIS here seems unsafe
dlclose(mod->code);
}
mod->code = dlopen(mod->path, RTLD_GLOBAL | RTLD_LAZY);
if (mod->code != NULL) {
mod->foo = dlsym(mod->code, "foo");
mod->bar = dlsym(mod->code, "bar");
// ...
mod->time = statbuf.st_mtime;
}
}
}
И мои функции называются так:
mod->foo();
mod->bar();
Система работает нормально, функции корректно обновляются, но меня что-то беспокоит. Функция module_load
выполняется в al oop в отдельном потоке, поэтому обновление может произойти в любое время, и, хотя оно работает нормально, мне интересно, какие странные вещи произойдут, если библиотека будет обновлена, а функция из нее будучи вызванным.
Я знаю, что могу вызвать функцию без l oop в присоединяемом потоке, а затем ждать, пока она не завершится sh. Возможно, это намного безопаснее, но я бы не стал создавать новый поток и присоединяться к нему каждый раз.
Я попытался временно получить две «живые» копии библиотеки одновременно, чтобы старые функции могли все еще использоваться, пока загружались новые, но код продолжал использовать старые функции, независимо от того, какие изменения я сделал.
Как можно безопасно перезагрузить библиотеку и ее функции, желательно в отдельном виде нить?
Чтобы сохранить две живые копии, я попытался сделать что-то вроде этого:
...
void *new_code = dlopen(mod->path, RTLD_GLOBAL | RTLD_LAZY);
if (new_code != NULL) {
mod->foo = dlsym(new_code, "foo");
mod->bar = dlsym(new_code, "bar");
// ...
void *tmp = mod->code;
mod->code = new_code;
if (tmp != NULL) {
dlclose(tmp);
}
mod->time = statbuf.st_mtime;