summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
author0scar <qgt268@alumni.ku.dk>2023-07-28 14:32:58 +0000
committer0scar <qgt268@alumni.ku.dk>2023-07-28 18:26:17 +0000
commit4cb29fbc2d20f20e9d605796b137b4b70363113a (patch)
tree9b5929ff4d16a7a2472bafac48196e5ddfabc7dd
parent5ee7f4b4b033de403f1861eedc942c7a5a0f31b6 (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.txt3
-rw-r--r--include/engine/dltools.h14
-rw-r--r--include/engine/state.h3
-rw-r--r--src/dltools.c62
-rw-r--r--src/engine.c13
-rw-r--r--src/state.c66
-rw-r--r--tools/cmake/DawAddState.cmake14
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()