mirror of
https://github.com/isar/libmdbx.git
synced 2026-01-24 18:32:25 +08:00
mdbx-windows: use timeouts instead of retries for file locking.
This commit is contained in:
@@ -245,7 +245,8 @@ __cold int mdbx_env_create(MDBX_env **penv) {
|
|||||||
|
|
||||||
#if defined(_WIN32) || defined(_WIN64)
|
#if defined(_WIN32) || defined(_WIN64)
|
||||||
imports.srwl_Init(&env->remap_guard);
|
imports.srwl_Init(&env->remap_guard);
|
||||||
InitializeCriticalSection(&env->windowsbug_lock);
|
InitializeCriticalSection(&env->lck_event_cs);
|
||||||
|
InitializeCriticalSection(&env->dxb_event_cs);
|
||||||
#else
|
#else
|
||||||
rc = osal_fastmutex_init(&env->remap_guard);
|
rc = osal_fastmutex_init(&env->remap_guard);
|
||||||
if (unlikely(rc != MDBX_SUCCESS)) {
|
if (unlikely(rc != MDBX_SUCCESS)) {
|
||||||
@@ -638,7 +639,8 @@ __cold int mdbx_env_close_ex(MDBX_env *env, bool dont_sync) {
|
|||||||
ENSURE(env, osal_fastmutex_destroy(&env->dbi_lock) == MDBX_SUCCESS);
|
ENSURE(env, osal_fastmutex_destroy(&env->dbi_lock) == MDBX_SUCCESS);
|
||||||
#if defined(_WIN32) || defined(_WIN64)
|
#if defined(_WIN32) || defined(_WIN64)
|
||||||
/* remap_guard don't have destructor (Slim Reader/Writer Lock) */
|
/* remap_guard don't have destructor (Slim Reader/Writer Lock) */
|
||||||
DeleteCriticalSection(&env->windowsbug_lock);
|
DeleteCriticalSection(&env->lck_event_cs);
|
||||||
|
DeleteCriticalSection(&env->dxb_event_cs);
|
||||||
#else
|
#else
|
||||||
ENSURE(env, osal_fastmutex_destroy(&env->remap_guard) == MDBX_SUCCESS);
|
ENSURE(env, osal_fastmutex_destroy(&env->remap_guard) == MDBX_SUCCESS);
|
||||||
#endif /* Windows */
|
#endif /* Windows */
|
||||||
@@ -922,6 +924,7 @@ __cold int mdbx_preopen_snapinfoW(const wchar_t *pathname, MDBX_envinfo *out, si
|
|||||||
env.fd4meta = INVALID_HANDLE_VALUE;
|
env.fd4meta = INVALID_HANDLE_VALUE;
|
||||||
#if defined(_WIN32) || defined(_WIN64)
|
#if defined(_WIN32) || defined(_WIN64)
|
||||||
env.dxb_lock_event = INVALID_HANDLE_VALUE;
|
env.dxb_lock_event = INVALID_HANDLE_VALUE;
|
||||||
|
env.lck_lock_event = INVALID_HANDLE_VALUE;
|
||||||
env.ioring.overlapped_fd = INVALID_HANDLE_VALUE;
|
env.ioring.overlapped_fd = INVALID_HANDLE_VALUE;
|
||||||
#endif /* Windows */
|
#endif /* Windows */
|
||||||
env_options_init(&env);
|
env_options_init(&env);
|
||||||
|
|||||||
13
src/env.c
13
src/env.c
@@ -305,6 +305,12 @@ __cold int env_open(MDBX_env *env, mdbx_mode_t mode) {
|
|||||||
|
|
||||||
env->fd4meta = env->lazy_fd;
|
env->fd4meta = env->lazy_fd;
|
||||||
#if defined(_WIN32) || defined(_WIN64)
|
#if defined(_WIN32) || defined(_WIN64)
|
||||||
|
env->dxb_lock_event = CreateEventW(nullptr, true, false, nullptr);
|
||||||
|
if (unlikely(!env->dxb_lock_event))
|
||||||
|
return (int)GetLastError();
|
||||||
|
env->lck_lock_event = CreateEventW(nullptr, true, false, nullptr);
|
||||||
|
if (unlikely(!env->lck_lock_event))
|
||||||
|
return (int)GetLastError();
|
||||||
eASSERT(env, env->ioring.overlapped_fd == 0);
|
eASSERT(env, env->ioring.overlapped_fd == 0);
|
||||||
bool ior_direct = false;
|
bool ior_direct = false;
|
||||||
if (!(env->flags & (MDBX_RDONLY | MDBX_SAFE_NOSYNC | MDBX_NOMETASYNC | MDBX_EXCLUSIVE))) {
|
if (!(env->flags & (MDBX_RDONLY | MDBX_SAFE_NOSYNC | MDBX_NOMETASYNC | MDBX_EXCLUSIVE))) {
|
||||||
@@ -346,9 +352,6 @@ __cold int env_open(MDBX_env *env, mdbx_mode_t mode) {
|
|||||||
&env->ioring.overlapped_fd, 0);
|
&env->ioring.overlapped_fd, 0);
|
||||||
if (unlikely(rc != MDBX_SUCCESS))
|
if (unlikely(rc != MDBX_SUCCESS))
|
||||||
return rc;
|
return rc;
|
||||||
env->dxb_lock_event = CreateEventW(nullptr, true, false, nullptr);
|
|
||||||
if (unlikely(!env->dxb_lock_event))
|
|
||||||
return (int)GetLastError();
|
|
||||||
osal_fseek(env->ioring.overlapped_fd, safe_parking_lot_offset);
|
osal_fseek(env->ioring.overlapped_fd, safe_parking_lot_offset);
|
||||||
}
|
}
|
||||||
#else
|
#else
|
||||||
@@ -545,6 +548,10 @@ __cold int env_close(MDBX_env *env, bool resurrect_after_fork) {
|
|||||||
CloseHandle(env->dxb_lock_event);
|
CloseHandle(env->dxb_lock_event);
|
||||||
env->dxb_lock_event = INVALID_HANDLE_VALUE;
|
env->dxb_lock_event = INVALID_HANDLE_VALUE;
|
||||||
}
|
}
|
||||||
|
if (env->lck_lock_event != INVALID_HANDLE_VALUE) {
|
||||||
|
CloseHandle(env->lck_lock_event);
|
||||||
|
env->lck_lock_event = INVALID_HANDLE_VALUE;
|
||||||
|
}
|
||||||
eASSERT(env, !resurrect_after_fork);
|
eASSERT(env, !resurrect_after_fork);
|
||||||
if (env->pathname_char) {
|
if (env->pathname_char) {
|
||||||
osal_free(env->pathname_char);
|
osal_free(env->pathname_char);
|
||||||
|
|||||||
@@ -352,6 +352,7 @@ struct MDBX_env {
|
|||||||
mdbx_filehandle_t dsync_fd, fd4meta;
|
mdbx_filehandle_t dsync_fd, fd4meta;
|
||||||
#if defined(_WIN32) || defined(_WIN64)
|
#if defined(_WIN32) || defined(_WIN64)
|
||||||
HANDLE dxb_lock_event;
|
HANDLE dxb_lock_event;
|
||||||
|
HANDLE lck_lock_event;
|
||||||
#endif /* Windows */
|
#endif /* Windows */
|
||||||
osal_mmap_t lck_mmap; /* The lock file */
|
osal_mmap_t lck_mmap; /* The lock file */
|
||||||
lck_t *lck;
|
lck_t *lck;
|
||||||
@@ -481,7 +482,8 @@ struct MDBX_env {
|
|||||||
#if defined(_WIN32) || defined(_WIN64)
|
#if defined(_WIN32) || defined(_WIN64)
|
||||||
osal_srwlock_t remap_guard;
|
osal_srwlock_t remap_guard;
|
||||||
/* Workaround for LockFileEx and WriteFile multithread bug */
|
/* Workaround for LockFileEx and WriteFile multithread bug */
|
||||||
CRITICAL_SECTION windowsbug_lock;
|
CRITICAL_SECTION lck_event_cs;
|
||||||
|
CRITICAL_SECTION dxb_event_cs;
|
||||||
char *pathname_char; /* cache of multi-byte representation of pathname
|
char *pathname_char; /* cache of multi-byte representation of pathname
|
||||||
to the DB files */
|
to the DB files */
|
||||||
#else
|
#else
|
||||||
|
|||||||
@@ -16,8 +16,11 @@
|
|||||||
#define LCK_WAITFOR 0
|
#define LCK_WAITFOR 0
|
||||||
#define LCK_DONTWAIT LOCKFILE_FAIL_IMMEDIATELY
|
#define LCK_DONTWAIT LOCKFILE_FAIL_IMMEDIATELY
|
||||||
|
|
||||||
static int flock_with_event(HANDLE fd, HANDLE event, unsigned flags, size_t offset, size_t bytes) {
|
static int flock_ex(HANDLE fd, HANDLE event, unsigned flags, size_t offset, size_t bytes, unsigned timeout_ms) {
|
||||||
TRACE("lock>>: fd %p, event %p, flags 0x%x offset %zu, bytes %zu >>", fd, event, flags, offset, bytes);
|
TRACE("lock>>: fd %p, timeout %u ms, event %p, flags 0x%x offset %zu, bytes %zu >>", fd, timeout_ms, event, flags,
|
||||||
|
offset, bytes);
|
||||||
|
assert(timeout_ms == 0 || (event && event != INVALID_HANDLE_VALUE));
|
||||||
|
assert(timeout_ms == 0 || (flags & LCK_DONTWAIT) == 0);
|
||||||
OVERLAPPED ov;
|
OVERLAPPED ov;
|
||||||
ov.Internal = 0;
|
ov.Internal = 0;
|
||||||
ov.InternalHigh = 0;
|
ov.InternalHigh = 0;
|
||||||
@@ -25,43 +28,43 @@ static int flock_with_event(HANDLE fd, HANDLE event, unsigned flags, size_t offs
|
|||||||
ov.Offset = (DWORD)offset;
|
ov.Offset = (DWORD)offset;
|
||||||
ov.OffsetHigh = HIGH_DWORD(offset);
|
ov.OffsetHigh = HIGH_DWORD(offset);
|
||||||
|
|
||||||
int retry_left = (flags & LOCKFILE_FAIL_IMMEDIATELY) ? 3 : 0;
|
if (LockFileEx(fd, flags, 0, (DWORD)bytes, HIGH_DWORD(bytes), &ov)) {
|
||||||
while (true) {
|
TRACE("lock<<: fd %p, timeout %u ms, event %p, flags 0x%x offset %zu, bytes %zu << %s", fd, timeout_ms, event,
|
||||||
if (LockFileEx(fd, flags, 0, (DWORD)bytes, HIGH_DWORD(bytes), &ov)) {
|
flags, offset, bytes, "done");
|
||||||
TRACE("lock<<: fd %p, event %p, flags 0x%x offset %zu, bytes %zu << %s", fd, event, flags, offset, bytes, "done");
|
return MDBX_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
DWORD rc = GetLastError();
|
||||||
|
if (rc == ERROR_IO_PENDING) {
|
||||||
|
if (timeout_ms) {
|
||||||
|
rc = osal_waitstatus2errcode(WaitForSingleObject(event, timeout_ms));
|
||||||
|
if (rc != MDBX_SUCCESS) {
|
||||||
|
if (rc == ERROR_TIMEOUT)
|
||||||
|
rc = ERROR_LOCK_VIOLATION;
|
||||||
|
goto bailout;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (GetOverlappedResult(fd, &ov, &rc, true)) {
|
||||||
|
TRACE("lock<<: fd %p, timeout %u ms, event %p, flags 0x%x offset %zu, bytes %zu << %s", fd, timeout_ms, event,
|
||||||
|
flags, offset, bytes, "overlapped-done");
|
||||||
return MDBX_SUCCESS;
|
return MDBX_SUCCESS;
|
||||||
}
|
}
|
||||||
|
bailout:
|
||||||
DWORD rc = GetLastError();
|
CancelIo(fd);
|
||||||
if (rc == ERROR_IO_PENDING) {
|
|
||||||
if (event) {
|
|
||||||
if (GetOverlappedResult(fd, &ov, &rc, true)) {
|
|
||||||
TRACE("lock<<: fd %p, event %p, flags 0x%x offset %zu, bytes %zu << %s", fd, event, flags, offset, bytes,
|
|
||||||
"overlapped-done");
|
|
||||||
return MDBX_SUCCESS;
|
|
||||||
}
|
|
||||||
rc = GetLastError();
|
|
||||||
} else
|
|
||||||
CancelIo(fd);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (rc != ERROR_LOCK_VIOLATION || --retry_left < 1) {
|
|
||||||
TRACE("lock<<: fd %p, event %p, flags 0x%x offset %zu, bytes %zu << err %d", fd, event, flags, offset, bytes,
|
|
||||||
(int)rc);
|
|
||||||
return (int)rc;
|
|
||||||
}
|
|
||||||
|
|
||||||
SleepEx(0, true);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TRACE("lock<<: fd %p, timeout %u ms, event %p, flags 0x%x offset %zu, bytes %zu << err %d", fd, timeout_ms, event,
|
||||||
|
flags, offset, bytes, (int)rc);
|
||||||
|
return (int)rc;
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline int flock(HANDLE fd, unsigned flags, size_t offset, size_t bytes) {
|
static int flock_lck(const MDBX_env *env, unsigned flags, size_t offset, size_t bytes, unsigned timeout_ms) {
|
||||||
return flock_with_event(fd, 0, flags, offset, bytes);
|
return flock_ex(env->lck_mmap.fd, env->lck_lock_event, flags, offset, bytes, timeout_ms);
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline int flock_data(const MDBX_env *env, unsigned flags, size_t offset, size_t bytes) {
|
static int flock_dxb(const MDBX_env *env, unsigned flags, size_t offset, size_t bytes) {
|
||||||
const HANDLE fd4data = env->ioring.overlapped_fd ? env->ioring.overlapped_fd : env->lazy_fd;
|
const HANDLE fd4data = env->ioring.overlapped_fd ? env->ioring.overlapped_fd : env->lazy_fd;
|
||||||
return flock_with_event(fd4data, env->dxb_lock_event, flags, offset, bytes);
|
return flock_ex(fd4data, env->dxb_lock_event, flags, offset, bytes, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
static int funlock(mdbx_filehandle_t fd, size_t offset, size_t bytes) {
|
static int funlock(mdbx_filehandle_t fd, size_t offset, size_t bytes) {
|
||||||
@@ -84,11 +87,11 @@ static int funlock(mdbx_filehandle_t fd, size_t offset, size_t bytes) {
|
|||||||
|
|
||||||
int lck_txn_lock(MDBX_env *env, bool dontwait) {
|
int lck_txn_lock(MDBX_env *env, bool dontwait) {
|
||||||
if (dontwait) {
|
if (dontwait) {
|
||||||
if (!TryEnterCriticalSection(&env->windowsbug_lock))
|
if (!TryEnterCriticalSection(&env->dxb_event_cs))
|
||||||
return MDBX_BUSY;
|
return MDBX_BUSY;
|
||||||
} else {
|
} else {
|
||||||
__try {
|
__try {
|
||||||
EnterCriticalSection(&env->windowsbug_lock);
|
EnterCriticalSection(&env->dxb_event_cs);
|
||||||
} __except ((GetExceptionCode() == 0xC0000194 /* STATUS_POSSIBLE_DEADLOCK / EXCEPTION_POSSIBLE_DEADLOCK */)
|
} __except ((GetExceptionCode() == 0xC0000194 /* STATUS_POSSIBLE_DEADLOCK / EXCEPTION_POSSIBLE_DEADLOCK */)
|
||||||
? EXCEPTION_EXECUTE_HANDLER
|
? EXCEPTION_EXECUTE_HANDLER
|
||||||
: EXCEPTION_CONTINUE_SEARCH) {
|
: EXCEPTION_CONTINUE_SEARCH) {
|
||||||
@@ -100,34 +103,32 @@ int lck_txn_lock(MDBX_env *env, bool dontwait) {
|
|||||||
if (env->flags & MDBX_EXCLUSIVE)
|
if (env->flags & MDBX_EXCLUSIVE)
|
||||||
goto done;
|
goto done;
|
||||||
|
|
||||||
const HANDLE fd4data = env->ioring.overlapped_fd ? env->ioring.overlapped_fd : env->lazy_fd;
|
int rc = flock_dxb(env, dontwait ? (LCK_EXCLUSIVE | LCK_DONTWAIT) : (LCK_EXCLUSIVE | LCK_WAITFOR), DXB_BODY);
|
||||||
int rc = flock_with_event(fd4data, env->dxb_lock_event,
|
|
||||||
dontwait ? (LCK_EXCLUSIVE | LCK_DONTWAIT) : (LCK_EXCLUSIVE | LCK_WAITFOR), DXB_BODY);
|
|
||||||
if (rc == MDBX_SUCCESS) {
|
if (rc == MDBX_SUCCESS) {
|
||||||
done:
|
done:
|
||||||
if (env->basal_txn)
|
if (env->basal_txn)
|
||||||
env->basal_txn->owner = osal_thread_self();
|
env->basal_txn->owner = osal_thread_self();
|
||||||
/* Zap: Failing to release lock 'env->windowsbug_lock'
|
/* Zap: Failing to release lock 'env->dxb_event_cs'
|
||||||
* in function 'mdbx_txn_lock' */
|
* in function 'mdbx_txn_lock' */
|
||||||
MDBX_SUPPRESS_GOOFY_MSVC_ANALYZER(26115);
|
MDBX_SUPPRESS_GOOFY_MSVC_ANALYZER(26115);
|
||||||
return MDBX_SUCCESS;
|
return MDBX_SUCCESS;
|
||||||
}
|
}
|
||||||
|
|
||||||
LeaveCriticalSection(&env->windowsbug_lock);
|
LeaveCriticalSection(&env->dxb_event_cs);
|
||||||
return (!dontwait || rc != ERROR_LOCK_VIOLATION) ? rc : MDBX_BUSY;
|
return (!dontwait || rc != ERROR_LOCK_VIOLATION) ? rc : MDBX_BUSY;
|
||||||
}
|
}
|
||||||
|
|
||||||
void lck_txn_unlock(MDBX_env *env) {
|
void lck_txn_unlock(MDBX_env *env) {
|
||||||
eASSERT(env, !env->basal_txn || env->basal_txn->owner == osal_thread_self());
|
eASSERT(env, !env->basal_txn || env->basal_txn->owner == osal_thread_self());
|
||||||
if ((env->flags & MDBX_EXCLUSIVE) == 0) {
|
if ((env->flags & MDBX_EXCLUSIVE) == 0) {
|
||||||
const HANDLE fd4data = env->ioring.overlapped_fd ? env->ioring.overlapped_fd : env->lazy_fd;
|
int err = funlock(env->ioring.overlapped_fd ? env->ioring.overlapped_fd : env->lazy_fd, DXB_BODY);
|
||||||
int err = funlock(fd4data, DXB_BODY);
|
|
||||||
if (err != MDBX_SUCCESS)
|
if (err != MDBX_SUCCESS)
|
||||||
mdbx_panic("%s failed: err %u", __func__, err);
|
mdbx_panic("%s failed: err %u", __func__, err);
|
||||||
}
|
}
|
||||||
if (env->basal_txn)
|
if (env->basal_txn)
|
||||||
env->basal_txn->owner = 0;
|
env->basal_txn->owner = 0;
|
||||||
LeaveCriticalSection(&env->windowsbug_lock);
|
LeaveCriticalSection(&env->dxb_event_cs);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*----------------------------------------------------------------------------*/
|
/*----------------------------------------------------------------------------*/
|
||||||
@@ -142,19 +143,34 @@ void lck_txn_unlock(MDBX_env *env) {
|
|||||||
#define LCK_UPPER LCK_UP_OFFSET, LCK_UP_LEN
|
#define LCK_UPPER LCK_UP_OFFSET, LCK_UP_LEN
|
||||||
|
|
||||||
int lck_rdt_lock(MDBX_env *env) {
|
int lck_rdt_lock(MDBX_env *env) {
|
||||||
|
int rc;
|
||||||
imports.srwl_AcquireShared(&env->remap_guard);
|
imports.srwl_AcquireShared(&env->remap_guard);
|
||||||
|
|
||||||
|
__try {
|
||||||
|
EnterCriticalSection(&env->lck_event_cs);
|
||||||
|
} __except ((GetExceptionCode() == 0xC0000194 /* STATUS_POSSIBLE_DEADLOCK / EXCEPTION_POSSIBLE_DEADLOCK */)
|
||||||
|
? EXCEPTION_EXECUTE_HANDLER
|
||||||
|
: EXCEPTION_CONTINUE_SEARCH) {
|
||||||
|
rc = MDBX_EDEADLK;
|
||||||
|
goto bailout;
|
||||||
|
}
|
||||||
|
|
||||||
if (env->lck_mmap.fd == INVALID_HANDLE_VALUE)
|
if (env->lck_mmap.fd == INVALID_HANDLE_VALUE)
|
||||||
return MDBX_SUCCESS; /* readonly database in readonly filesystem */
|
goto done; /* readonly database in readonly filesystem */
|
||||||
|
|
||||||
/* transition from S-? (used) to S-E (locked),
|
/* transition from S-? (used) to S-E (locked),
|
||||||
* e.g. exclusive lock upper-part */
|
* e.g. exclusive lock upper-part */
|
||||||
if (env->flags & MDBX_EXCLUSIVE)
|
if (env->flags & MDBX_EXCLUSIVE)
|
||||||
return MDBX_SUCCESS;
|
goto done;
|
||||||
|
|
||||||
int rc = flock(env->lck_mmap.fd, LCK_EXCLUSIVE | LCK_WAITFOR, LCK_UPPER);
|
rc = flock_lck(env, LCK_EXCLUSIVE | LCK_WAITFOR, LCK_UPPER, 0);
|
||||||
if (rc == MDBX_SUCCESS)
|
if (rc == MDBX_SUCCESS) {
|
||||||
|
done:
|
||||||
|
MDBX_SUPPRESS_GOOFY_MSVC_ANALYZER(26115);
|
||||||
return MDBX_SUCCESS;
|
return MDBX_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
bailout:
|
||||||
imports.srwl_ReleaseShared(&env->remap_guard);
|
imports.srwl_ReleaseShared(&env->remap_guard);
|
||||||
return rc;
|
return rc;
|
||||||
}
|
}
|
||||||
@@ -166,11 +182,12 @@ void lck_rdt_unlock(MDBX_env *env) {
|
|||||||
if (err != MDBX_SUCCESS)
|
if (err != MDBX_SUCCESS)
|
||||||
mdbx_panic("%s failed: err %u", __func__, err);
|
mdbx_panic("%s failed: err %u", __func__, err);
|
||||||
}
|
}
|
||||||
|
LeaveCriticalSection(&env->lck_event_cs);
|
||||||
imports.srwl_ReleaseShared(&env->remap_guard);
|
imports.srwl_ReleaseShared(&env->remap_guard);
|
||||||
}
|
}
|
||||||
|
|
||||||
int osal_lockfile(mdbx_filehandle_t fd, bool wait) {
|
int osal_lockfile(mdbx_filehandle_t fd, bool wait) {
|
||||||
return flock(fd, wait ? LCK_EXCLUSIVE | LCK_WAITFOR : LCK_EXCLUSIVE | LCK_DONTWAIT, 0, DXB_MAXLEN);
|
return flock_ex(fd, 0, wait ? LCK_EXCLUSIVE | LCK_WAITFOR : LCK_EXCLUSIVE | LCK_DONTWAIT, 0, DXB_MAXLEN, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
static int suspend_and_append(mdbx_handle_array_t **array, const DWORD ThreadId) {
|
static int suspend_and_append(mdbx_handle_array_t **array, const DWORD ThreadId) {
|
||||||
@@ -359,15 +376,18 @@ static void lck_unlock(MDBX_env *env) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#define TIMEOUT_SHORT_MS 121
|
||||||
|
#define TIMEOUT_LONG_MS 900000 /* 15 min */
|
||||||
|
|
||||||
/* Seize state as 'exclusive-write' (E-E and returns MDBX_RESULT_TRUE)
|
/* Seize state as 'exclusive-write' (E-E and returns MDBX_RESULT_TRUE)
|
||||||
* or as 'used' (S-? and returns MDBX_RESULT_FALSE).
|
* or as 'used' (S-? and returns MDBX_RESULT_FALSE).
|
||||||
* Otherwise returns an error. */
|
* Otherwise returns an error. */
|
||||||
static int internal_seize_lck(HANDLE lfd) {
|
static int internal_seize_lck(MDBX_env *env) {
|
||||||
assert(lfd != INVALID_HANDLE_VALUE);
|
assert(env->lck_mmap.fd != INVALID_HANDLE_VALUE);
|
||||||
|
|
||||||
/* 1) now on ?-? (free), get ?-E (middle) */
|
/* 1) now on ?-? (free), get ?-E (middle) */
|
||||||
jitter4testing(false);
|
jitter4testing(false);
|
||||||
int rc = flock(lfd, LCK_EXCLUSIVE | LCK_WAITFOR, LCK_UPPER);
|
int rc = flock_lck(env, LCK_EXCLUSIVE | LCK_WAITFOR, LCK_UPPER, TIMEOUT_LONG_MS);
|
||||||
if (rc != MDBX_SUCCESS) {
|
if (rc != MDBX_SUCCESS) {
|
||||||
/* 2) something went wrong, give up */;
|
/* 2) something went wrong, give up */;
|
||||||
ERROR("%s, err %u", "?-?(free) >> ?-E(middle)", rc);
|
ERROR("%s, err %u", "?-?(free) >> ?-E(middle)", rc);
|
||||||
@@ -376,7 +396,7 @@ static int internal_seize_lck(HANDLE lfd) {
|
|||||||
|
|
||||||
/* 3) now on ?-E (middle), try E-E (exclusive-write) */
|
/* 3) now on ?-E (middle), try E-E (exclusive-write) */
|
||||||
jitter4testing(false);
|
jitter4testing(false);
|
||||||
rc = flock(lfd, LCK_EXCLUSIVE | LCK_DONTWAIT, LCK_LOWER);
|
rc = flock_lck(env, LCK_EXCLUSIVE, LCK_LOWER, TIMEOUT_SHORT_MS);
|
||||||
if (rc == MDBX_SUCCESS)
|
if (rc == MDBX_SUCCESS)
|
||||||
return MDBX_RESULT_TRUE /* 4) got E-E (exclusive-write), done */;
|
return MDBX_RESULT_TRUE /* 4) got E-E (exclusive-write), done */;
|
||||||
|
|
||||||
@@ -384,7 +404,7 @@ static int internal_seize_lck(HANDLE lfd) {
|
|||||||
jitter4testing(false);
|
jitter4testing(false);
|
||||||
if (rc != ERROR_SHARING_VIOLATION && rc != ERROR_LOCK_VIOLATION) {
|
if (rc != ERROR_SHARING_VIOLATION && rc != ERROR_LOCK_VIOLATION) {
|
||||||
/* 6) something went wrong, give up */
|
/* 6) something went wrong, give up */
|
||||||
rc = funlock(lfd, LCK_UPPER);
|
rc = funlock(env->lck_mmap.fd, LCK_UPPER);
|
||||||
if (rc != MDBX_SUCCESS)
|
if (rc != MDBX_SUCCESS)
|
||||||
mdbx_panic("%s(%s) failed: err %u", __func__, "?-E(middle) >> ?-?(free)", rc);
|
mdbx_panic("%s(%s) failed: err %u", __func__, "?-E(middle) >> ?-?(free)", rc);
|
||||||
return rc;
|
return rc;
|
||||||
@@ -392,7 +412,7 @@ static int internal_seize_lck(HANDLE lfd) {
|
|||||||
|
|
||||||
/* 7) still on ?-E (middle), try S-E (locked) */
|
/* 7) still on ?-E (middle), try S-E (locked) */
|
||||||
jitter4testing(false);
|
jitter4testing(false);
|
||||||
rc = flock(lfd, LCK_SHARED | LCK_DONTWAIT, LCK_LOWER);
|
rc = flock_lck(env, LCK_SHARED, LCK_LOWER, TIMEOUT_LONG_MS);
|
||||||
|
|
||||||
jitter4testing(false);
|
jitter4testing(false);
|
||||||
if (rc != MDBX_SUCCESS)
|
if (rc != MDBX_SUCCESS)
|
||||||
@@ -400,7 +420,7 @@ static int internal_seize_lck(HANDLE lfd) {
|
|||||||
|
|
||||||
/* 8) now on S-E (locked) or still on ?-E (middle),
|
/* 8) now on S-E (locked) or still on ?-E (middle),
|
||||||
* transition to S-? (used) or ?-? (free) */
|
* transition to S-? (used) or ?-? (free) */
|
||||||
int err = funlock(lfd, LCK_UPPER);
|
int err = funlock(env->lck_mmap.fd, LCK_UPPER);
|
||||||
if (err != MDBX_SUCCESS)
|
if (err != MDBX_SUCCESS)
|
||||||
mdbx_panic("%s(%s) failed: err %u", __func__, "X-E(locked/middle) >> X-?(used/free)", err);
|
mdbx_panic("%s(%s) failed: err %u", __func__, "X-E(locked/middle) >> X-?(used/free)", err);
|
||||||
|
|
||||||
@@ -409,8 +429,6 @@ static int internal_seize_lck(HANDLE lfd) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
int lck_seize(MDBX_env *env) {
|
int lck_seize(MDBX_env *env) {
|
||||||
const HANDLE fd4data = env->ioring.overlapped_fd ? env->ioring.overlapped_fd : env->lazy_fd;
|
|
||||||
assert(fd4data != INVALID_HANDLE_VALUE);
|
|
||||||
if (env->flags & MDBX_EXCLUSIVE)
|
if (env->flags & MDBX_EXCLUSIVE)
|
||||||
return MDBX_RESULT_TRUE /* nope since files were must be opened
|
return MDBX_RESULT_TRUE /* nope since files were must be opened
|
||||||
non-shareable */
|
non-shareable */
|
||||||
@@ -419,13 +437,13 @@ int lck_seize(MDBX_env *env) {
|
|||||||
if (env->lck_mmap.fd == INVALID_HANDLE_VALUE) {
|
if (env->lck_mmap.fd == INVALID_HANDLE_VALUE) {
|
||||||
/* LY: without-lck mode (e.g. on read-only filesystem) */
|
/* LY: without-lck mode (e.g. on read-only filesystem) */
|
||||||
jitter4testing(false);
|
jitter4testing(false);
|
||||||
int rc = flock_data(env, LCK_SHARED | LCK_DONTWAIT, DXB_WHOLE);
|
int rc = flock_dxb(env, LCK_SHARED | LCK_DONTWAIT, DXB_WHOLE);
|
||||||
if (rc != MDBX_SUCCESS)
|
if (rc != MDBX_SUCCESS)
|
||||||
ERROR("%s, err %u", "without-lck", rc);
|
ERROR("%s, err %u", "without-lck", rc);
|
||||||
return rc;
|
return rc;
|
||||||
}
|
}
|
||||||
|
|
||||||
int rc = internal_seize_lck(env->lck_mmap.fd);
|
int rc = internal_seize_lck(env);
|
||||||
jitter4testing(false);
|
jitter4testing(false);
|
||||||
if (rc == MDBX_RESULT_TRUE && (env->flags & MDBX_RDONLY) == 0) {
|
if (rc == MDBX_RESULT_TRUE && (env->flags & MDBX_RDONLY) == 0) {
|
||||||
/* Check that another process don't operates in without-lck mode.
|
/* Check that another process don't operates in without-lck mode.
|
||||||
@@ -434,7 +452,7 @@ int lck_seize(MDBX_env *env) {
|
|||||||
* - we need an exclusive lock for do so;
|
* - we need an exclusive lock for do so;
|
||||||
* - we can't lock meta-pages, otherwise other process could get an error
|
* - we can't lock meta-pages, otherwise other process could get an error
|
||||||
* while opening db in valid (non-conflict) mode. */
|
* while opening db in valid (non-conflict) mode. */
|
||||||
int err = flock_data(env, LCK_EXCLUSIVE | LCK_DONTWAIT, DXB_WHOLE);
|
int err = flock_dxb(env, LCK_EXCLUSIVE | LCK_DONTWAIT, DXB_WHOLE);
|
||||||
if (err != MDBX_SUCCESS) {
|
if (err != MDBX_SUCCESS) {
|
||||||
ERROR("%s, err %u", "lock-against-without-lck", err);
|
ERROR("%s, err %u", "lock-against-without-lck", err);
|
||||||
jitter4testing(false);
|
jitter4testing(false);
|
||||||
@@ -442,7 +460,7 @@ int lck_seize(MDBX_env *env) {
|
|||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
jitter4testing(false);
|
jitter4testing(false);
|
||||||
err = funlock(fd4data, DXB_WHOLE);
|
err = funlock(env->ioring.overlapped_fd ? env->ioring.overlapped_fd : env->lazy_fd, DXB_WHOLE);
|
||||||
if (err != MDBX_SUCCESS)
|
if (err != MDBX_SUCCESS)
|
||||||
mdbx_panic("%s(%s) failed: err %u", __func__, "unlock-against-without-lck", err);
|
mdbx_panic("%s(%s) failed: err %u", __func__, "unlock-against-without-lck", err);
|
||||||
}
|
}
|
||||||
@@ -451,9 +469,7 @@ int lck_seize(MDBX_env *env) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
int lck_downgrade(MDBX_env *env) {
|
int lck_downgrade(MDBX_env *env) {
|
||||||
const HANDLE fd4data = env->ioring.overlapped_fd ? env->ioring.overlapped_fd : env->lazy_fd;
|
|
||||||
/* Transite from exclusive-write state (E-E) to used (S-?) */
|
/* Transite from exclusive-write state (E-E) to used (S-?) */
|
||||||
assert(fd4data != INVALID_HANDLE_VALUE);
|
|
||||||
assert(env->lck_mmap.fd != INVALID_HANDLE_VALUE);
|
assert(env->lck_mmap.fd != INVALID_HANDLE_VALUE);
|
||||||
|
|
||||||
if (env->flags & MDBX_EXCLUSIVE)
|
if (env->flags & MDBX_EXCLUSIVE)
|
||||||
@@ -465,7 +481,7 @@ int lck_downgrade(MDBX_env *env) {
|
|||||||
mdbx_panic("%s(%s) failed: err %u", __func__, "E-E(exclusive-write) >> ?-E(middle)", rc);
|
mdbx_panic("%s(%s) failed: err %u", __func__, "E-E(exclusive-write) >> ?-E(middle)", rc);
|
||||||
|
|
||||||
/* 2) now at ?-E (middle), transition to S-E (locked) */
|
/* 2) now at ?-E (middle), transition to S-E (locked) */
|
||||||
rc = flock(env->lck_mmap.fd, LCK_SHARED | LCK_DONTWAIT, LCK_LOWER);
|
rc = flock_lck(env, LCK_SHARED, LCK_LOWER, TIMEOUT_LONG_MS);
|
||||||
if (rc != MDBX_SUCCESS) {
|
if (rc != MDBX_SUCCESS) {
|
||||||
/* 3) something went wrong, give up */;
|
/* 3) something went wrong, give up */;
|
||||||
ERROR("%s, err %u", "?-E(middle) >> S-E(locked)", rc);
|
ERROR("%s, err %u", "?-E(middle) >> S-E(locked)", rc);
|
||||||
@@ -490,7 +506,7 @@ int lck_upgrade(MDBX_env *env, bool dont_wait) {
|
|||||||
|
|
||||||
/* 1) now on S-? (used), try S-E (locked) */
|
/* 1) now on S-? (used), try S-E (locked) */
|
||||||
jitter4testing(false);
|
jitter4testing(false);
|
||||||
int rc = flock(env->lck_mmap.fd, dont_wait ? LCK_EXCLUSIVE | LCK_DONTWAIT : LCK_EXCLUSIVE, LCK_UPPER);
|
int rc = flock_lck(env, dont_wait ? LCK_EXCLUSIVE | LCK_DONTWAIT : LCK_EXCLUSIVE, LCK_UPPER, 0);
|
||||||
if (rc != MDBX_SUCCESS) {
|
if (rc != MDBX_SUCCESS) {
|
||||||
/* 2) something went wrong, give up */;
|
/* 2) something went wrong, give up */;
|
||||||
VERBOSE("%s, err %u", "S-?(used) >> S-E(locked)", rc);
|
VERBOSE("%s, err %u", "S-?(used) >> S-E(locked)", rc);
|
||||||
@@ -504,7 +520,7 @@ int lck_upgrade(MDBX_env *env, bool dont_wait) {
|
|||||||
|
|
||||||
/* 4) now on ?-E (middle), try E-E (exclusive-write) */
|
/* 4) now on ?-E (middle), try E-E (exclusive-write) */
|
||||||
jitter4testing(false);
|
jitter4testing(false);
|
||||||
rc = flock(env->lck_mmap.fd, dont_wait ? LCK_EXCLUSIVE | LCK_DONTWAIT : LCK_EXCLUSIVE, LCK_LOWER);
|
rc = flock_lck(env, dont_wait ? LCK_EXCLUSIVE | LCK_DONTWAIT : LCK_EXCLUSIVE, LCK_LOWER, 0);
|
||||||
if (rc != MDBX_SUCCESS) {
|
if (rc != MDBX_SUCCESS) {
|
||||||
/* 5) something went wrong, give up */;
|
/* 5) something went wrong, give up */;
|
||||||
VERBOSE("%s, err %u", "?-E(middle) >> E-E(exclusive-write)", rc);
|
VERBOSE("%s, err %u", "?-E(middle) >> E-E(exclusive-write)", rc);
|
||||||
|
|||||||
25
src/osal.c
25
src/osal.c
@@ -14,23 +14,6 @@
|
|||||||
#include <crtdbg.h>
|
#include <crtdbg.h>
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
static int waitstatus2errcode(DWORD result) {
|
|
||||||
switch (result) {
|
|
||||||
case WAIT_OBJECT_0:
|
|
||||||
return MDBX_SUCCESS;
|
|
||||||
case WAIT_FAILED:
|
|
||||||
return (int)GetLastError();
|
|
||||||
case WAIT_ABANDONED:
|
|
||||||
return ERROR_ABANDONED_WAIT_0;
|
|
||||||
case WAIT_IO_COMPLETION:
|
|
||||||
return ERROR_USER_APC;
|
|
||||||
case WAIT_TIMEOUT:
|
|
||||||
return ERROR_TIMEOUT;
|
|
||||||
default:
|
|
||||||
return ERROR_UNHANDLED_ERROR;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Map a result from an NTAPI call to WIN32 error code. */
|
/* Map a result from an NTAPI call to WIN32 error code. */
|
||||||
static int ntstatus2errcode(NTSTATUS status) {
|
static int ntstatus2errcode(NTSTATUS status) {
|
||||||
DWORD dummy;
|
DWORD dummy;
|
||||||
@@ -420,7 +403,7 @@ int osal_condpair_destroy(osal_condpair_t *condpair) {
|
|||||||
int osal_condpair_lock(osal_condpair_t *condpair) {
|
int osal_condpair_lock(osal_condpair_t *condpair) {
|
||||||
#if defined(_WIN32) || defined(_WIN64)
|
#if defined(_WIN32) || defined(_WIN64)
|
||||||
DWORD code = WaitForSingleObject(condpair->mutex, INFINITE);
|
DWORD code = WaitForSingleObject(condpair->mutex, INFINITE);
|
||||||
return waitstatus2errcode(code);
|
return osal_waitstatus2errcode(code);
|
||||||
#else
|
#else
|
||||||
return osal_pthread_mutex_lock(&condpair->mutex);
|
return osal_pthread_mutex_lock(&condpair->mutex);
|
||||||
#endif
|
#endif
|
||||||
@@ -450,7 +433,7 @@ int osal_condpair_wait(osal_condpair_t *condpair, bool part) {
|
|||||||
if (code == WAIT_OBJECT_0)
|
if (code == WAIT_OBJECT_0)
|
||||||
return MDBX_SUCCESS;
|
return MDBX_SUCCESS;
|
||||||
}
|
}
|
||||||
return waitstatus2errcode(code);
|
return osal_waitstatus2errcode(code);
|
||||||
#else
|
#else
|
||||||
return pthread_cond_wait(&condpair->cond[part], &condpair->mutex);
|
return pthread_cond_wait(&condpair->cond[part], &condpair->mutex);
|
||||||
#endif
|
#endif
|
||||||
@@ -1192,7 +1175,7 @@ int osal_openfile(const enum osal_openfile_purpose purpose, const MDBX_env *env,
|
|||||||
case MDBX_OPEN_LCK:
|
case MDBX_OPEN_LCK:
|
||||||
CreationDisposition = OPEN_ALWAYS;
|
CreationDisposition = OPEN_ALWAYS;
|
||||||
DesiredAccess |= GENERIC_READ | GENERIC_WRITE;
|
DesiredAccess |= GENERIC_READ | GENERIC_WRITE;
|
||||||
FlagsAndAttributes |= FILE_ATTRIBUTE_HIDDEN | FILE_ATTRIBUTE_TEMPORARY;
|
FlagsAndAttributes |= FILE_ATTRIBUTE_HIDDEN | FILE_ATTRIBUTE_TEMPORARY | FILE_FLAG_OVERLAPPED;
|
||||||
break;
|
break;
|
||||||
case MDBX_OPEN_DXB_READ:
|
case MDBX_OPEN_DXB_READ:
|
||||||
CreationDisposition = OPEN_EXISTING;
|
CreationDisposition = OPEN_EXISTING;
|
||||||
@@ -1694,7 +1677,7 @@ int osal_thread_create(osal_thread_t *thread, THREAD_RESULT(THREAD_CALL *start_r
|
|||||||
int osal_thread_join(osal_thread_t thread) {
|
int osal_thread_join(osal_thread_t thread) {
|
||||||
#if defined(_WIN32) || defined(_WIN64)
|
#if defined(_WIN32) || defined(_WIN64)
|
||||||
DWORD code = WaitForSingleObject(thread, INFINITE);
|
DWORD code = WaitForSingleObject(thread, INFINITE);
|
||||||
return waitstatus2errcode(code);
|
return osal_waitstatus2errcode(code);
|
||||||
#else
|
#else
|
||||||
void *unused_retval = &unused_retval;
|
void *unused_retval = &unused_retval;
|
||||||
return pthread_join(thread, &unused_retval);
|
return pthread_join(thread, &unused_retval);
|
||||||
|
|||||||
17
src/osal.h
17
src/osal.h
@@ -206,6 +206,23 @@ typedef struct osal_mmap {
|
|||||||
|
|
||||||
#define MDBX_HAVE_PWRITEV 0
|
#define MDBX_HAVE_PWRITEV 0
|
||||||
|
|
||||||
|
static inline int osal_waitstatus2errcode(DWORD result) {
|
||||||
|
switch (result) {
|
||||||
|
case WAIT_OBJECT_0:
|
||||||
|
return MDBX_SUCCESS;
|
||||||
|
case WAIT_FAILED:
|
||||||
|
return (int)GetLastError();
|
||||||
|
case WAIT_ABANDONED:
|
||||||
|
return ERROR_ABANDONED_WAIT_0;
|
||||||
|
case WAIT_IO_COMPLETION:
|
||||||
|
return ERROR_USER_APC;
|
||||||
|
case WAIT_TIMEOUT:
|
||||||
|
return ERROR_TIMEOUT;
|
||||||
|
default:
|
||||||
|
return ERROR_UNHANDLED_ERROR;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#elif defined(__ANDROID_API__)
|
#elif defined(__ANDROID_API__)
|
||||||
|
|
||||||
#if __ANDROID_API__ < 24
|
#if __ANDROID_API__ < 24
|
||||||
|
|||||||
@@ -9,32 +9,15 @@ static std::unordered_map<unsigned, HANDLE> events;
|
|||||||
static HANDLE hBarrierSemaphore, hBarrierEvent;
|
static HANDLE hBarrierSemaphore, hBarrierEvent;
|
||||||
static HANDLE hProgressActiveEvent, hProgressPassiveEvent;
|
static HANDLE hProgressActiveEvent, hProgressPassiveEvent;
|
||||||
|
|
||||||
static int waitstatus2errcode(DWORD result) {
|
|
||||||
switch (result) {
|
|
||||||
case WAIT_OBJECT_0:
|
|
||||||
return MDBX_SUCCESS;
|
|
||||||
case WAIT_FAILED:
|
|
||||||
return GetLastError();
|
|
||||||
case WAIT_ABANDONED:
|
|
||||||
return ERROR_ABANDONED_WAIT_0;
|
|
||||||
case WAIT_IO_COMPLETION:
|
|
||||||
return ERROR_USER_APC;
|
|
||||||
case WAIT_TIMEOUT:
|
|
||||||
return ERROR_TIMEOUT;
|
|
||||||
default:
|
|
||||||
return ERROR_UNHANDLED_ERROR;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void osal_wait4barrier(void) {
|
void osal_wait4barrier(void) {
|
||||||
DWORD rc = WaitForSingleObject(hBarrierSemaphore, 0);
|
DWORD rc = WaitForSingleObject(hBarrierSemaphore, 0);
|
||||||
switch (rc) {
|
switch (rc) {
|
||||||
default:
|
default:
|
||||||
failure_perror("WaitForSingleObject(BarrierSemaphore)", waitstatus2errcode(rc));
|
failure_perror("WaitForSingleObject(BarrierSemaphore)", osal_waitstatus2errcode(rc));
|
||||||
case WAIT_OBJECT_0:
|
case WAIT_OBJECT_0:
|
||||||
rc = WaitForSingleObject(hBarrierEvent, INFINITE);
|
rc = WaitForSingleObject(hBarrierEvent, INFINITE);
|
||||||
if (rc != WAIT_OBJECT_0)
|
if (rc != WAIT_OBJECT_0)
|
||||||
failure_perror("WaitForSingleObject(BarrierEvent)", waitstatus2errcode(rc));
|
failure_perror("WaitForSingleObject(BarrierEvent)", osal_waitstatus2errcode(rc));
|
||||||
break;
|
break;
|
||||||
case WAIT_TIMEOUT:
|
case WAIT_TIMEOUT:
|
||||||
if (!SetEvent(hBarrierEvent))
|
if (!SetEvent(hBarrierEvent))
|
||||||
@@ -95,7 +78,7 @@ void osal_broadcast(unsigned id) {
|
|||||||
int osal_waitfor(unsigned id) {
|
int osal_waitfor(unsigned id) {
|
||||||
log_trace("osal_waitfor: event %u", id);
|
log_trace("osal_waitfor: event %u", id);
|
||||||
DWORD rc = WaitForSingleObject(events.at(id), INFINITE);
|
DWORD rc = WaitForSingleObject(events.at(id), INFINITE);
|
||||||
return waitstatus2errcode(rc);
|
return osal_waitstatus2errcode(rc);
|
||||||
}
|
}
|
||||||
|
|
||||||
int osal_delay(unsigned seconds) {
|
int osal_delay(unsigned seconds) {
|
||||||
@@ -397,7 +380,7 @@ int osal_actor_poll(mdbx_pid_t &pid, unsigned timeout) {
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
return waitstatus2errcode(rc);
|
return osal_waitstatus2errcode(rc);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -418,7 +401,7 @@ void osal_udelay(size_t us) {
|
|||||||
if (us > threshold_us && us > 1000) {
|
if (us > threshold_us && us > 1000) {
|
||||||
DWORD rc = SleepEx(unsigned(us / 1000), TRUE);
|
DWORD rc = SleepEx(unsigned(us / 1000), TRUE);
|
||||||
if (rc)
|
if (rc)
|
||||||
failure_perror("SleepEx()", waitstatus2errcode(rc));
|
failure_perror("SleepEx()", osal_waitstatus2errcode(rc));
|
||||||
us = 0;
|
us = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user