loader: Fix static library initialization
authorLenny Komow <lenny@lunarg.com>
Fri, 19 Jan 2018 18:22:28 +0000 (11:22 -0700)
committerLenny Komow <lenny@lunarg.com>
Fri, 19 Jan 2018 23:44:17 +0000 (16:44 -0700)
Fix the loader's global initialization being skipped when the loader
is built as a static library on windows

Change-Id: I492f87104fb3bf32aed3c7d251dee2caeb4f00ba

loader/CMakeLists.txt
loader/loader.c
loader/loader.h
loader/trampoline.c
loader/vk_loader_platform.h

index e5334c4..f2bd152 100644 (file)
@@ -155,16 +155,26 @@ if (WIN32)
     set(CMAKE_C_FLAGS_DEBUG " ")
     separate_arguments(LOCAL_C_FLAGS_REL WINDOWS_COMMAND ${CMAKE_C_FLAGS_RELEASE})
 
-    add_library(loader-norm OBJECT ${NORMAL_LOADER_SRCS} dirent_on_windows.c)
-    add_dependencies(loader-norm generate_helper_files loader_gen_files)
-
-    target_compile_options(loader-norm PUBLIC "$<$<CONFIG:DEBUG>:${LOCAL_C_FLAGS_DBG}>")
-    add_library(loader-opt OBJECT ${OPT_LOADER_SRCS})
-    add_dependencies(loader-opt generate_helper_files loader_gen_files loader_asm_gen_files)
-
-    target_compile_options(loader-opt PUBLIC "$<$<CONFIG:DEBUG>:${LOCAL_C_FLAGS_REL}>")
-    add_library(${API_LOWERCASE}-${MAJOR} SHARED $<TARGET_OBJECTS:loader-opt> $<TARGET_OBJECTS:loader-norm> ${CMAKE_CURRENT_BINARY_DIR}/${API_LOWERCASE}-${MAJOR}.def ${CMAKE_CURRENT_SOURCE_DIR}/loader.rc)
-    add_library(VKstatic.${MAJOR} STATIC $<TARGET_OBJECTS:loader-opt> $<TARGET_OBJECTS:loader-norm>)
+    add_library(loader-norm-dy OBJECT ${NORMAL_LOADER_SRCS} dirent_on_windows.c)
+    add_dependencies(loader-norm-dy generate_helper_files loader_gen_files)
+    target_compile_definitions(loader-norm-dy PUBLIC LOADER_DYNAMIC_LIB)
+    target_compile_options(loader-norm-dy PUBLIC "$<$<CONFIG:DEBUG>:${LOCAL_C_FLAGS_DBG}>")
+
+    add_library(loader-opt-dy OBJECT ${OPT_LOADER_SRCS})
+    add_dependencies(loader-opt-dy generate_helper_files loader_gen_files loader_asm_gen_files)
+    target_compile_definitions(loader-opt-dy PUBLIC LOADER_DYNAMIC_LIB)
+    target_compile_options(loader-opt-dy PUBLIC "$<$<CONFIG:DEBUG>:${LOCAL_C_FLAGS_REL}>")
+
+    add_library(loader-norm-st OBJECT ${NORMAL_LOADER_SRCS} dirent_on_windows.c)
+    add_dependencies(loader-norm-st generate_helper_files loader_gen_files)
+    target_compile_options(loader-norm-st PUBLIC "$<$<CONFIG:DEBUG>:${LOCAL_C_FLAGS_DBG}>")
+
+    add_library(loader-opt-st OBJECT ${OPT_LOADER_SRCS})
+    add_dependencies(loader-opt-st generate_helper_files loader_gen_files loader_asm_gen_files)
+    target_compile_options(loader-opt-st PUBLIC "$<$<CONFIG:DEBUG>:${LOCAL_C_FLAGS_REL}>")
+
+    add_library(${API_LOWERCASE}-${MAJOR} SHARED $<TARGET_OBJECTS:loader-opt-dy> $<TARGET_OBJECTS:loader-norm-dy> ${CMAKE_CURRENT_BINARY_DIR}/${API_LOWERCASE}-${MAJOR}.def ${CMAKE_CURRENT_SOURCE_DIR}/loader.rc)
+    add_library(VKstatic.${MAJOR} STATIC $<TARGET_OBJECTS:loader-opt-st> $<TARGET_OBJECTS:loader-norm-st>)
     # Suppress conflicting libs warning for debug builds.
     set_target_properties(${API_LOWERCASE}-${MAJOR} PROPERTIES LINK_FLAGS_DEBUG /ignore:4098)
     set_target_properties(VKstatic.${MAJOR} PROPERTIES OUTPUT_NAME VKstatic.${MAJOR})
@@ -197,6 +207,7 @@ else()
 
     add_library(${API_LOWERCASE} SHARED ${NORMAL_LOADER_SRCS} ${OPT_LOADER_SRCS})
     add_dependencies(${API_LOWERCASE} generate_helper_files loader_gen_files loader_asm_gen_files)
+    target_compile_definitions(${API_LOWERCASE} PUBLIC -DLOADER_DYNAMIC_LIB)
     set_target_properties(${API_LOWERCASE} PROPERTIES SOVERSION "1" VERSION "1.0.${vk_header_version}")
     target_link_libraries(${API_LOWERCASE} -ldl -lpthread -lm)
 
index 5ac8632..f1bc589 100644 (file)
@@ -93,6 +93,8 @@ uint32_t g_loader_log_msgs = 0;
 loader_platform_thread_mutex loader_lock;
 loader_platform_thread_mutex loader_json_lock;
 
+LOADER_PLATFORM_THREAD_ONCE_DECLARATION(once_init);
+
 void *loader_instance_heap_alloc(const struct loader_instance *instance, size_t size, VkSystemAllocationScope alloc_scope) {
     void *pMemory = NULL;
 #if (DEBUG_DISABLE_APP_ALLOCATORS == 1)
@@ -6008,6 +6010,8 @@ VKAPI_ATTR VkResult VKAPI_CALL terminator_EnumerateInstanceLayerProperties(const
     struct loader_layer_list instance_layer_list;
     tls_instance = NULL;
 
+    LOADER_PLATFORM_THREAD_ONCE(&once_init, loader_initialize);
+
     uint32_t copy_size;
 
     // Get layer libraries
@@ -6037,7 +6041,7 @@ out:
     return result;
 }
 
-#if defined(_WIN32)
+#if defined(_WIN32) && defined(LOADER_DYNAMIC_LIB)
 BOOL WINAPI DllMain(HINSTANCE hinst, DWORD reason, LPVOID reserved) {
     switch (reason) {
         case DLL_PROCESS_ATTACH:
@@ -6054,7 +6058,7 @@ BOOL WINAPI DllMain(HINSTANCE hinst, DWORD reason, LPVOID reserved) {
     }
     return TRUE;
 }
-#else
+#elif !defined(_WIN32)
 __attribute__((constructor)) void loader_init_library() { loader_initialize(); }
 
 __attribute__((destructor)) void loader_free_library() { loader_release(); }
index 2c5739b..58dfefe 100644 (file)
@@ -379,6 +379,9 @@ static inline void loader_init_dispatch(void *obj, const void *data) {
 // Global variables used across files
 extern struct loader_struct loader;
 extern THREAD_LOCAL_DECL struct loader_instance *tls_instance;
+#if defined(_WIN32) && !defined(LOADER_DYNAMIC_LIB)
+extern LOADER_PLATFORM_THREAD_ONCE_DEFINITION(once_init);
+#endif
 extern loader_platform_thread_mutex loader_lock;
 extern loader_platform_thread_mutex loader_json_lock;
 
index 26d3092..607085c 100644 (file)
@@ -95,6 +95,7 @@ LOADER_EXPORT VKAPI_ATTR VkResult VKAPI_CALL vkEnumerateInstanceExtensionPropert
                                                                                     uint32_t *pPropertyCount,
                                                                                     VkExtensionProperties *pProperties) {
     tls_instance = NULL;
+    LOADER_PLATFORM_THREAD_ONCE(&once_init, loader_initialize);
 
     // We know we need to call at least the terminator
     VkResult res = VK_SUCCESS;
@@ -182,6 +183,7 @@ LOADER_EXPORT VKAPI_ATTR VkResult VKAPI_CALL vkEnumerateInstanceExtensionPropert
 LOADER_EXPORT VKAPI_ATTR VkResult VKAPI_CALL vkEnumerateInstanceLayerProperties(uint32_t *pPropertyCount,
                                                                                 VkLayerProperties *pProperties) {
     tls_instance = NULL;
+    LOADER_PLATFORM_THREAD_ONCE(&once_init, loader_initialize);
 
     // We know we need to call at least the terminator
     VkResult res = VK_SUCCESS;
@@ -273,6 +275,8 @@ LOADER_EXPORT VKAPI_ATTR VkResult VKAPI_CALL vkCreateInstance(const VkInstanceCr
     bool loaderLocked = false;
     VkResult res = VK_ERROR_INITIALIZATION_FAILED;
 
+    LOADER_PLATFORM_THREAD_ONCE(&once_init, loader_initialize);
+
     // Fail if the requested Vulkan apiVersion is > 1.0 since the loader only supports 1.0.
     // Having pCreateInfo == NULL, pCreateInfo->pApplication == NULL, or
     // pCreateInfo->pApplicationInfo->apiVersion == 0 all indicate that the application is
index 3fd8330..40de844 100644 (file)
@@ -118,11 +118,11 @@ static inline const char *loader_platform_get_proc_address_error(const char *nam
 // Threads:
 typedef pthread_t loader_platform_thread;
 #define THREAD_LOCAL_DECL __thread
-static inline void loader_platform_thread_once(pthread_once_t *ctl, void (*func)(void)) {
-    assert(func != NULL);
-    assert(ctl != NULL);
-    pthread_once(ctl, func);
-}
+
+// The once init functionality is not used on Linux
+#define LOADER_PLATFORM_THREAD_ONCE_DECLARATION(var)
+#define LOADER_PLATFORM_THREAD_ONCE_DEFINITION(var)
+#define LOADER_PLATFORM_THREAD_ONCE(ctl, func)
 
 // Thread IDs:
 typedef pthread_t loader_platform_thread_id;
@@ -299,19 +299,29 @@ static char *loader_platform_get_proc_address_error(const char *name) {
 // Threads:
 typedef HANDLE loader_platform_thread;
 #define THREAD_LOCAL_DECL __declspec(thread)
+
+// The once init functionality is not used when building a DLL on Windows. This is because there is no way to clean up the
+// resources allocated by anything allocated by once init. This isn't a problem for static libraries, but it is for dynamic
+// ones. When building a DLL, we use DllMain() instead to allow properly cleaning up resources.
+#if defined(LOADER_DYNAMIC_LIB)
+#define LOADER_PLATFORM_THREAD_ONCE_DECLARATION(var)
+#define LOADER_PLATFORM_THREAD_ONCE_DEFINITION(var)
+#define LOADER_PLATFORM_THREAD_ONCE(ctl, func)
+#else
 #define LOADER_PLATFORM_THREAD_ONCE_DECLARATION(var) INIT_ONCE var = INIT_ONCE_STATIC_INIT;
 #define LOADER_PLATFORM_THREAD_ONCE_DEFINITION(var) INIT_ONCE var;
+#define LOADER_PLATFORM_THREAD_ONCE(ctl, func) loader_platform_thread_once_fn(ctl, func)
 static BOOL CALLBACK InitFuncWrapper(PINIT_ONCE InitOnce, PVOID Parameter, PVOID *Context) {
     void (*func)(void) = (void (*)(void))Parameter;
     func();
     return TRUE;
 }
-
-static void loader_platform_thread_once(void *ctl, void (*func)(void)) {
+static void loader_platform_thread_once_fn(void *ctl, void (*func)(void)) {
     assert(func != NULL);
     assert(ctl != NULL);
     InitOnceExecuteOnce((PINIT_ONCE)ctl, InitFuncWrapper, func, NULL);
 }
+#endif
 
 // Thread IDs:
 typedef DWORD loader_platform_thread_id;