summaryrefslogtreecommitdiff
path: root/plugins/Dbx_mdb/src/mdbx/lck-windows.c
diff options
context:
space:
mode:
authorGeorge Hazan <ghazan@miranda.im>2017-10-08 23:19:54 +0300
committerGeorge Hazan <ghazan@miranda.im>2017-10-08 23:20:01 +0300
commit7aeec32a33811541b330a75352958267f22fa0a3 (patch)
tree397d770b0956933ea4ec4963943ea9827e2cf881 /plugins/Dbx_mdb/src/mdbx/lck-windows.c
parent73b775b4863e6498bc7794c9de27ef88529c835f (diff)
moving lmdb to mdbx engine
Diffstat (limited to 'plugins/Dbx_mdb/src/mdbx/lck-windows.c')
-rw-r--r--plugins/Dbx_mdb/src/mdbx/lck-windows.c449
1 files changed, 449 insertions, 0 deletions
diff --git a/plugins/Dbx_mdb/src/mdbx/lck-windows.c b/plugins/Dbx_mdb/src/mdbx/lck-windows.c
new file mode 100644
index 0000000000..66591db912
--- /dev/null
+++ b/plugins/Dbx_mdb/src/mdbx/lck-windows.c
@@ -0,0 +1,449 @@
+/*
+ * Copyright 2015-2017 Leonid Yuriev <leo@yuriev.ru>
+ * and other libmdbx authors: please see AUTHORS file.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+
+#include "./bits.h"
+
+/* PREAMBLE FOR WINDOWS:
+ *
+ * We are not concerned for performance here.
+ * If you are running Windows a performance could NOT be the goal.
+ * Otherwise please use Linux.
+ *
+ * Regards,
+ * LY
+ */
+
+/*----------------------------------------------------------------------------*/
+/* rthc */
+
+static CRITICAL_SECTION rthc_critical_section;
+
+static void NTAPI tls_callback(PVOID module, DWORD reason, PVOID reserved) {
+ (void)module;
+ (void)reserved;
+ switch (reason) {
+ case DLL_PROCESS_ATTACH:
+ InitializeCriticalSection(&rthc_critical_section);
+ break;
+ case DLL_PROCESS_DETACH:
+ DeleteCriticalSection(&rthc_critical_section);
+ break;
+
+ case DLL_THREAD_ATTACH:
+ break;
+ case DLL_THREAD_DETACH:
+ mdbx_rthc_cleanup();
+ break;
+ }
+}
+
+void mdbx_rthc_lock(void) { EnterCriticalSection(&rthc_critical_section); }
+
+void mdbx_rthc_unlock(void) { LeaveCriticalSection(&rthc_critical_section); }
+
+/* *INDENT-OFF* */
+/* clang-format off */
+#if defined(_MSC_VER)
+# pragma const_seg(push)
+# pragma data_seg(push)
+
+# ifdef _WIN64
+ /* kick a linker to create the TLS directory if not already done */
+# pragma comment(linker, "/INCLUDE:_tls_used")
+ /* Force some symbol references. */
+# pragma comment(linker, "/INCLUDE:mdbx_tls_callback")
+ /* specific const-segment for WIN64 */
+# pragma const_seg(".CRT$XLB")
+ const
+# else
+ /* kick a linker to create the TLS directory if not already done */
+# pragma comment(linker, "/INCLUDE:__tls_used")
+ /* Force some symbol references. */
+# pragma comment(linker, "/INCLUDE:_mdbx_tls_callback")
+ /* specific data-segment for WIN32 */
+# pragma data_seg(".CRT$XLB")
+# endif
+
+ PIMAGE_TLS_CALLBACK mdbx_tls_callback = tls_callback;
+# pragma data_seg(pop)
+# pragma const_seg(pop)
+
+#elif defined(__GNUC__)
+# ifdef _WIN64
+ const
+# endif
+ PIMAGE_TLS_CALLBACK mdbx_tls_callback __attribute__((section(".CRT$XLB"), used))
+ = tls_callback;
+#else
+# error FIXME
+#endif
+/* *INDENT-ON* */
+/* clang-format on */
+
+/*----------------------------------------------------------------------------*/
+
+#define LCK_SHARED 0
+#define LCK_EXCLUSIVE LOCKFILE_EXCLUSIVE_LOCK
+#define LCK_WAITFOR 0
+#define LCK_DONTWAIT LOCKFILE_FAIL_IMMEDIATELY
+
+static __inline BOOL flock(mdbx_filehandle_t fd, DWORD flags, uint64_t offset,
+ size_t bytes) {
+ OVERLAPPED ov;
+ ov.hEvent = 0;
+ ov.Offset = (DWORD)offset;
+ ov.OffsetHigh = HIGH_DWORD(offset);
+ return LockFileEx(fd, flags, 0, (DWORD)bytes, HIGH_DWORD(bytes), &ov);
+}
+
+static __inline BOOL funlock(mdbx_filehandle_t fd, uint64_t offset,
+ size_t bytes) {
+ return UnlockFile(fd, (DWORD)offset, HIGH_DWORD(offset), (DWORD)bytes,
+ HIGH_DWORD(bytes));
+}
+
+/*----------------------------------------------------------------------------*/
+/* global `write` lock for write-txt processing,
+ * exclusive locking both meta-pages) */
+
+#define LCK_MAXLEN (1u + (size_t)(MAXSSIZE_T))
+#define LCK_META_OFFSET 0
+#define LCK_META_LEN 0x10000u
+#define LCK_BODY_OFFSET LCK_META_LEN
+#define LCK_BODY_LEN (LCK_MAXLEN - LCK_BODY_OFFSET + 1u)
+#define LCK_META LCK_META_OFFSET, LCK_META_LEN
+#define LCK_BODY LCK_BODY_OFFSET, LCK_BODY_LEN
+#define LCK_WHOLE 0, LCK_MAXLEN
+
+int mdbx_txn_lock(MDBX_env *env) {
+ if (flock(env->me_fd, LCK_EXCLUSIVE | LCK_WAITFOR, LCK_BODY))
+ return MDBX_SUCCESS;
+ return GetLastError();
+}
+
+void mdbx_txn_unlock(MDBX_env *env) {
+ if (!funlock(env->me_fd, LCK_BODY))
+ mdbx_panic("%s failed: errcode %u", mdbx_func_, GetLastError());
+}
+
+/*----------------------------------------------------------------------------*/
+/* global `read` lock for readers registration,
+ * exclusive locking `mti_numreaders` (second) cacheline */
+
+#define LCK_LO_OFFSET 0
+#define LCK_LO_LEN offsetof(MDBX_lockinfo, mti_numreaders)
+#define LCK_UP_OFFSET LCK_LO_LEN
+#define LCK_UP_LEN (MDBX_LOCKINFO_WHOLE_SIZE - LCK_UP_OFFSET)
+#define LCK_LOWER LCK_LO_OFFSET, LCK_LO_LEN
+#define LCK_UPPER LCK_UP_OFFSET, LCK_UP_LEN
+
+int mdbx_rdt_lock(MDBX_env *env) {
+ if (env->me_lfd == INVALID_HANDLE_VALUE)
+ return MDBX_SUCCESS; /* readonly database in readonly filesystem */
+
+ /* transite from S-? (used) to S-E (locked), e.g. exclusive lock upper-part */
+ if (flock(env->me_lfd, LCK_EXCLUSIVE | LCK_WAITFOR, LCK_UPPER))
+ return MDBX_SUCCESS;
+ return GetLastError();
+}
+
+void mdbx_rdt_unlock(MDBX_env *env) {
+ if (env->me_lfd != INVALID_HANDLE_VALUE) {
+ /* transite from S-E (locked) to S-? (used), e.g. unlock upper-part */
+ if (!funlock(env->me_lfd, LCK_UPPER))
+ mdbx_panic("%s failed: errcode %u", mdbx_func_, GetLastError());
+ }
+}
+
+/*----------------------------------------------------------------------------*/
+/* global `initial` lock for lockfile initialization,
+ * exclusive/shared locking first cacheline */
+
+/* FIXME: locking schema/algo descritpion.
+ ?-? = free
+ S-? = used
+ E-? = exclusive-read
+ ?-S
+ ?-E = middle
+ S-S
+ S-E = locked
+ E-S
+ E-E = exclusive-write
+*/
+
+int mdbx_lck_init(MDBX_env *env) {
+ (void)env;
+ return MDBX_SUCCESS;
+}
+
+/* Seize state as 'exclusive-write' (E-E and returns MDBX_RESULT_TRUE)
+ * or as 'used' (S-? and returns MDBX_RESULT_FALSE), otherwise returns an error
+ */
+static int internal_seize_lck(HANDLE lfd) {
+ int rc;
+ assert(lfd != INVALID_HANDLE_VALUE);
+
+ /* 1) now on ?-? (free), get ?-E (middle) */
+ mdbx_jitter4testing(false);
+ if (!flock(lfd, LCK_EXCLUSIVE | LCK_WAITFOR, LCK_UPPER)) {
+ rc = GetLastError() /* 2) something went wrong, give up */;
+ mdbx_error("%s(%s) failed: errcode %u", mdbx_func_,
+ "?-?(free) >> ?-E(middle)", rc);
+ return rc;
+ }
+
+ /* 3) now on ?-E (middle), try E-E (exclusive-write) */
+ mdbx_jitter4testing(false);
+ if (flock(lfd, LCK_EXCLUSIVE | LCK_DONTWAIT, LCK_LOWER))
+ return MDBX_RESULT_TRUE /* 4) got E-E (exclusive-write), done */;
+
+ /* 5) still on ?-E (middle) */
+ rc = GetLastError();
+ mdbx_jitter4testing(false);
+ if (rc != ERROR_SHARING_VIOLATION && rc != ERROR_LOCK_VIOLATION) {
+ /* 6) something went wrong, give up */
+ if (!funlock(lfd, LCK_UPPER))
+ mdbx_panic("%s(%s) failed: errcode %u", mdbx_func_,
+ "?-E(middle) >> ?-?(free)", GetLastError());
+ return rc;
+ }
+
+ /* 7) still on ?-E (middle), try S-E (locked) */
+ mdbx_jitter4testing(false);
+ rc = flock(lfd, LCK_SHARED | LCK_DONTWAIT, LCK_LOWER) ? MDBX_RESULT_FALSE
+ : GetLastError();
+
+ mdbx_jitter4testing(false);
+ if (rc != MDBX_RESULT_FALSE)
+ mdbx_error("%s(%s) failed: errcode %u", mdbx_func_,
+ "?-E(middle) >> S-E(locked)", rc);
+
+ /* 8) now on S-E (locked) or still on ?-E (middle),
+ * transite to S-? (used) or ?-? (free) */
+ if (!funlock(lfd, LCK_UPPER))
+ mdbx_panic("%s(%s) failed: errcode %u", mdbx_func_,
+ "X-E(locked/middle) >> X-?(used/free)", GetLastError());
+
+ /* 9) now on S-? (used, DONE) or ?-? (free, FAILURE) */
+ return rc;
+}
+
+int mdbx_lck_seize(MDBX_env *env) {
+ int rc;
+
+ assert(env->me_fd != INVALID_HANDLE_VALUE);
+ if (env->me_lfd == INVALID_HANDLE_VALUE) {
+ /* LY: without-lck mode (e.g. on read-only filesystem) */
+ mdbx_jitter4testing(false);
+ if (!flock(env->me_fd, LCK_SHARED | LCK_DONTWAIT, LCK_WHOLE)) {
+ rc = GetLastError();
+ mdbx_error("%s(%s) failed: errcode %u", mdbx_func_, "without-lck", rc);
+ return rc;
+ }
+ return MDBX_RESULT_FALSE;
+ }
+
+ rc = internal_seize_lck(env->me_lfd);
+ mdbx_jitter4testing(false);
+ if (rc == MDBX_RESULT_TRUE && (env->me_flags & MDBX_RDONLY) == 0) {
+ /* Check that another process don't operates in without-lck mode.
+ * Doing such check by exclusive locking the body-part of db. Should be
+ * noted:
+ * - we need an exclusive lock for do so;
+ * - we can't lock meta-pages, otherwise other process could get an error
+ * while opening db in valid (non-conflict) mode. */
+ if (!flock(env->me_fd, LCK_EXCLUSIVE | LCK_DONTWAIT, LCK_BODY)) {
+ rc = GetLastError();
+ mdbx_error("%s(%s) failed: errcode %u", mdbx_func_,
+ "lock-against-without-lck", rc);
+ mdbx_jitter4testing(false);
+ mdbx_lck_destroy(env);
+ } else {
+ mdbx_jitter4testing(false);
+ if (!funlock(env->me_fd, LCK_BODY))
+ mdbx_panic("%s(%s) failed: errcode %u", mdbx_func_,
+ "unlock-against-without-lck", GetLastError());
+ }
+ }
+
+ return rc;
+}
+
+int mdbx_lck_downgrade(MDBX_env *env, bool complete) {
+ /* Transite from exclusive state (E-?) to used (S-?) */
+ assert(env->me_fd != INVALID_HANDLE_VALUE);
+ assert(env->me_lfd != INVALID_HANDLE_VALUE);
+
+ /* 1) must be at E-E (exclusive-write) */
+ if (!complete) {
+ /* transite from E-E to E_? (exclusive-read) */
+ if (!funlock(env->me_lfd, LCK_UPPER))
+ mdbx_panic("%s(%s) failed: errcode %u", mdbx_func_,
+ "E-E(exclusive-write) >> E-?(exclusive-read)", GetLastError());
+ return MDBX_SUCCESS /* 2) now at E-? (exclusive-read), done */;
+ }
+
+ /* 3) now at E-E (exclusive-write), transite to ?_E (middle) */
+ if (!funlock(env->me_lfd, LCK_LOWER))
+ mdbx_panic("%s(%s) failed: errcode %u", mdbx_func_,
+ "E-E(exclusive-write) >> ?-E(middle)", GetLastError());
+
+ /* 4) now at ?-E (middle), transite to S-E (locked) */
+ if (!flock(env->me_lfd, LCK_SHARED | LCK_DONTWAIT, LCK_LOWER)) {
+ int rc = GetLastError() /* 5) something went wrong, give up */;
+ mdbx_error("%s(%s) failed: errcode %u", mdbx_func_,
+ "?-E(middle) >> S-E(locked)", rc);
+ return rc;
+ }
+
+ /* 6) got S-E (locked), continue transition to S-? (used) */
+ if (!funlock(env->me_lfd, LCK_UPPER))
+ mdbx_panic("%s(%s) failed: errcode %u", mdbx_func_,
+ "S-E(locked) >> S-?(used)", GetLastError());
+
+ return MDBX_SUCCESS /* 7) now at S-? (used), done */;
+}
+
+int mdbx_lck_upgrade(MDBX_env *env) {
+ /* Transite from locked state (S-E) to exclusive-write (E-E) */
+ assert(env->me_fd != INVALID_HANDLE_VALUE);
+ assert(env->me_lfd != INVALID_HANDLE_VALUE);
+
+ /* 1) must be at S-E (locked), transite to ?_E (middle) */
+ if (!funlock(env->me_lfd, LCK_LOWER))
+ mdbx_panic("%s(%s) failed: errcode %u", mdbx_func_,
+ "S-E(locked) >> ?-E(middle)", GetLastError());
+
+ /* 3) now on ?-E (middle), try E-E (exclusive-write) */
+ mdbx_jitter4testing(false);
+ if (flock(env->me_lfd, LCK_EXCLUSIVE | LCK_DONTWAIT, LCK_LOWER))
+ return MDBX_RESULT_TRUE; /* 4) got E-E (exclusive-write), done */
+
+ /* 5) still on ?-E (middle) */
+ int rc = GetLastError();
+ mdbx_jitter4testing(false);
+ if (rc != ERROR_SHARING_VIOLATION && rc != ERROR_LOCK_VIOLATION) {
+ /* 6) something went wrong, report but continue */
+ mdbx_error("%s(%s) failed: errcode %u", mdbx_func_,
+ "?-E(middle) >> E-E(exclusive-write)", rc);
+ }
+
+ /* 7) still on ?-E (middle), try restore S-E (locked) */
+ mdbx_jitter4testing(false);
+ rc = flock(env->me_lfd, LCK_SHARED | LCK_DONTWAIT, LCK_LOWER)
+ ? MDBX_RESULT_FALSE
+ : GetLastError();
+
+ mdbx_jitter4testing(false);
+ if (rc != MDBX_RESULT_FALSE) {
+ mdbx_fatal("%s(%s) failed: errcode %u", mdbx_func_,
+ "?-E(middle) >> S-E(locked)", rc);
+ return rc;
+ }
+
+ /* 8) now on S-E (locked) */
+ return MDBX_RESULT_FALSE;
+}
+
+void mdbx_lck_destroy(MDBX_env *env) {
+ int rc;
+
+ if (env->me_lfd != INVALID_HANDLE_VALUE) {
+ /* double `unlock` for robustly remove overlapped shared/exclusive locks */
+ while (funlock(env->me_lfd, LCK_LOWER))
+ ;
+ rc = GetLastError();
+ assert(rc == ERROR_NOT_LOCKED);
+ (void)rc;
+ SetLastError(ERROR_SUCCESS);
+
+ while (funlock(env->me_lfd, LCK_UPPER))
+ ;
+ rc = GetLastError();
+ assert(rc == ERROR_NOT_LOCKED);
+ (void)rc;
+ SetLastError(ERROR_SUCCESS);
+ }
+
+ if (env->me_fd != INVALID_HANDLE_VALUE) {
+ /* explicitly unlock to avoid latency for other processes (windows kernel
+ * releases such locks via deferred queues) */
+ while (funlock(env->me_fd, LCK_BODY))
+ ;
+ rc = GetLastError();
+ assert(rc == ERROR_NOT_LOCKED);
+ (void)rc;
+ SetLastError(ERROR_SUCCESS);
+
+ while (funlock(env->me_fd, LCK_META))
+ ;
+ rc = GetLastError();
+ assert(rc == ERROR_NOT_LOCKED);
+ (void)rc;
+ SetLastError(ERROR_SUCCESS);
+
+ while (funlock(env->me_fd, LCK_WHOLE))
+ ;
+ rc = GetLastError();
+ assert(rc == ERROR_NOT_LOCKED);
+ (void)rc;
+ SetLastError(ERROR_SUCCESS);
+ }
+}
+
+/*----------------------------------------------------------------------------*/
+/* reader checking (by pid) */
+
+int mdbx_rpid_set(MDBX_env *env) {
+ (void)env;
+ return MDBX_SUCCESS;
+}
+
+int mdbx_rpid_clear(MDBX_env *env) {
+ (void)env;
+ return MDBX_SUCCESS;
+}
+
+/* Checks reader by pid.
+ *
+ * Returns:
+ * MDBX_RESULT_TRUE, if pid is live (unable to acquire lock)
+ * MDBX_RESULT_FALSE, if pid is dead (lock acquired)
+ * or otherwise the errcode. */
+int mdbx_rpid_check(MDBX_env *env, mdbx_pid_t pid) {
+ (void)env;
+ HANDLE hProcess = OpenProcess(PROCESS_QUERY_LIMITED_INFORMATION, FALSE, pid);
+ int rc;
+ if (hProcess) {
+ rc = WaitForSingleObject(hProcess, 0);
+ CloseHandle(hProcess);
+ } else {
+ rc = GetLastError();
+ }
+
+ switch (rc) {
+ case ERROR_INVALID_PARAMETER:
+ /* pid seem invalid */
+ return MDBX_RESULT_FALSE;
+ case WAIT_OBJECT_0:
+ /* process just exited */
+ return MDBX_RESULT_FALSE;
+ case WAIT_TIMEOUT:
+ /* pid running */
+ return MDBX_RESULT_TRUE;
+ default:
+ /* failure */
+ return rc;
+ }
+}