diff --git a/src/bits.h b/src/bits.h index 4b0edd01..35abd8d7 100644 --- a/src/bits.h +++ b/src/bits.h @@ -779,7 +779,7 @@ struct MDBX_env { } me_dbgeo; /* */ #if defined(_WIN32) || defined(_WIN64) - SRWLOCK me_remap_guard; + MDBX_srwlock me_remap_guard; /* Workaround for LockFileEx and WriteFile multithread bug */ CRITICAL_SECTION me_windowsbug_lock; #else diff --git a/src/lck-windows.c b/src/lck-windows.c index 831200d3..cb5add9b 100644 --- a/src/lck-windows.c +++ b/src/lck-windows.c @@ -156,7 +156,7 @@ void mdbx_txn_unlock(MDBX_env *env) { #define LCK_UPPER LCK_UP_OFFSET, LCK_UP_LEN int mdbx_rdt_lock(MDBX_env *env) { - AcquireSRWLockShared(&env->me_remap_guard); + mdbx_srwlock_AcquireShared(&env->me_remap_guard); if (env->me_lfd == INVALID_HANDLE_VALUE) return MDBX_SUCCESS; /* readonly database in readonly filesystem */ @@ -165,7 +165,7 @@ int mdbx_rdt_lock(MDBX_env *env) { return MDBX_SUCCESS; int rc = GetLastError(); - ReleaseSRWLockShared(&env->me_remap_guard); + mdbx_srwlock_ReleaseShared(&env->me_remap_guard); return rc; } @@ -175,7 +175,7 @@ void mdbx_rdt_unlock(MDBX_env *env) { if (!funlock(env->me_lfd, LCK_UPPER)) mdbx_panic("%s failed: errcode %u", mdbx_func_, GetLastError()); } - ReleaseSRWLockShared(&env->me_remap_guard); + mdbx_srwlock_ReleaseShared(&env->me_remap_guard); } static int suspend_and_append(mdbx_handle_array_t **array, @@ -573,3 +573,103 @@ int mdbx_rpid_check(MDBX_env *env, mdbx_pid_t pid) { return rc; } } + +//---------------------------------------------------------------------------- +// Stub for slim read-write lock +// Copyright (C) 1995-2002 Brad Wilson + +static void WINAPI stub_srwlock_Init(MDBX_srwlock *srwl) { + srwl->readerCount = srwl->writerCount = 0; +} + +static void WINAPI stub_srwlock_AcquireShared(MDBX_srwlock *srwl) { + while (true) { + assert(srwl->writerCount >= 0 && srwl->readerCount >= 0); + + // If there's a writer already, spin without unnecessarily + // interlocking the CPUs + if (srwl->writerCount != 0) { + YieldProcessor(); + continue; + } + + // Add to the readers list + _InterlockedIncrement(&srwl->readerCount); + + // Check for writers again (we may have been pre-empted). If + // there are no writers writing or waiting, then we're done. + if (srwl->writerCount == 0) + break; + + // Remove from the readers list, spin, try again + _InterlockedDecrement(&srwl->readerCount); + YieldProcessor(); + } +} + +static void WINAPI stub_srwlock_ReleaseShared(MDBX_srwlock *srwl) { + assert(srwl->readerCount > 0); + _InterlockedDecrement(&srwl->readerCount); +} + +static void WINAPI stub_srwlock_AcquireExclusive(MDBX_srwlock *srwl) { + while (true) { + assert(srwl->writerCount >= 0 && srwl->readerCount >= 0); + + // If there's a writer already, spin without unnecessarily + // interlocking the CPUs + if (srwl->writerCount != 0) { + YieldProcessor(); + continue; + } + + // See if we can become the writer (expensive, because it inter- + // locks the CPUs, so writing should be an infrequent process) + if (_InterlockedExchange(&srwl->writerCount, 1) == 0) + break; + } + + // Now we're the writer, but there may be outstanding readers. + // Spin until there aren't any more; new readers will wait now + // that we're the writer. + while (srwl->readerCount != 0) { + assert(srwl->writerCount >= 0 && srwl->readerCount >= 0); + YieldProcessor(); + } +} + +static void WINAPI stub_srwlock_ReleaseExclusive(MDBX_srwlock *srwl) { + assert(srwl->writerCount == 1 && srwl->readerCount >= 0); + srwl->writerCount = 0; +} + +static void WINAPI srwlock_thunk_init(MDBX_srwlock *srwl) { + HINSTANCE hInst = GetModuleHandleA("kernel32.dll"); + MDBX_srwlock_function init = + (MDBX_srwlock_function)GetProcAddress(hInst, "InitializeSRWLock"); + if (init != NULL) { + mdbx_srwlock_AcquireShared = + (MDBX_srwlock_function)GetProcAddress(hInst, "AcquireSRWLockShared"); + mdbx_srwlock_ReleaseShared = + (MDBX_srwlock_function)GetProcAddress(hInst, "ReleaseSRWLockShared"); + mdbx_srwlock_AcquireExclusive = + (MDBX_srwlock_function)GetProcAddress(hInst, "AcquireSRWLockExclusive"); + mdbx_srwlock_ReleaseExclusive = + (MDBX_srwlock_function)GetProcAddress(hInst, "ReleaseSRWLockExclusive"); + } else { + init = stub_srwlock_Init; + mdbx_srwlock_AcquireShared = stub_srwlock_AcquireShared; + mdbx_srwlock_ReleaseShared = stub_srwlock_ReleaseShared; + mdbx_srwlock_AcquireExclusive = stub_srwlock_AcquireExclusive; + mdbx_srwlock_ReleaseExclusive = stub_srwlock_ReleaseExclusive; + } + mdbx_compiler_barrier(); + mdbx_srwlock_Init = init; + mdbx_srwlock_Init(srwl); +} + +MDBX_srwlock_function mdbx_srwlock_Init = srwlock_thunk_init; +MDBX_srwlock_function mdbx_srwlock_AcquireShared; +MDBX_srwlock_function mdbx_srwlock_ReleaseShared; +MDBX_srwlock_function mdbx_srwlock_AcquireExclusive; +MDBX_srwlock_function mdbx_srwlock_ReleaseExclusive; diff --git a/src/mdbx.c b/src/mdbx.c index c6c8a41b..029d6015 100644 --- a/src/mdbx.c +++ b/src/mdbx.c @@ -2003,7 +2003,7 @@ static int mdbx_mapresize(MDBX_env *env, const pgno_t size_pgno, /* Acquire guard in exclusive mode for: * - to avoid collision between read and write txns around env->me_dbgeo; * - to avoid attachment of new reading threads (see mdbx_rdt_lock); */ - AcquireSRWLockExclusive(&env->me_remap_guard); + mdbx_srwlock_AcquireExclusive(&env->me_remap_guard); mdbx_handle_array_t *suspended = NULL; mdbx_handle_array_t array_onstack; int rc = MDBX_SUCCESS; @@ -2083,7 +2083,7 @@ bailout: #if defined(_WIN32) || defined(_WIN64) int err = MDBX_SUCCESS; - ReleaseSRWLockExclusive(&env->me_remap_guard); + mdbx_srwlock_ReleaseExclusive(&env->me_remap_guard); if (suspended) { err = mdbx_resume_threads_after_remap(suspended); if (suspended != &array_onstack) @@ -4912,7 +4912,7 @@ int __cold mdbx_env_create(MDBX_env **penv) { goto bailout; #if defined(_WIN32) || defined(_WIN64) - InitializeSRWLock(&env->me_remap_guard); + mdbx_srwlock_Init(&env->me_remap_guard); InitializeCriticalSection(&env->me_windowsbug_lock); #else rc = mdbx_fastmutex_init(&env->me_remap_guard); diff --git a/src/osal.h b/src/osal.h index 46bf82a0..3e40b80c 100644 --- a/src/osal.h +++ b/src/osal.h @@ -560,6 +560,23 @@ LIBMDBX_API void mdbx_txn_unlock(MDBX_env *env); int mdbx_rpid_set(MDBX_env *env); int mdbx_rpid_clear(MDBX_env *env); +#if defined(_WIN32) || defined(_WIN64) +typedef struct MDBX_srwlock { + union { + struct { + long volatile readerCount; + long volatile writerCount; + }; + RTL_SRWLOCK native; + }; +} MDBX_srwlock; + +typedef void(WINAPI *MDBX_srwlock_function)(MDBX_srwlock *); +extern MDBX_srwlock_function mdbx_srwlock_Init, mdbx_srwlock_AcquireShared, + mdbx_srwlock_ReleaseShared, mdbx_srwlock_AcquireExclusive, + mdbx_srwlock_ReleaseExclusive; +#endif /* Windows */ + /* Checks reader by pid. * * Returns: