Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
Unix: Refactoring plugin management (part 1)
It is not possible to keep multiple game plugins in
memory because of symbol clashing. Applying
refactorings to allow unloading other game libraries
when changing the game.
  • Loading branch information
skyjake committed Dec 29, 2011
1 parent b2f8b6e commit b17fdca
Show file tree
Hide file tree
Showing 6 changed files with 117 additions and 4 deletions.
1 change: 1 addition & 0 deletions doomsday/engine/api/dd_plugin.h
Expand Up @@ -36,6 +36,7 @@
#define MAX_HOOKS 16
#define HOOKF_EXCLUSIVE 0x01000000

typedef int (*pluginfunc_t) (void);
typedef int (*hookfunc_t) (int type, int parm, void *data);

// Hook types.
Expand Down
6 changes: 6 additions & 0 deletions doomsday/engine/portable/include/library.h
Expand Up @@ -47,6 +47,12 @@ void Library_Init(void);
*/
void Library_Shutdown(void);

/**
* Closes the library handles of all game plugins. The library will be
* reopened automatically when needed.
*/
void Library_ReleaseGames(void);

/**
* Defines an additional library @a dir where to look for dynamic libraries.
*/
Expand Down
18 changes: 18 additions & 0 deletions doomsday/engine/portable/src/dd_main.c
Expand Up @@ -1099,6 +1099,14 @@ boolean DD_ChangeGame2(Game* game, boolean allowReload)

Con_ClearDatabases();

{ // Tell the plugin it is being unloaded.
void* unloader = DD_FindEntryPoint(Game_PluginId(theGame), "DP_Unload");
#ifdef _DEBUG
Con_Message("DD_ChangeGame2: Calling DP_Unload (%p)\n", unloader);
#endif
if(unloader) ((pluginfunc_t)unloader)();
}

// The current game is now the special "null-game".
theGame = nullGame;

Expand Down Expand Up @@ -1133,6 +1141,8 @@ boolean DD_ChangeGame2(Game* game, boolean allowReload)
}
)

Library_ReleaseGames();

if(!exchangeEntryPoints(Game_PluginId(game)))
{
DD_ComposeMainWindowTitle(buf);
Expand All @@ -1149,6 +1159,14 @@ boolean DD_ChangeGame2(Game* game, boolean allowReload)
// This is now the current game.
theGame = game;

{ // Tell the plugin it is being unloaded.
void* loader = DD_FindEntryPoint(Game_PluginId(theGame), "DP_Load");
#ifdef _DEBUG
Con_Message("DD_ChangeGame2: Calling DP_Load (%p)\n", loader);
#endif
if(loader) ((pluginfunc_t)loader)();
}

DD_ComposeMainWindowTitle(buf);
Sys_SetWindowTitle(windowIDX, buf);

Expand Down
85 changes: 81 additions & 4 deletions doomsday/engine/portable/src/library.c
Expand Up @@ -33,16 +33,20 @@
#include <dlfcn.h>
#include <string.h>

typedef void* handle_t;
#define MAX_LIBRARIES 64 /// @todo Replace with a dynamic list.

static filename_t appDir; /// @todo Use ddstring_t
static ddstring_t* lastError;
typedef void* handle_t;

struct library_s {
ddstring_t* path;
handle_t handle;
boolean isGamePlugin;
};

static filename_t appDir; /// @todo Use ddstring_t
static ddstring_t* lastError;
static Library* loadedLibs[MAX_LIBRARIES];

static void getBundlePath(char* path, size_t len)
{
if(ArgCheckWith("-libdir", 1))
Expand Down Expand Up @@ -71,6 +75,34 @@ static void getBundlePath(char* path, size_t len)
#endif
}

static void addToLoaded(Library* lib)
{
int i;
for(i = 0; i < MAX_LIBRARIES; ++i)
{
if(!loadedLibs[i])
{
loadedLibs[i] = lib;
return;
}
}
assert(false);
}

static void removeFromLoaded(Library* lib)
{
int i;
for(i = 0; i < MAX_LIBRARIES; ++i)
{
if(loadedLibs[i] == lib)
{
loadedLibs[i] = 0;
return;
}
}
assert(false);
}

void Library_Init(void)
{
lastError = Str_NewStd();
Expand All @@ -81,7 +113,39 @@ void Library_Shutdown(void)
{
Str_Delete(lastError); lastError = 0;

/// @todo Unload all remaining libraries.
/// @todo Unload all remaining libraries?
}

void Library_ReleaseGames(void)
{
int i;

for(i = 0; i < MAX_LIBRARIES; ++i)
{
Library* lib = loadedLibs[i];
if(!lib) continue;
if(lib->isGamePlugin && lib->handle)
{
#ifdef _DEBUG
fprintf(stderr, "Library_ReleaseGames: Closing '%s'\n", Str_Text(lib->path));
#endif
dlclose(lib->handle);
lib->handle = 0;
}
}
}

static void reopenLibraryIfNeeded(Library* lib)
{
assert(lib);
if(!lib->handle)
{
#ifdef _DEBUG
fprintf(stderr, "reopenLibraryIfNeeded: Opening '%s'\n", Str_Text(lib->path));
#endif
lib->handle = dlopen(Str_Text(lib->path), RTLD_NOW);
assert(lib->handle);
}
}

Library* Library_New(const char *fileName)
Expand Down Expand Up @@ -130,6 +194,17 @@ Library* Library_New(const char *fileName)
lib->handle = handle;
lib->path = Str_NewStd();
Str_Set(lib->path, bundlePath);

addToLoaded(lib);

// Symbols from game plugins conflict with each other, so we have to
// keep track of them.
/// @todo Needs a more generic way to detect the type of plugin.
if(Library_Symbol(lib, "G_RegisterGames"))
{
lib->isGamePlugin = true;
}

return lib;
}

Expand All @@ -141,12 +216,14 @@ void Library_Delete(Library *lib)
dlclose(lib->handle);
}
Str_Delete(lib->path);
removeFromLoaded(lib);
free(lib);
}

void* Library_Symbol(Library* lib, const char* symbolName)
{
assert(lib);
reopenLibraryIfNeeded(lib);
void* ptr = dlsym(lib->handle, symbolName);
if(!ptr)
{
Expand Down
2 changes: 2 additions & 0 deletions doomsday/plugins/jdoom/api/jdoom.def
Expand Up @@ -3,3 +3,5 @@ LIBRARY JDOOM
EXPORTS
DP_Initialize @1
GetGameAPI @2
DP_Load @3
DP_Unload @4
9 changes: 9 additions & 0 deletions doomsday/plugins/jdoom/src/d_api.c
Expand Up @@ -245,5 +245,14 @@ game_export_t* GetGameAPI(game_import_t* imports)
void DP_Initialize()
{
Plug_AddHook(HOOK_STARTUP, G_RegisterGames);
}

void DP_Load(void)
{
Plug_AddHook(HOOK_VIEWPORT_RESHAPE, R_UpdateViewport);
}

void DP_Unload(void)
{
Plug_RemoveHook(HOOK_VIEWPORT_RESHAPE, R_UpdateViewport);
}

0 comments on commit b17fdca

Please sign in to comment.