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);