#include "RuntimeModuleManager.h" #include #define _BSD_SOURCE // usleep() #include #include #include #include #include #include "RuntimeModule.h" #include #include #include "Globals.h" #include "Serializer.h" using namespace std; const char* state_file = "state.ser"; void RuntimeModuleManager::RegisterModule(const char* name) { RuntimeModule* module = new RuntimeModule(); module->name = name; mModules.push_back(module); } void RuntimeModuleManager::UnregisterModules() { UnloadModules(); for (int i = 0; i < mModules.size(); i++) { if (mModules[i]->handle) { mModules[i]->api.finalize(mModules[i]->state); mModules[i]->state = nullptr; dlclose(mModules[i]->handle); mModules[i]->handle = 0; mModules[i]->id = 0; delete mModules[i]; } } } void RuntimeModuleManager::LoadModule(RuntimeModule* module) { struct stat attr; bool stat_result = stat(module->name.c_str(), &attr); if (glfwGetKey(gWindow, GLFW_KEY_F11) == GLFW_PRESS) { gLog ("Module %s id = %d mtime %d", module->name.c_str(), module->id, module->mtime); } if ( stat_result == 0 && (module->id != attr.st_ino || module->mtime != attr.st_mtime) ) { gLog ("Loading module %s (size %d)", module->name.c_str(), attr.st_size); void *handle = dlopen(module->name.c_str(), RTLD_NOW | RTLD_GLOBAL); if (handle) { module->handle = handle; module->id = attr.st_ino; module->mtime = attr.st_mtime; module->mtimensec = attr.st_mtim.tv_nsec; const struct module_api *api = (module_api*) dlsym(module->handle, "MODULE_API"); if (api != NULL) { module->api = *api; if (module->state == NULL) { gLog("Initializing module %s", module->name.c_str()); module->state = module->api.init(); } gLog("Reloading module %s", module->name.c_str()); module->api.reload(module->state, gReadSerializer); } else { gLog("Loading module failed: Could not find API for module %s", module->name.c_str()); dlclose(module->handle); module->handle = NULL; module->id = 0; } } else { gLog ("Loading module failed: could not load shared library for module %s: %s", module->name.c_str(), dlerror()); module->handle = NULL; module->id = 0; abort(); } } } void RuntimeModuleManager::Update(float dt) { for (int i = mModules.size() - 1; i >= 0; i--) { if (mModules[i]->handle) { mModules[i]->api.step(mModules[i]->state, dt); } } } bool RuntimeModuleManager::CheckModulesChanged() { struct stat attr; double current_time = gGetTimeSinceStart(); for (int i = 0; i < mModules.size(); i++) { RuntimeModule* module = mModules[i]; bool stat_result = stat(module->name.c_str(), &attr); if (stat_result != 0) { gLog ("Error: could not stat module %s", module->name.c_str()); abort(); } if ( module->id != attr.st_ino || module->mtime != attr.st_mtime || module->fsize != attr.st_size || module->fsize == 0 ) { module->id = attr.st_ino; module->mtime = attr.st_mtime; module->mtimensec = attr.st_mtim.tv_nsec; module->fsize = attr.st_size; gLog ("Detected file change of %s: new size %d", module->name.c_str(), attr.st_size); gLog ("Triggering reload"); return true; } } return false; } void RuntimeModuleManager::UnloadModules() { gWriteSerializer->Open(state_file); for (int i = mModules.size() - 1; i >= 0 ; i--) { if (mModules[i]->handle) { gLog("Unloading module %s", mModules[i]->name.c_str()); mModules[i]->api.unload(mModules[i]->state, gWriteSerializer); mModules[i]->state = nullptr; gLog ("Unloading shared library %s", mModules[i]->name.c_str()); int unload_result = dlclose(mModules[i]->handle); if (unload_result != 0) { gLog ("Unload failed (code %d): %s", unload_result, dlerror()); } mModules[i]->handle = 0; mModules[i]->id = 0; gLog("Unloading module %s complete", mModules[i]->name.c_str()); } } gLog ("Writing state to file %s", state_file); gWriteSerializer->Close(); } void RuntimeModuleManager::LoadModules() { gLog ("Reading state from file %s", state_file); gReadSerializer->Open(state_file); for (int i = 0; i < mModules.size(); i++) { LoadModule(mModules[i]); } gReadSerializer->Close(); }