diff options
| author | 0scar <qgt268@alumni.ku.dk> | 2023-07-28 14:32:58 +0000 |
|---|---|---|
| committer | 0scar <qgt268@alumni.ku.dk> | 2023-07-28 18:26:17 +0000 |
| commit | 4cb29fbc2d20f20e9d605796b137b4b70363113a (patch) | |
| tree | 9b5929ff4d16a7a2472bafac48196e5ddfabc7dd | |
| parent | 5ee7f4b4b033de403f1861eedc942c7a5a0f31b6 (diff) | |
Implement hotloading
* This commit implements dltools.{c,h}: _A simple wrapper around libdl / dlfcn.h_
* Reload states when done initializing engine, if hotloading is enabled.
* Adds general function types for state functions.
| -rw-r--r-- | CMakeLists.txt | 3 | ||||
| -rw-r--r-- | include/engine/dltools.h | 14 | ||||
| -rw-r--r-- | include/engine/state.h | 3 | ||||
| -rw-r--r-- | src/dltools.c | 62 | ||||
| -rw-r--r-- | src/engine.c | 13 | ||||
| -rw-r--r-- | src/state.c | 66 | ||||
| -rw-r--r-- | tools/cmake/DawAddState.cmake | 14 |
7 files changed, 161 insertions, 14 deletions
diff --git a/CMakeLists.txt b/CMakeLists.txt index ebe9aae..c7355a6 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -56,6 +56,7 @@ endif() set(ENGINE_SOURCES src/btree.c + src/dltools.c src/engine.c src/fov.c src/hashmap.c @@ -79,6 +80,7 @@ target_include_directories(daw PUBLIC target_link_libraries(${PROJECT_NAME} SDL2 SDL2_image SDL2_ttf $<$<NOT:$<PLATFORM_ID:Windows>>:m> + $<$<BOOL:${DAW_BUILD_HOTRELOAD}>:dl> $<$<AND:$<NOT:$<C_COMPILER_ID:MSVC>>,$<BOOL:${DAW_BUILD_UBSAN}>>:ubsan> ) @@ -97,6 +99,7 @@ target_compile_options(${PROJECT_NAME} PUBLIC target_compile_definitions(${PROJECT_NAME} PUBLIC $<$<BOOL:${DAW_BUILD_DEBUG}>:DAW_BUILD_DEBUG> + $<$<BOOL:${DAW_BUILD_HOTRELOAD}>:DAW_BUILD_HOTRELOAD> ) diff --git a/include/engine/dltools.h b/include/engine/dltools.h new file mode 100644 index 0000000..9306ae7 --- /dev/null +++ b/include/engine/dltools.h @@ -0,0 +1,14 @@ +#ifndef DLTOOLS_H +#define DLTOOLS_H + +/* Utility functions for handling runtime linked shared libraries */ +bool dynamic_library_close(void* shared_library); +void* dynamic_library_open(const char *library_path); +void* dynamic_library_reload(void* shared_library, const char *library_path); + +/* Returns the address of symbol in the provided shared_library handle. + * NULL on error*/ +void* dynamic_library_get_symbol(void* shared_library, const char *symbol); +char* dynamic_library_get_error(void); + +#endif diff --git a/include/engine/state.h b/include/engine/state.h index 402c701..cf486cc 100644 --- a/include/engine/state.h +++ b/include/engine/state.h @@ -20,4 +20,7 @@ void State_init(StateType type, memory *mem); void State_free(StateType type, memory *mem); StateType State_update(StateType type, memory *mem); +/* Reloads shared object file associated with state */ +bool State_reload(StateType type); + #endif diff --git a/src/dltools.c b/src/dltools.c new file mode 100644 index 0000000..de7c83b --- /dev/null +++ b/src/dltools.c @@ -0,0 +1,62 @@ +#include <stdbool.h> +#include <stdlib.h> + +#if defined (_WIN32) || defined (__WIN32__) || defined (WIN32) + /* include winapi */ +#else +#include <dlfcn.h> +#endif + +#include <engine/logging.h> +#include <engine/dltools.h> + +bool +dynamic_library_close(void* shared_library) { +#if defined (_WIN32) || defined (__WIN32__) || defined (WIN32) + return true; +#else + return dlclose(shared_library) == 0; +#endif +} + +void* +dynamic_library_open(const char *library_path) { +#if defined (_WIN32) || defined (__WIN32__) || defined (WIN32) + return NULL; +#else + return dlopen(library_path, RTLD_NOW); +#endif +} + +char* dynamic_library_get_error(void) { +#if defined (_WIN32) || defined (__WIN32__) || defined (WIN32) + return "unsupported on windows"; +#else + return dlerror(); +#endif +} + +void* +dynamic_library_reload(void* shared_library, const char *library_path) { + void* library_address = NULL; + if (!dynamic_library_close(shared_library)) { + ERROR("Failed to close shared library: %s", dynamic_library_get_error()); + ERROR("Reloading dynamic library failed."); + return library_address; + } + if ((library_address = dynamic_library_open(library_path)) == NULL) { + ERROR("Failed to open shared library: %s", dynamic_library_get_error()); + ERROR("Reloading dynamic library %s failed.", library_path); + } + return library_address; +} + + +void* +dynamic_library_get_symbol(void *restrict shared_library, const char *symbol) { +#if defined (_WIN32) || defined (__WIN32__) || defined (WIN32) + return NULL; +#else + return dlsym(shared_library, symbol); +#endif +} diff --git a/src/engine.c b/src/engine.c index fa528c0..36eb349 100644 --- a/src/engine.c +++ b/src/engine.c @@ -379,6 +379,19 @@ Platform *engine_init( GLOBAL_PLATFORM = p; + +#ifdef DAW_BUILD_HOTRELOAD + +#define State(name) \ +if (!State_reload(STATE_##name)) { \ + ERROR("Failed to reload shared object file for state %s", #name ); \ +}; + +#include <states/list_of_states.h> +#undef State + +#endif + return p; } diff --git a/src/state.c b/src/state.c index 853037f..1d59ad7 100644 --- a/src/state.c +++ b/src/state.c @@ -1,5 +1,6 @@ #include <engine/state.h> #include <engine/logging.h> +#include <engine/dltools.h> typedef StateType state_update_t(void*); @@ -17,18 +18,22 @@ typedef struct name##_state name##_state; \ typedef void (state_##name##_init_t)(name##_state*); \ typedef void (state_##name##_free_t)(name##_state*); \ typedef StateType (state_##name##_update_t)(name##_state*); -#include <states/all_states.h> +#include <states/list_of_states.h> #undef State #ifdef DAW_BUILD_HOTRELOAD + // When hotreloading is enabled, we want to assign state function pointers // dynamically. -#define State(name) \ -state_##name##_init_t *name##_init = NULL; \ -state_##name##_free_t *name##_free = NULL; \ -state_##name##_update_t *name##_update = NULL; - +#define State(name) \ +state_##name##_init_t *name##_init = NULL; \ +state_##name##_free_t *name##_free = NULL; \ +state_##name##_update_t *name##_update = NULL; \ + \ +void* libstate_##name = NULL; \ +const char* libstate_##name##_str = "lib" #name ".so"; #else + // Otherwise we just declare them. #define State(name) \ state_##name##_init_t name##_init; \ @@ -36,9 +41,11 @@ state_##name##_free_t name##_free; \ state_##name##_update_t name##_update; #endif -#include <states/all_states.h> +#include <states/list_of_states.h> #undef State +#include <states/all_states.h> + void State_init(StateType type, memory *mem) { switch (type) { #define State(name) \ @@ -80,11 +87,19 @@ void State_free(StateType type, memory *mem) { StateType (*State_updateFunc(StateType type))(void*) { switch (type) { +#ifdef DAW_BUILD_HOTRELOAD +#define State(name) \ + case (STATE_##name): { \ + return (state_update_t*)name##_update; \ + break; \ + } +#else #define State(name) \ case (STATE_##name): { \ return (state_update_t*)&name##_update; \ break; \ } +#endif #include <states/list_of_states.h> #undef State case STATE_null: @@ -116,3 +131,40 @@ StateType State_update(StateType type, memory *mem) { } return next_state; } + +bool State_reload(StateType type) { +#ifdef DAW_BUILD_HOTRELOAD + switch (type) { +#define State(name) \ + case (STATE_##name): { \ + if (libstate_##name == NULL) { \ + libstate_##name = dynamic_library_open(libstate_##name##_str); \ + } else { \ + libstate_##name = \ + dynamic_library_reload(libstate_##name, libstate_##name##_str); \ + } \ + if (libstate_##name == NULL) { \ + ERROR("Failed loading shared object: %s (%s)", \ + libstate_##name##_str, \ + dynamic_library_get_error()); \ + return false; \ + } \ + \ + name##_init = (state_##name##_init_t*)dynamic_library_get_symbol(libstate_##name, STR( name##_init ) ); \ + name##_free = (state_##name##_free_t*)dynamic_library_get_symbol(libstate_##name, STR( name##_free ) ); \ + name##_update = (state_##name##_update_t*)dynamic_library_get_symbol(libstate_##name, STR( name##_update ) ); \ + break; \ + } +#include <states/list_of_states.h> +#undef State + case STATE_null: + case STATE_quit: + ERROR("Invalid state"); + DEBUG("Got %s state.\n", StateTypeStr[type]); + break; + default: + exit(EXIT_FAILURE); + } +#endif + return true; +} diff --git a/tools/cmake/DawAddState.cmake b/tools/cmake/DawAddState.cmake index f6d7410..ac455b1 100644 --- a/tools/cmake/DawAddState.cmake +++ b/tools/cmake/DawAddState.cmake @@ -36,11 +36,11 @@ macro(daw_add_state STATENAME) # TODO: When state reloading is implemented properly, add MODULE library # option In general, this should only be available when debugging. if(BUILD_SHARED_LIBS) - #if(DAW_BUILD_DEBUG AND DAW_BUILD_HOTRELOAD) - # add_library(${STATENAME} MODULE ${STATE_SOURCES}) - #else() + if(DAW_BUILD_DEBUG AND DAW_BUILD_HOTRELOAD) + add_library(${STATENAME} MODULE ${STATE_SOURCES}) + else() add_library(${STATENAME} SHARED ${STATE_SOURCES}) - #endif() + endif() else() add_library(${STATENAME} OBJECT ${STATE_SOURCES}) endif() @@ -52,9 +52,9 @@ macro(daw_add_state STATENAME) include ) - set_property(TARGET daw - APPEND PROPERTY LINK_LIBRARIES - ${STATENAME}) + #set_property(TARGET daw + # APPEND PROPERTY LINK_LIBRARIES + # ${STATENAME}) list(APPEND STATE_LIST ${STATENAME}) endmacro() |
