2016-09-16 17:29:17 +02:00
|
|
|
#include "RuntimeModuleManager.h"
|
2016-10-21 22:07:23 +02:00
|
|
|
#include <GLFW/glfw3.h>
|
2016-09-16 17:29:17 +02:00
|
|
|
|
|
|
|
#define _BSD_SOURCE // usleep()
|
|
|
|
#include <stdio.h>
|
|
|
|
#include <unistd.h>
|
|
|
|
#include <sys/types.h>
|
|
|
|
#include <sys/stat.h>
|
|
|
|
#include <dlfcn.h>
|
|
|
|
|
|
|
|
#include "RuntimeModule.h"
|
|
|
|
#include <iostream>
|
2016-11-20 21:50:53 +01:00
|
|
|
#include <fstream>
|
2016-09-16 17:29:17 +02:00
|
|
|
|
2016-10-21 22:07:23 +02:00
|
|
|
#include "Globals.h"
|
2016-11-25 22:27:37 +01:00
|
|
|
#include "Serializer.h"
|
2016-10-21 22:07:23 +02:00
|
|
|
|
2016-11-20 21:50:53 +01:00
|
|
|
using namespace std;
|
2016-11-25 22:27:37 +01:00
|
|
|
const char* state_file = "state.ser";
|
2016-11-20 21:50:53 +01:00
|
|
|
|
2016-09-16 17:29:17 +02:00
|
|
|
void RuntimeModuleManager::RegisterModule(const char* name) {
|
|
|
|
RuntimeModule* module = new RuntimeModule();
|
|
|
|
module->name = name;
|
|
|
|
mModules.push_back(module);
|
|
|
|
}
|
|
|
|
|
2016-11-25 22:27:37 +01:00
|
|
|
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];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-09-16 17:29:17 +02:00
|
|
|
void RuntimeModuleManager::LoadModule(RuntimeModule* module) {
|
|
|
|
struct stat attr;
|
2016-10-21 22:07:23 +02:00
|
|
|
|
|
|
|
bool stat_result = stat(module->name.c_str(), &attr);
|
|
|
|
if (glfwGetKey(gWindow, GLFW_KEY_F11) == GLFW_PRESS) {
|
|
|
|
std::cerr << "Module " << module->name << " id = " << module->id
|
|
|
|
<< " mtime " << ctime((time_t *) &module->mtime) << std::endl;
|
|
|
|
}
|
|
|
|
|
|
|
|
if ( stat_result == 0 &&
|
|
|
|
(module->id != attr.st_ino || module->mtime != attr.st_mtime)
|
|
|
|
) {
|
2017-02-05 10:37:49 +01:00
|
|
|
std::cout << "Opening module " << module->name
|
|
|
|
<< " (size = " << attr.st_size << ")" << std::endl;
|
2016-10-21 22:07:23 +02:00
|
|
|
void *handle = dlopen(module->name.c_str(), RTLD_NOW | RTLD_GLOBAL);
|
2016-09-16 17:29:17 +02:00
|
|
|
if (handle) {
|
|
|
|
module->handle = handle;
|
|
|
|
module->id = attr.st_ino;
|
2016-10-21 22:07:23 +02:00
|
|
|
module->mtime = attr.st_mtime;
|
2017-01-14 16:21:47 +01:00
|
|
|
module->mtimensec = attr.st_mtim.tv_nsec;
|
2016-09-16 17:29:17 +02:00
|
|
|
const struct module_api *api = (module_api*) dlsym(module->handle, "MODULE_API");
|
|
|
|
if (api != NULL) {
|
|
|
|
module->api = *api;
|
|
|
|
if (module->state == NULL) {
|
2016-10-21 22:07:23 +02:00
|
|
|
std::cout << "Initializing module " << module->name << std::endl;
|
2016-09-16 17:29:17 +02:00
|
|
|
module->state = module->api.init();
|
|
|
|
}
|
2016-10-21 22:07:23 +02:00
|
|
|
std::cout << "Reloading module " << module->name << std::endl;
|
2017-01-09 21:24:20 +01:00
|
|
|
module->api.reload(module->state, gReadSerializer);
|
2016-09-16 17:29:17 +02:00
|
|
|
} else {
|
2016-10-21 22:07:23 +02:00
|
|
|
std::cerr << "Error: could not find API for module " << module->name << std::endl;
|
2016-09-16 17:29:17 +02:00
|
|
|
dlclose(module->handle);
|
|
|
|
module->handle = NULL;
|
|
|
|
module->id = 0;
|
|
|
|
}
|
|
|
|
} else {
|
2016-10-21 22:07:23 +02:00
|
|
|
std::cerr << "Error: could not load module " << module->name << std::endl;
|
|
|
|
std::cerr << dlerror() << std::endl;
|
2016-09-16 17:29:17 +02:00
|
|
|
module->handle = NULL;
|
|
|
|
module->id = 0;
|
2016-10-21 22:07:23 +02:00
|
|
|
abort();
|
2016-09-16 17:29:17 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void RuntimeModuleManager::Update(float dt) {
|
2016-11-25 22:27:37 +01:00
|
|
|
for (int i = mModules.size() - 1; i >= 0; i--) {
|
2016-09-16 17:29:17 +02:00
|
|
|
if (mModules[i]->handle) {
|
2016-11-07 21:34:18 +01:00
|
|
|
mModules[i]->api.step(mModules[i]->state, dt);
|
2016-09-16 17:29:17 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-10-21 22:07:23 +02:00
|
|
|
bool RuntimeModuleManager::CheckModulesChanged() {
|
|
|
|
struct stat attr;
|
|
|
|
|
2017-02-05 10:37:49 +01:00
|
|
|
double current_time = gGetTimeSinceStart();
|
|
|
|
mNumUpdatesSinceLastModuleChange++;
|
|
|
|
|
2016-10-21 22:07:23 +02:00
|
|
|
for (int i = 0; i < mModules.size(); i++) {
|
|
|
|
RuntimeModule* module = mModules[i];
|
|
|
|
bool stat_result = stat(module->name.c_str(), &attr);
|
|
|
|
|
2017-02-05 10:37:49 +01:00
|
|
|
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
|
2016-10-21 22:07:23 +02:00
|
|
|
) {
|
2017-02-05 10:37:49 +01:00
|
|
|
module->id = attr.st_ino;
|
|
|
|
module->mtime = attr.st_mtime;
|
|
|
|
module->mtimensec = attr.st_mtim.tv_nsec;
|
|
|
|
module->fsize = attr.st_size;
|
|
|
|
mNumUpdatesSinceLastModuleChange = 0;
|
|
|
|
|
|
|
|
gLog ("Detected file change of %s: new size %d",
|
|
|
|
module->name.c_str(), attr.st_size);
|
2016-10-21 22:07:23 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-02-05 10:37:49 +01:00
|
|
|
// We have to delay the actual reload trigger to make
|
|
|
|
// sure all writes to the dynamic libraries are complete.
|
|
|
|
if (mNumUpdatesSinceLastModuleChange == 5) {
|
|
|
|
gLog ("Triggering reload");
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2016-10-21 22:07:23 +02:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2016-09-16 17:29:17 +02:00
|
|
|
void RuntimeModuleManager::UnloadModules() {
|
2016-11-25 22:27:37 +01:00
|
|
|
gWriteSerializer->Open(state_file);
|
|
|
|
|
|
|
|
for (int i = mModules.size() - 1; i >= 0 ; i--) {
|
2016-09-16 17:29:17 +02:00
|
|
|
if (mModules[i]->handle) {
|
2017-03-11 22:16:12 +01:00
|
|
|
gLog("Unloading module %s", mModules[i]->name.c_str());
|
2017-01-09 21:24:20 +01:00
|
|
|
mModules[i]->api.unload(mModules[i]->state, gWriteSerializer);
|
2016-09-16 17:29:17 +02:00
|
|
|
mModules[i]->state = nullptr;
|
|
|
|
dlclose(mModules[i]->handle);
|
|
|
|
mModules[i]->handle = 0;
|
|
|
|
mModules[i]->id = 0;
|
|
|
|
}
|
|
|
|
}
|
2016-11-25 22:27:37 +01:00
|
|
|
|
|
|
|
std::cout << "Writing state to file " << state_file << std::endl;
|
|
|
|
gWriteSerializer->Close();
|
2016-09-16 17:29:17 +02:00
|
|
|
}
|
|
|
|
|
2016-11-25 22:27:37 +01:00
|
|
|
void RuntimeModuleManager::LoadModules() {
|
|
|
|
std::cout << "Reading state from file " << state_file << std::endl;
|
|
|
|
gReadSerializer->Open(state_file);
|
|
|
|
for (int i = 0; i < mModules.size(); i++) {
|
|
|
|
LoadModule(mModules[i]);
|
|
|
|
}
|
|
|
|
gReadSerializer->Close();
|
|
|
|
}
|