diff --git a/src/lck-windows.c b/src/lck-windows.c index 478c79df..c019a6d5 100644 --- a/src/lck-windows.c +++ b/src/lck-windows.c @@ -226,12 +226,19 @@ static int suspend_and_append(mdbx_handle_array_t **array, (*array)->limit = limit * 2; } - HANDLE hThread = OpenThread(THREAD_SUSPEND_RESUME, FALSE, ThreadId); + HANDLE hThread = OpenThread(THREAD_SUSPEND_RESUME | THREAD_QUERY_INFORMATION, + FALSE, ThreadId); if (hThread == NULL) return GetLastError(); + if (SuspendThread(hThread) == -1) { + int err = GetLastError(); + DWORD ExitCode; + if (err == /* workaround for Win10 UCRT bug */ ERROR_ACCESS_DENIED || + !GetExitCodeThread(hThread, &ExitCode) || ExitCode != STILL_ACTIVE) + err = MDBX_SUCCESS; CloseHandle(hThread); - return GetLastError(); + return err; } (*array)->handles[(*array)->count++] = hThread; @@ -316,9 +323,15 @@ int mdbx_suspend_threads_before_remap(MDBX_env *env, int mdbx_resume_threads_after_remap(mdbx_handle_array_t *array) { int rc = MDBX_SUCCESS; for (unsigned i = 0; i < array->count; ++i) { - if (ResumeThread(array->handles[i]) == -1) - rc = GetLastError(); - CloseHandle(array->handles[i]); + const HANDLE hThread = array->handles[i]; + if (ResumeThread(hThread) == -1) { + const int err = GetLastError(); + DWORD ExitCode; + if (err != /* workaround for Win10 UCRT bug */ ERROR_ACCESS_DENIED && + GetExitCodeThread(hThread, &ExitCode) && ExitCode == STILL_ACTIVE) + rc = err; + } + CloseHandle(hThread); } return rc; } diff --git a/src/mdbx.c b/src/mdbx.c index 8abb0508..ca3774d5 100644 --- a/src/mdbx.c +++ b/src/mdbx.c @@ -2295,18 +2295,20 @@ bailout: VALGRIND_CREATE_BLOCK(env->me_map, env->me_mapsize, "mdbx"); } #endif - } else if (rc != MDBX_RESULT_TRUE) { - mdbx_error("failed resize datafile/mapping: " - "present %" PRIuPTR " -> %" PRIuPTR ", " - "limit %" PRIuPTR " -> %" PRIuPTR ", errcode %d", - env->me_dbgeo.now, size_bytes, env->me_dbgeo.upper, limit_bytes, - rc); } else { - mdbx_notice("unable resize datafile/mapping: " - "present %" PRIuPTR " -> %" PRIuPTR ", " - "limit %" PRIuPTR " -> %" PRIuPTR ", errcode %d", - env->me_dbgeo.now, size_bytes, env->me_dbgeo.upper, limit_bytes, - rc); + if (rc != MDBX_RESULT_TRUE) { + mdbx_error("failed resize datafile/mapping: " + "present %" PRIuPTR " -> %" PRIuPTR ", " + "limit %" PRIuPTR " -> %" PRIuPTR ", errcode %d", + env->me_dbgeo.now, size_bytes, env->me_dbgeo.upper, + limit_bytes, rc); + } else { + mdbx_notice("unable resize datafile/mapping: " + "present %" PRIuPTR " -> %" PRIuPTR ", " + "limit %" PRIuPTR " -> %" PRIuPTR ", errcode %d", + env->me_dbgeo.now, size_bytes, env->me_dbgeo.upper, + limit_bytes, rc); + } if (!env->me_dxb_mmap.address) { env->me_flags |= MDBX_FATAL_ERROR; if (env->me_txn) @@ -3161,6 +3163,16 @@ static int mdbx_txn_renew0(MDBX_txn *txn, unsigned flags) { rc = mdbx_rdt_lock(env); if (unlikely(MDBX_IS_ERROR(rc))) return rc; + if (unlikely(env->me_flags & MDBX_FATAL_ERROR)) { + mdbx_rdt_unlock(env); + return MDBX_PANIC; + } +#if defined(_WIN32) || defined(_WIN64) + if (unlikely(!env->me_map)) { + mdbx_rdt_unlock(env); + return MDBX_EPERM; + } +#endif /* Windows */ rc = MDBX_SUCCESS; if (unlikely(env->me_live_reader != env->me_pid)) { @@ -3259,6 +3271,16 @@ static int mdbx_txn_renew0(MDBX_txn *txn, unsigned flags) { rc = mdbx_txn_lock(env, F_ISSET(flags, MDBX_TRYTXN)); if (unlikely(rc)) return rc; + if (unlikely(env->me_flags & MDBX_FATAL_ERROR)) { + mdbx_txn_unlock(env); + return MDBX_PANIC; + } +#if defined(_WIN32) || defined(_WIN64) + if (unlikely(!env->me_map)) { + mdbx_txn_unlock(env); + return MDBX_EPERM; + } +#endif /* Windows */ mdbx_jitter4testing(false); MDBX_meta *meta = mdbx_meta_head(env); @@ -3318,8 +3340,11 @@ static int mdbx_txn_renew0(MDBX_txn *txn, unsigned flags) { goto bailout; } rc = mdbx_mapresize(env, txn->mt_end_pgno, upper_pgno); - if (rc != MDBX_SUCCESS) + if (rc != MDBX_SUCCESS) { + if (rc == MDBX_RESULT_TRUE) + rc = MDBX_MAP_RESIZED; goto bailout; + } } txn->mt_owner = mdbx_thread_self(); return MDBX_SUCCESS; @@ -3367,6 +3392,7 @@ int mdbx_txn_begin(MDBX_env *env, MDBX_txn *parent, unsigned flags, if (unlikely(!env || !ret)) return MDBX_EINVAL; + *ret = NULL; if (unlikely(env->me_signature != MDBX_ME_SIGNATURE)) return MDBX_EBADSIGN; @@ -3376,8 +3402,12 @@ int mdbx_txn_begin(MDBX_env *env, MDBX_txn *parent, unsigned flags, if (unlikely(env->me_flags & MDBX_FATAL_ERROR)) return MDBX_PANIC; +#if !defined(_WIN32) && !defined(_WIN64) + /* Don't check env->me_map until lock to avoid race with re-mapping for + * shrinking */ if (unlikely(!env->me_map)) return MDBX_EPERM; +#endif /* Windows */ flags &= MDBX_TXN_BEGIN_FLAGS; flags |= env->me_flags & MDBX_WRITEMAP; @@ -3388,16 +3418,21 @@ int mdbx_txn_begin(MDBX_env *env, MDBX_txn *parent, unsigned flags, if (parent) { if (unlikely(parent->mt_signature != MDBX_MT_SIGNATURE)) - return MDBX_EINVAL; + return MDBX_EBADSIGN; if (unlikely(parent->mt_owner != mdbx_thread_self())) return MDBX_THREAD_MISMATCH; +#if defined(_WIN32) || defined(_WIN64) + if (unlikely(!env->me_map)) + return MDBX_EPERM; +#endif /* Windows */ + /* Nested transactions: Max 1 child, write txns only, no writemap */ flags |= parent->mt_flags; - if (unlikely(flags & (MDBX_RDONLY | MDBX_WRITEMAP | MDBX_TXN_BLOCKED))) { + if (unlikely(flags & (MDBX_RDONLY | MDBX_WRITEMAP | MDBX_TXN_BLOCKED))) return (parent->mt_flags & MDBX_TXN_RDONLY) ? MDBX_EINVAL : MDBX_BAD_TXN; - } + /* Child txns save MDBX_pgstate and use own copy of cursors */ size = env->me_maxdbs * (sizeof(MDBX_db) + sizeof(MDBX_cursor *) + 1); size += tsize = sizeof(MDBX_ntxn); @@ -3475,10 +3510,11 @@ int mdbx_txn_begin(MDBX_env *env, MDBX_txn *parent, unsigned flags, rc = mdbx_txn_renew0(txn, flags); } - if (unlikely(rc)) { + if (unlikely(rc != MDBX_SUCCESS)) { if (txn != env->me_txn0) mdbx_free(txn); } else { + mdbx_assert(env, (txn->mt_flags & ~MDBX_RDONLY) == 0); txn->mt_signature = MDBX_MT_SIGNATURE; *ret = txn; mdbx_debug("begin txn %" PRIaTXN "%c %p on env %p, root page %" PRIaPGNO @@ -4859,8 +4895,10 @@ int mdbx_txn_commit(MDBX_txn *txn) { rc = mdbx_sync_locked( env, env->me_flags | txn->mt_flags | MDBX_SHRINK_ALLOWED, &meta); } - if (unlikely(rc != MDBX_SUCCESS)) + if (unlikely(rc != MDBX_SUCCESS)) { + env->me_flags |= MDBX_FATAL_ERROR; goto fail; + } if (likely(env->me_lck)) env->me_lck->mti_readers_refresh_flag = false; diff --git a/src/osal.c b/src/osal.c index ee5b1f3f..c5dd5c03 100644 --- a/src/osal.c +++ b/src/osal.c @@ -1071,11 +1071,11 @@ int mdbx_mresize(int flags, mdbx_mmap_t *map, size_t size, size_t limit) { &ReservedSize, MEM_RESERVE, PAGE_NOACCESS); if (!NT_SUCCESS(status)) { ReservedAddress = NULL; - if (status != /* STATUS_CONFLICTING_ADDRESSES */ 0xC0000018 || - limit == map->length) + if (status != /* STATUS_CONFLICTING_ADDRESSES */ 0xC0000018) goto bailout_ntstatus /* no way to recovery */; - /* assume we can change base address if mapping size changed */ + /* assume we can change base address if mapping size changed or prev address + * couldn't be used */ map->address = NULL; } @@ -1132,8 +1132,8 @@ retry_mapview:; if (!NT_SUCCESS(status)) { if (status == /* STATUS_CONFLICTING_ADDRESSES */ 0xC0000018 && - map->address && limit != map->length) { - /* try remap at another base address, but only if the limit is changing */ + map->address) { + /* try remap at another base address */ map->address = NULL; goto retry_mapview; }