diff --git a/libraries/libmdb/mdb.c b/libraries/libmdb/mdb.c index 983c75c8de578896b01828728ef1f48f3e530ad2..fc889ce7ade76807eb8302a0e5d7108a73639659 100644 --- a/libraries/libmdb/mdb.c +++ b/libraries/libmdb/mdb.c @@ -2568,11 +2568,8 @@ mdb_env_open2(MDB_env *env, unsigned int flags) } -#ifndef _WIN32 /** Release a reader thread's slot in the reader lock table. * This function is called automatically when a thread exits. - * Windows doesn't support destructor callbacks for thread-specific storage, - * so this function is not compiled there. * @param[in] ptr This points to the slot in the reader lock table. */ static void @@ -2584,6 +2581,60 @@ mdb_env_reader_dest(void *ptr) reader->mr_pid = 0; reader->mr_tid = 0; } + +#ifdef _WIN32 +/** Junk for arranging thread-specific callbacks on Windows. This is + * necessarily platform and compiler-specific. Windows supports up + * to 1088 keys. Let's assume nobody opens more than 64 environments + * in a single process, for now. They can override this if needed. + */ +#ifndef MAX_TLS_KEYS +#define MAX_TLS_KEYS 64 +#endif +static pthread_key_t mdb_tls_keys[MAX_TLS_KEYS]; +static int mdb_tls_nkeys; + +static void NTAPI mdb_tls_callback(PVOID module, DWORD reason, PVOID ptr) +{ + int i; + switch(reason) { + case DLL_PROCESS_ATTACH: break; + case DLL_THREAD_ATTACH: break; + case DLL_THREAD_DETACH: + for (i=0; i<mdb_tls_nkeys; i++) { + MDB_reader *r = pthread_getspecific(mdb_tls_keys[i]); + mdb_env_reader_dest(r); + } + break; + case DLL_PROCESS_DETACH: break; + } +} +#ifdef __GNUC__ +#ifdef _WIN64 +const PIMAGE_TLS_CALLBACK mdb_tls_cbp __attribute__((section (".CRT$XLB"))) = mdb_tls_callback; +#else +PIMAGE_TLS_CALLBACK mdb_tls_cbp __attribute__((section (".CRT$XLB"))) = mdb_tls_callback; +#endif +#else +#ifdef _WIN64 +/* Force some symbol references. + * _tls_used forces the linker to create the TLS directory if not already done + * mdb_tls_cbp prevents whole-program-optimizer from dropping the symbol. + */ +#pragma comment(linker, "/INCLUDE:_tls_used") +#pragma comment(linker, "/INCLUDE:mdb_tls_cbp") +#pragma const_seg(".CRT$XLB") +extern const PIMAGE_TLS_CALLBACK mdb_tls_callback; +const PIMAGE_TLS_CALLBACK mdb_tls_cbp = mdb_tls_callback; +#pragma const_seg() +#else /* WIN32 */ +#pragma comment(linker, "/INCLUDE:__tls_used") +#pragma comment(linker, "/INCLUDE:_mdb_tls_cbp") +#pragma data_seg(".CRT$XLB") +PIMAGE_TLS_CALLBACK mdb_tls_cbp = mdb_tls_callback; +#pragma data_seg() +#endif /* WIN 32/64 */ +#endif /* !__GNUC__ */ #endif /** Downgrade the exclusive lock on the region back to shared */ @@ -3040,6 +3091,15 @@ mdb_env_open(MDB_env *env, const char *path, unsigned int flags, mode_t mode) env->me_path = strdup(path); DPRINTF("opened dbenv %p", (void *) env); pthread_key_create(&env->me_txkey, mdb_env_reader_dest); +#ifdef _WIN32 + /* Windows TLS callbacks need help finding their TLS info. */ + if (mdb_tls_nkeys < MAX_TLS_KEYS) + mdb_tls_keys[mdb_tls_nkeys++] = env->me_txkey; + else { + rc = ENOMEM; + goto leave; + } +#endif LAZY_RWLOCK_INIT(&env->me_dblock, NULL); if (excl) mdb_env_share_locks(env); @@ -3087,6 +3147,17 @@ mdb_env_close(MDB_env *env) LAZY_RWLOCK_DESTROY(&env->me_dblock); pthread_key_delete(env->me_txkey); +#ifdef _WIN32 + /* Delete our key from the global list */ + { int i; + for (i=0; i<mdb_tls_nkeys; i++) + if (mdb_tls_keys[i] == env->me_txkey) { + mdb_tls_keys[i] = mdb_tls_keys[mdb_tls_nkeys-1]; + mdb_tls_nkeys--; + break; + } + } +#endif if (env->me_map) { munmap(env->me_map, env->me_mapsize);