mirror of
https://github.com/isar/libmdbx.git
synced 2025-10-22 23:58:57 +08:00
mdbx-build: amalgamation.
Change-Id: Ic32de6ee119df2bc12136b882f4f7cabaa1314a9
This commit is contained in:
14723
src/elements/core.c
Normal file
14723
src/elements/core.c
Normal file
File diff suppressed because it is too large
Load Diff
455
src/elements/defs.h
Normal file
455
src/elements/defs.h
Normal file
@@ -0,0 +1,455 @@
|
||||
/*
|
||||
* Copyright 2015-2019 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>.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
/* *INDENT-OFF* */
|
||||
/* clang-format off */
|
||||
|
||||
#ifndef __GNUC_PREREQ
|
||||
# if defined(__GNUC__) && defined(__GNUC_MINOR__)
|
||||
# define __GNUC_PREREQ(maj, min) \
|
||||
((__GNUC__ << 16) + __GNUC_MINOR__ >= ((maj) << 16) + (min))
|
||||
# else
|
||||
# define __GNUC_PREREQ(maj, min) (0)
|
||||
# endif
|
||||
#endif /* __GNUC_PREREQ */
|
||||
|
||||
#ifndef __CLANG_PREREQ
|
||||
# ifdef __clang__
|
||||
# define __CLANG_PREREQ(maj,min) \
|
||||
((__clang_major__ << 16) + __clang_minor__ >= ((maj) << 16) + (min))
|
||||
# else
|
||||
# define __CLANG_PREREQ(maj,min) (0)
|
||||
# endif
|
||||
#endif /* __CLANG_PREREQ */
|
||||
|
||||
#ifndef __GLIBC_PREREQ
|
||||
# if defined(__GLIBC__) && defined(__GLIBC_MINOR__)
|
||||
# define __GLIBC_PREREQ(maj, min) \
|
||||
((__GLIBC__ << 16) + __GLIBC_MINOR__ >= ((maj) << 16) + (min))
|
||||
# else
|
||||
# define __GLIBC_PREREQ(maj, min) (0)
|
||||
# endif
|
||||
#endif /* __GLIBC_PREREQ */
|
||||
|
||||
#ifndef __has_attribute
|
||||
# define __has_attribute(x) (0)
|
||||
#endif
|
||||
|
||||
#ifndef __has_feature
|
||||
# define __has_feature(x) (0)
|
||||
#endif
|
||||
|
||||
#ifndef __has_extension
|
||||
# define __has_extension(x) (0)
|
||||
#endif
|
||||
|
||||
#ifndef __has_builtin
|
||||
# define __has_builtin(x) (0)
|
||||
#endif
|
||||
|
||||
#ifndef __has_warning
|
||||
# define __has_warning(x) (0)
|
||||
#endif
|
||||
|
||||
#ifndef __has_include
|
||||
# define __has_include(x) (0)
|
||||
#endif
|
||||
|
||||
#if __has_feature(thread_sanitizer)
|
||||
# define __SANITIZE_THREAD__ 1
|
||||
#endif
|
||||
|
||||
#if __has_feature(address_sanitizer)
|
||||
# define __SANITIZE_ADDRESS__ 1
|
||||
#endif
|
||||
|
||||
/*----------------------------------------------------------------------------*/
|
||||
|
||||
#ifndef __extern_C
|
||||
# ifdef __cplusplus
|
||||
# define __extern_C extern "C"
|
||||
# else
|
||||
# define __extern_C
|
||||
# endif
|
||||
#endif /* __extern_C */
|
||||
|
||||
#ifndef __cplusplus
|
||||
# ifndef bool
|
||||
# define bool _Bool
|
||||
# endif
|
||||
# ifndef true
|
||||
# define true (1)
|
||||
# endif
|
||||
# ifndef false
|
||||
# define false (0)
|
||||
# endif
|
||||
#endif
|
||||
|
||||
#if !defined(nullptr) && !defined(__cplusplus) || (__cplusplus < 201103L && !defined(_MSC_VER))
|
||||
# define nullptr NULL
|
||||
#endif
|
||||
|
||||
/*----------------------------------------------------------------------------*/
|
||||
|
||||
#ifndef __always_inline
|
||||
# if defined(__GNUC__) || __has_attribute(__always_inline__)
|
||||
# define __always_inline __inline __attribute__((__always_inline__))
|
||||
# elif defined(_MSC_VER)
|
||||
# define __always_inline __forceinline
|
||||
# else
|
||||
# define __always_inline
|
||||
# endif
|
||||
#endif /* __always_inline */
|
||||
|
||||
#ifndef __noinline
|
||||
# if defined(__GNUC__) || __has_attribute(__noinline__)
|
||||
# define __noinline __attribute__((__noinline__))
|
||||
# elif defined(_MSC_VER)
|
||||
# define __noinline __declspec(noinline)
|
||||
# elif defined(__SUNPRO_C) || defined(__sun) || defined(sun)
|
||||
# define __noinline inline
|
||||
# elif !defined(__INTEL_COMPILER)
|
||||
# define __noinline /* FIXME ? */
|
||||
# endif
|
||||
#endif /* __noinline */
|
||||
|
||||
#ifndef __must_check_result
|
||||
# if defined(__GNUC__) || __has_attribute(__warn_unused_result__)
|
||||
# define __must_check_result __attribute__((__warn_unused_result__))
|
||||
# else
|
||||
# define __must_check_result
|
||||
# endif
|
||||
#endif /* __must_check_result */
|
||||
|
||||
#ifndef __maybe_unused
|
||||
# if defined(__GNUC__) || __has_attribute(__unused__)
|
||||
# define __maybe_unused __attribute__((__unused__))
|
||||
# else
|
||||
# define __maybe_unused
|
||||
# endif
|
||||
#endif /* __maybe_unused */
|
||||
|
||||
#ifndef __deprecated
|
||||
# if defined(__GNUC__) || __has_attribute(__deprecated__)
|
||||
# define __deprecated __attribute__((__deprecated__))
|
||||
# elif defined(_MSC_VER)
|
||||
# define __deprecated __declspec(deprecated)
|
||||
# else
|
||||
# define __deprecated
|
||||
# endif
|
||||
#endif /* __deprecated */
|
||||
|
||||
#if !defined(__noop) && !defined(_MSC_VER)
|
||||
# ifdef __cplusplus
|
||||
static inline void __noop_consume_args() {}
|
||||
template <typename First, typename... Rest>
|
||||
static inline void
|
||||
__noop_consume_args(const First &first, const Rest &... rest) {
|
||||
(void) first; __noop_consume_args(rest...);
|
||||
}
|
||||
# define __noop(...) __noop_consume_args(__VA_ARGS__)
|
||||
# elif defined(__GNUC__) && (!defined(__STRICT_ANSI__) || !__STRICT_ANSI__)
|
||||
static __inline void __noop_consume_args(void* anchor, ...) {
|
||||
(void) anchor;
|
||||
}
|
||||
# define __noop(...) __noop_consume_args(0, ##__VA_ARGS__)
|
||||
# else
|
||||
# define __noop(...) do {} while(0)
|
||||
# endif
|
||||
#endif /* __noop */
|
||||
|
||||
#ifndef __fallthrough
|
||||
# if __GNUC_PREREQ(7, 0) || __has_attribute(__fallthrough__)
|
||||
# define __fallthrough __attribute__((__fallthrough__))
|
||||
# else
|
||||
# define __fallthrough __noop()
|
||||
# endif
|
||||
#endif /* __fallthrough */
|
||||
|
||||
#ifndef __unreachable
|
||||
# if __GNUC_PREREQ(4,5)
|
||||
# define __unreachable() __builtin_unreachable()
|
||||
# elif defined(_MSC_VER)
|
||||
# define __unreachable() __assume(0)
|
||||
# else
|
||||
# define __unreachable() __noop()
|
||||
# endif
|
||||
#endif /* __unreachable */
|
||||
|
||||
#ifndef __prefetch
|
||||
# if defined(__GNUC__) || defined(__clang__)
|
||||
# define __prefetch(ptr) __builtin_prefetch(ptr)
|
||||
# else
|
||||
# define __prefetch(ptr) __noop(ptr)
|
||||
# endif
|
||||
#endif /* __prefetch */
|
||||
|
||||
#ifndef __noreturn
|
||||
# if defined(__GNUC__) || __has_attribute(__noreturn__)
|
||||
# define __noreturn __attribute__((__noreturn__))
|
||||
# elif defined(_MSC_VER)
|
||||
# define __noreturn __declspec(noreturn)
|
||||
# else
|
||||
# define __noreturn
|
||||
# endif
|
||||
#endif /* __noreturn */
|
||||
|
||||
#ifndef __nothrow
|
||||
# if defined(__cplusplus)
|
||||
# if __cplusplus < 201703L
|
||||
# define __nothrow throw()
|
||||
# else
|
||||
# define __nothrow noexcept(true)
|
||||
# endif /* __cplusplus */
|
||||
# elif defined(__GNUC__) || __has_attribute(__nothrow__)
|
||||
# define __nothrow __attribute__((__nothrow__))
|
||||
# elif defined(_MSC_VER) && defined(__cplusplus)
|
||||
# define __nothrow __declspec(nothrow)
|
||||
# else
|
||||
# define __nothrow
|
||||
# endif
|
||||
#endif /* __nothrow */
|
||||
|
||||
#ifndef __pure_function
|
||||
/* Many functions have no effects except the return value and their
|
||||
* return value depends only on the parameters and/or global variables.
|
||||
* Such a function can be subject to common subexpression elimination
|
||||
* and loop optimization just as an arithmetic operator would be.
|
||||
* These functions should be declared with the attribute pure. */
|
||||
# if defined(__GNUC__) || __has_attribute(__pure__)
|
||||
# define __pure_function __attribute__((__pure__))
|
||||
# else
|
||||
# define __pure_function
|
||||
# endif
|
||||
#endif /* __pure_function */
|
||||
|
||||
#ifndef __const_function
|
||||
/* Many functions do not examine any values except their arguments,
|
||||
* and have no effects except the return value. Basically this is just
|
||||
* slightly more strict class than the PURE attribute, since function
|
||||
* is not allowed to read global memory.
|
||||
*
|
||||
* Note that a function that has pointer arguments and examines the
|
||||
* data pointed to must not be declared const. Likewise, a function
|
||||
* that calls a non-const function usually must not be const.
|
||||
* It does not make sense for a const function to return void. */
|
||||
# if defined(__GNUC__) || __has_attribute(__const__)
|
||||
# define __const_function __attribute__((__const__))
|
||||
# else
|
||||
# define __const_function
|
||||
# endif
|
||||
#endif /* __const_function */
|
||||
|
||||
#ifndef __hidden
|
||||
# if defined(__GNUC__) || __has_attribute(__visibility__)
|
||||
# define __hidden __attribute__((__visibility__("hidden")))
|
||||
# else
|
||||
# define __hidden
|
||||
# endif
|
||||
#endif /* __hidden */
|
||||
|
||||
#ifndef __optimize
|
||||
# if defined(__OPTIMIZE__)
|
||||
# if defined(__clang__) && !__has_attribute(__optimize__)
|
||||
# define __optimize(ops)
|
||||
# elif defined(__GNUC__) || __has_attribute(__optimize__)
|
||||
# define __optimize(ops) __attribute__((__optimize__(ops)))
|
||||
# else
|
||||
# define __optimize(ops)
|
||||
# endif
|
||||
# else
|
||||
# define __optimize(ops)
|
||||
# endif
|
||||
#endif /* __optimize */
|
||||
|
||||
#ifndef __hot
|
||||
# if defined(__OPTIMIZE__)
|
||||
# if defined(__e2k__)
|
||||
# define __hot __attribute__((__hot__)) __optimize(3)
|
||||
# elif defined(__clang__) && !__has_attribute(__hot_) \
|
||||
&& __has_attribute(__section__) && (defined(__linux__) || defined(__gnu_linux__))
|
||||
/* just put frequently used functions in separate section */
|
||||
# define __hot __attribute__((__section__("text.hot"))) __optimize("O3")
|
||||
# elif defined(__GNUC__) || __has_attribute(__hot__)
|
||||
# define __hot __attribute__((__hot__)) __optimize("O3")
|
||||
# else
|
||||
# define __hot __optimize("O3")
|
||||
# endif
|
||||
# else
|
||||
# define __hot
|
||||
# endif
|
||||
#endif /* __hot */
|
||||
|
||||
#ifndef __cold
|
||||
# if defined(__OPTIMIZE__)
|
||||
# if defined(__e2k__)
|
||||
# define __cold __attribute__((__cold__)) __optimize(1)
|
||||
# elif defined(__clang__) && !__has_attribute(cold) \
|
||||
&& __has_attribute(__section__) && (defined(__linux__) || defined(__gnu_linux__))
|
||||
/* just put infrequently used functions in separate section */
|
||||
# define __cold __attribute__((__section__("text.unlikely"))) __optimize("Os")
|
||||
# elif defined(__GNUC__) || __has_attribute(cold)
|
||||
# define __cold __attribute__((__cold__)) __optimize("Os")
|
||||
# else
|
||||
# define __cold __optimize("Os")
|
||||
# endif
|
||||
# else
|
||||
# define __cold
|
||||
# endif
|
||||
#endif /* __cold */
|
||||
|
||||
#ifndef __flatten
|
||||
# if defined(__OPTIMIZE__) && (defined(__GNUC__) || __has_attribute(__flatten__))
|
||||
# define __flatten __attribute__((__flatten__))
|
||||
# else
|
||||
# define __flatten
|
||||
# endif
|
||||
#endif /* __flatten */
|
||||
|
||||
#ifndef likely
|
||||
# if (defined(__GNUC__) || defined(__clang__)) && !defined(__COVERITY__)
|
||||
# define likely(cond) __builtin_expect(!!(cond), 1)
|
||||
# else
|
||||
# define likely(x) (x)
|
||||
# endif
|
||||
#endif /* likely */
|
||||
|
||||
#ifndef unlikely
|
||||
# if (defined(__GNUC__) || defined(__clang__)) && !defined(__COVERITY__)
|
||||
# define unlikely(cond) __builtin_expect(!!(cond), 0)
|
||||
# else
|
||||
# define unlikely(x) (x)
|
||||
# endif
|
||||
#endif /* unlikely */
|
||||
|
||||
/* Workaround for Coverity Scan */
|
||||
#if defined(__COVERITY__) && __GNUC_PREREQ(7, 0) && !defined(__cplusplus)
|
||||
typedef float _Float32;
|
||||
typedef double _Float32x;
|
||||
typedef double _Float64;
|
||||
typedef long double _Float64x;
|
||||
typedef float _Float128 __attribute__((__mode__(__TF__)));
|
||||
typedef __complex__ float __cfloat128 __attribute__ ((__mode__ (__TC__)));
|
||||
typedef _Complex float __cfloat128 __attribute__ ((__mode__ (__TC__)));
|
||||
#endif /* Workaround for Coverity Scan */
|
||||
|
||||
/* Wrapper around __func__, which is a C99 feature */
|
||||
#if defined(__STDC_VERSION__) && __STDC_VERSION__ >= 199901L
|
||||
# define mdbx_func_ __func__
|
||||
#elif (defined(__GNUC__) && __GNUC__ >= 2) || defined(__clang__) || defined(_MSC_VER)
|
||||
# define mdbx_func_ __FUNCTION__
|
||||
#else
|
||||
# define mdbx_func_ "<mdbx_unknown>"
|
||||
#endif
|
||||
|
||||
#if defined(__GNUC__) || __has_attribute(__format__)
|
||||
#define __printf_args(format_index, first_arg) \
|
||||
__attribute__((__format__(printf, format_index, first_arg)))
|
||||
#else
|
||||
#define __printf_args(format_index, first_arg)
|
||||
#endif
|
||||
|
||||
/*----------------------------------------------------------------------------*/
|
||||
|
||||
#if defined(USE_VALGRIND)
|
||||
# include <valgrind/memcheck.h>
|
||||
# ifndef VALGRIND_DISABLE_ADDR_ERROR_REPORTING_IN_RANGE
|
||||
/* LY: available since Valgrind 3.10 */
|
||||
# define VALGRIND_DISABLE_ADDR_ERROR_REPORTING_IN_RANGE(a,s)
|
||||
# define VALGRIND_ENABLE_ADDR_ERROR_REPORTING_IN_RANGE(a,s)
|
||||
# endif
|
||||
#elif !defined(RUNNING_ON_VALGRIND)
|
||||
# define VALGRIND_CREATE_MEMPOOL(h,r,z)
|
||||
# define VALGRIND_DESTROY_MEMPOOL(h)
|
||||
# define VALGRIND_MEMPOOL_TRIM(h,a,s)
|
||||
# define VALGRIND_MEMPOOL_ALLOC(h,a,s)
|
||||
# define VALGRIND_MEMPOOL_FREE(h,a)
|
||||
# define VALGRIND_MEMPOOL_CHANGE(h,a,b,s)
|
||||
# define VALGRIND_MAKE_MEM_NOACCESS(a,s)
|
||||
# define VALGRIND_MAKE_MEM_DEFINED(a,s)
|
||||
# define VALGRIND_MAKE_MEM_UNDEFINED(a,s)
|
||||
# define VALGRIND_DISABLE_ADDR_ERROR_REPORTING_IN_RANGE(a,s)
|
||||
# define VALGRIND_ENABLE_ADDR_ERROR_REPORTING_IN_RANGE(a,s)
|
||||
# define VALGRIND_CHECK_MEM_IS_ADDRESSABLE(a,s) (0)
|
||||
# define VALGRIND_CHECK_MEM_IS_DEFINED(a,s) (0)
|
||||
# define RUNNING_ON_VALGRIND (0)
|
||||
#endif /* USE_VALGRIND */
|
||||
|
||||
#ifdef __SANITIZE_ADDRESS__
|
||||
# include <sanitizer/asan_interface.h>
|
||||
#elif !defined(ASAN_POISON_MEMORY_REGION)
|
||||
# define ASAN_POISON_MEMORY_REGION(addr, size) \
|
||||
((void)(addr), (void)(size))
|
||||
# define ASAN_UNPOISON_MEMORY_REGION(addr, size) \
|
||||
((void)(addr), (void)(size))
|
||||
#endif /* __SANITIZE_ADDRESS__ */
|
||||
|
||||
/*----------------------------------------------------------------------------*/
|
||||
|
||||
#ifndef ARRAY_LENGTH
|
||||
# ifdef __cplusplus
|
||||
template <typename T, size_t N>
|
||||
char (&__ArraySizeHelper(T (&array)[N]))[N];
|
||||
# define ARRAY_LENGTH(array) (sizeof(::__ArraySizeHelper(array)))
|
||||
# else
|
||||
# define ARRAY_LENGTH(array) (sizeof(array) / sizeof(array[0]))
|
||||
# endif
|
||||
#endif /* ARRAY_LENGTH */
|
||||
|
||||
#ifndef ARRAY_END
|
||||
# define ARRAY_END(array) (&array[ARRAY_LENGTH(array)])
|
||||
#endif /* ARRAY_END */
|
||||
|
||||
#ifndef STRINGIFY
|
||||
# define STRINGIFY_HELPER(x) #x
|
||||
# define STRINGIFY(x) STRINGIFY_HELPER(x)
|
||||
#endif /* STRINGIFY */
|
||||
|
||||
#ifndef offsetof
|
||||
# define offsetof(type, member) __builtin_offsetof(type, member)
|
||||
#endif /* offsetof */
|
||||
|
||||
#ifndef container_of
|
||||
# define container_of(ptr, type, member) \
|
||||
((type *)((char *)(ptr) - offsetof(type, member)))
|
||||
#endif /* container_of */
|
||||
|
||||
#define MDBX_TETRAD(a, b, c, d) \
|
||||
((uint32_t)(a) << 24 | (uint32_t)(b) << 16 | (uint32_t)(c) << 8 | (d))
|
||||
|
||||
#define MDBX_STRING_TETRAD(str) MDBX_TETRAD(str[0], str[1], str[2], str[3])
|
||||
|
||||
#define FIXME "FIXME: " __FILE__ ", " STRINGIFY(__LINE__)
|
||||
|
||||
#ifndef STATIC_ASSERT_MSG
|
||||
# if (defined(__STDC_VERSION__) && __STDC_VERSION__ >= 201112L) \
|
||||
|| __has_feature(c_static_assert)
|
||||
# define STATIC_ASSERT_MSG(expr, msg) _Static_assert(expr, msg)
|
||||
# elif defined(static_assert)
|
||||
# define STATIC_ASSERT_MSG(expr, msg) static_assert(expr, msg)
|
||||
# elif defined(_MSC_VER)
|
||||
# include <crtdbg.h>
|
||||
# define STATIC_ASSERT_MSG(expr, msg) _STATIC_ASSERT(expr)
|
||||
# else
|
||||
# define STATIC_ASSERT_MSG(expr, msg) switch (0) {case 0:case (expr):;}
|
||||
# endif
|
||||
#endif /* STATIC_ASSERT */
|
||||
|
||||
#ifndef STATIC_ASSERT
|
||||
# define STATIC_ASSERT(expr) STATIC_ASSERT_MSG(expr, #expr)
|
||||
#endif
|
||||
|
||||
/* *INDENT-ON* */
|
||||
/* clang-format on */
|
1280
src/elements/internals.h
Normal file
1280
src/elements/internals.h
Normal file
File diff suppressed because it is too large
Load Diff
489
src/elements/lck-linux.c
Normal file
489
src/elements/lck-linux.c
Normal file
@@ -0,0 +1,489 @@
|
||||
/*
|
||||
* Copyright 2015-2019 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>.
|
||||
*/
|
||||
|
||||
#if !(defined(__linux__) || defined(__gnu_linux__))
|
||||
#error "This implementation of locking only supports Linux,\
|
||||
where is no interaction between the types of lock placed\
|
||||
by flock() and fcntl()."
|
||||
#endif
|
||||
|
||||
#include "./internals.h"
|
||||
#include <sys/utsname.h>
|
||||
|
||||
/* Some platforms define the EOWNERDEAD error code
|
||||
* even though they don't support Robust Mutexes.
|
||||
* Compile with -DMDBX_USE_ROBUST=0. */
|
||||
#ifndef MDBX_USE_ROBUST
|
||||
/* Howard Chu: Android currently lacks Robust Mutex support */
|
||||
#if defined(EOWNERDEAD) && \
|
||||
!defined(__ANDROID__) /* LY: glibc before 2.10 has a troubles \
|
||||
with Robust Mutex too. */ \
|
||||
&& (!defined(__GLIBC__) || __GLIBC_PREREQ(2, 10) || \
|
||||
_POSIX_C_SOURCE >= 200809L)
|
||||
#define MDBX_USE_ROBUST 1
|
||||
#else
|
||||
#define MDBX_USE_ROBUST 0
|
||||
#endif
|
||||
#endif /* MDBX_USE_ROBUST */
|
||||
|
||||
/*----------------------------------------------------------------------------*/
|
||||
/* global constructor/destructor */
|
||||
|
||||
#ifndef MDBX_ALLOY
|
||||
uint32_t mdbx_linux_kernel_version;
|
||||
#endif /* MDBX_ALLOY */
|
||||
|
||||
static __cold __attribute__((__constructor__)) void
|
||||
mdbx_global_constructor(void) {
|
||||
struct utsname buffer;
|
||||
if (uname(&buffer) == 0) {
|
||||
int i = 0;
|
||||
char *p = buffer.release;
|
||||
while (*p && i < 4) {
|
||||
if (*p >= '0' && *p <= '9') {
|
||||
long number = strtol(p, &p, 10);
|
||||
if (number > 0) {
|
||||
if (number > 255)
|
||||
number = 255;
|
||||
mdbx_linux_kernel_version += number << (24 - i * 8);
|
||||
}
|
||||
++i;
|
||||
} else {
|
||||
++p;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
mdbx_rthc_global_init();
|
||||
}
|
||||
|
||||
static __cold __attribute__((__destructor__)) void
|
||||
mdbx_global_destructor(void) {
|
||||
mdbx_rthc_global_dtor();
|
||||
}
|
||||
|
||||
/*----------------------------------------------------------------------------*/
|
||||
/* lck */
|
||||
|
||||
/* Описание реализации блокировок для Linux:
|
||||
*
|
||||
* lck-файл отображается в память, в нём организуется таблица читателей и
|
||||
* размещаются совместно используемые posix-мьютексы (futex). Посредством
|
||||
* этих мьютексов (см struct MDBX_lockinfo) реализуются:
|
||||
* - Блокировка таблицы читателей для регистрации,
|
||||
* т.е. функции mdbx_rdt_lock() и mdbx_rdt_unlock().
|
||||
* - Блокировка БД для пишущих транзакций,
|
||||
* т.е. функции mdbx_txn_lock() и mdbx_txn_unlock().
|
||||
*
|
||||
* Остальной функционал реализуется отдельно посредством файловых блокировок:
|
||||
* - Первоначальный захват БД в режиме exclusive/shared и последующий перевод
|
||||
* в операционный режим, функции mdbx_lck_seize() и mdbx_lck_downgrade().
|
||||
* - Проверка присутствие процессов-читателей,
|
||||
* т.е. функции mdbx_rpid_set(), mdbx_rpid_clear() и mdbx_rpid_check().
|
||||
*
|
||||
* Используется два вида файловых блокировок flock() и fcntl(F_SETLK),
|
||||
* как для lck-файла, так и для основного файла БД:
|
||||
* - Для контроля процессов-читателей используются однобайтовые
|
||||
* range-блокировки lck-файла посредством fcntl(F_SETLK). При этом
|
||||
* в качестве позиции используется pid процесса-читателя.
|
||||
* - Для первоначального захвата и shared/exclusive блокировок используется
|
||||
* комбинация flock() и fcntl(F_SETLK) блокировки одного байта lck-файла
|
||||
* в нулевой позиции (нулевая позиция не используется механизмом контроля
|
||||
* процессов-читателей, так как pid пользовательского процесса в Linux
|
||||
* всегда больше 0).
|
||||
* - Кроме этого, flock() блокировка основного файла БД используется при работе
|
||||
* в режимах без lck-файла, как в в read-only, так и в эксклюзивном.
|
||||
* - Блокировки flock() и fcntl(F_SETLK) в Linux работают независимо. Поэтому
|
||||
* их комбинирование позволяет предотвратить совместное использование БД
|
||||
* через NFS, что позволяет fcntl(F_SETLK), одновременно защитившись
|
||||
* от проблем не-аторманости flock() при переходе между эксклюзивным
|
||||
* и атомарным режимами блокировок.
|
||||
*/
|
||||
|
||||
static int op_setlk, op_setlkw, op_getlk;
|
||||
static void __cold choice_fcntl() {
|
||||
assert(!op_setlk && !op_setlkw && !op_getlk);
|
||||
#if defined(F_OFD_SETLK) && defined(F_OFD_SETLKW) && defined(F_OFD_GETLK)
|
||||
if (mdbx_linux_kernel_version >
|
||||
0x030f0000 /* OFD locks are available since 3.15, but engages here
|
||||
only for 3.16 and larer kernels (LTS) for reliability reasons */
|
||||
&& (mdbx_runtime_flags & MDBX_DBG_LEGACY_MULTIOPEN) == 0) {
|
||||
op_setlk = F_OFD_SETLK;
|
||||
op_setlkw = F_OFD_SETLKW;
|
||||
op_getlk = F_OFD_GETLK;
|
||||
return;
|
||||
}
|
||||
#endif /* OFD locks */
|
||||
op_setlk = F_SETLK;
|
||||
op_setlkw = F_SETLKW;
|
||||
op_getlk = F_GETLK;
|
||||
}
|
||||
|
||||
#ifndef OFF_T_MAX
|
||||
#define OFF_T_MAX \
|
||||
((sizeof(off_t) > 4 ? INT64_MAX : INT32_MAX) & ~(size_t)0xffff)
|
||||
#endif
|
||||
#define LCK_WHOLE OFF_T_MAX
|
||||
|
||||
static int mdbx_lck_op(mdbx_filehandle_t fd, int cmd, short lck, off_t offset,
|
||||
off_t len) {
|
||||
for (;;) {
|
||||
struct flock lock_op;
|
||||
memset(&lock_op, 0, sizeof(lock_op));
|
||||
lock_op.l_type = lck;
|
||||
lock_op.l_whence = SEEK_SET;
|
||||
lock_op.l_start = offset;
|
||||
lock_op.l_len = len;
|
||||
if (fcntl(fd, cmd, &lock_op) == 0) {
|
||||
if (cmd == op_getlk) {
|
||||
/* 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). */
|
||||
return (lock_op.l_type == F_UNLCK) ? MDBX_RESULT_FALSE
|
||||
: MDBX_RESULT_TRUE;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
int rc = errno;
|
||||
if (rc != EINTR || cmd == op_setlkw)
|
||||
return rc;
|
||||
}
|
||||
}
|
||||
|
||||
static __inline int mdbx_lck_exclusive(int lfd, bool fallback2shared) {
|
||||
assert(lfd != INVALID_HANDLE_VALUE);
|
||||
if (flock(lfd, LOCK_EX | LOCK_NB))
|
||||
return errno;
|
||||
int rc = mdbx_lck_op(lfd, op_setlk, F_WRLCK, 0, 1);
|
||||
if (rc != 0 && fallback2shared) {
|
||||
while (flock(lfd, LOCK_SH)) {
|
||||
int rc = errno;
|
||||
if (rc != EINTR)
|
||||
return rc;
|
||||
}
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
|
||||
static __inline int mdbx_lck_shared(int lfd) {
|
||||
assert(lfd != INVALID_HANDLE_VALUE);
|
||||
while (flock(lfd, LOCK_SH)) {
|
||||
int rc = errno;
|
||||
if (rc != EINTR)
|
||||
return rc;
|
||||
}
|
||||
return mdbx_lck_op(lfd, op_setlkw, F_RDLCK, 0, 1);
|
||||
}
|
||||
|
||||
MDBX_INTERNAL_FUNC int mdbx_lck_downgrade(MDBX_env *env, bool complete) {
|
||||
assert(env->me_lfd != INVALID_HANDLE_VALUE);
|
||||
return complete ? mdbx_lck_shared(env->me_lfd) : MDBX_SUCCESS;
|
||||
}
|
||||
|
||||
MDBX_INTERNAL_FUNC int mdbx_rpid_set(MDBX_env *env) {
|
||||
assert(env->me_lfd != INVALID_HANDLE_VALUE);
|
||||
assert(env->me_pid > 0);
|
||||
return mdbx_lck_op(env->me_lfd, op_setlk, F_WRLCK, env->me_pid, 1);
|
||||
}
|
||||
|
||||
MDBX_INTERNAL_FUNC int mdbx_rpid_clear(MDBX_env *env) {
|
||||
assert(env->me_lfd != INVALID_HANDLE_VALUE);
|
||||
assert(env->me_pid > 0);
|
||||
return mdbx_lck_op(env->me_lfd, op_setlkw, F_UNLCK, env->me_pid, 1);
|
||||
}
|
||||
|
||||
MDBX_INTERNAL_FUNC int mdbx_rpid_check(MDBX_env *env, mdbx_pid_t pid) {
|
||||
assert(env->me_lfd != INVALID_HANDLE_VALUE);
|
||||
assert(pid > 0);
|
||||
return mdbx_lck_op(env->me_lfd, op_getlk, F_WRLCK, pid, 1);
|
||||
}
|
||||
|
||||
/*---------------------------------------------------------------------------*/
|
||||
|
||||
static int mdbx_mutex_failed(MDBX_env *env, pthread_mutex_t *mutex,
|
||||
const int rc);
|
||||
|
||||
MDBX_INTERNAL_FUNC int __cold mdbx_lck_init(MDBX_env *env,
|
||||
int global_uniqueness_flag) {
|
||||
if (global_uniqueness_flag == MDBX_RESULT_FALSE)
|
||||
return MDBX_SUCCESS;
|
||||
|
||||
pthread_mutexattr_t ma;
|
||||
int rc = pthread_mutexattr_init(&ma);
|
||||
if (rc)
|
||||
return rc;
|
||||
|
||||
rc = pthread_mutexattr_setpshared(&ma, PTHREAD_PROCESS_SHARED);
|
||||
if (rc)
|
||||
goto bailout;
|
||||
|
||||
#if MDBX_USE_ROBUST
|
||||
#if defined(__GLIBC__) && !__GLIBC_PREREQ(2, 12) && \
|
||||
!defined(pthread_mutex_consistent) && _POSIX_C_SOURCE < 200809L
|
||||
rc = pthread_mutexattr_setrobust_np(&ma, PTHREAD_MUTEX_ROBUST_NP);
|
||||
#else
|
||||
rc = pthread_mutexattr_setrobust(&ma, PTHREAD_MUTEX_ROBUST);
|
||||
#endif
|
||||
if (rc)
|
||||
goto bailout;
|
||||
#endif /* MDBX_USE_ROBUST */
|
||||
|
||||
#if _POSIX_C_SOURCE >= 199506L && !defined(MDBX_SAFE4QEMU)
|
||||
rc = pthread_mutexattr_setprotocol(&ma, PTHREAD_PRIO_INHERIT);
|
||||
if (rc == ENOTSUP)
|
||||
rc = pthread_mutexattr_setprotocol(&ma, PTHREAD_PRIO_NONE);
|
||||
if (rc)
|
||||
goto bailout;
|
||||
#endif /* PTHREAD_PRIO_INHERIT */
|
||||
|
||||
rc = pthread_mutexattr_settype(&ma, PTHREAD_MUTEX_ERRORCHECK);
|
||||
if (rc)
|
||||
goto bailout;
|
||||
|
||||
rc = pthread_mutex_init(&env->me_lck->mti_rmutex, &ma);
|
||||
if (rc)
|
||||
goto bailout;
|
||||
rc = pthread_mutex_init(&env->me_lck->mti_wmutex, &ma);
|
||||
|
||||
bailout:
|
||||
pthread_mutexattr_destroy(&ma);
|
||||
return rc;
|
||||
}
|
||||
|
||||
MDBX_INTERNAL_FUNC int __cold mdbx_lck_destroy(MDBX_env *env,
|
||||
MDBX_env *inprocess_neighbor) {
|
||||
if (env->me_lfd != INVALID_HANDLE_VALUE && !inprocess_neighbor &&
|
||||
env->me_lck &&
|
||||
/* try get exclusive access */ mdbx_lck_exclusive(env->me_lfd, false) ==
|
||||
0) {
|
||||
mdbx_info("%s: got exclusive, drown mutexes", mdbx_func_);
|
||||
int rc = pthread_mutex_destroy(&env->me_lck->mti_rmutex);
|
||||
if (rc == 0)
|
||||
rc = pthread_mutex_destroy(&env->me_lck->mti_wmutex);
|
||||
assert(rc == 0);
|
||||
(void)rc;
|
||||
msync(env->me_lck, env->me_os_psize, MS_ASYNC);
|
||||
/* file locks would be released (by kernel)
|
||||
* while the me_lfd will be closed */
|
||||
}
|
||||
|
||||
if (op_setlk == F_SETLK) {
|
||||
/* File locks would be released (by kernel) while the file-descriptors
|
||||
* will be closed. But to avoid false-positive EDEADLK from the kernel,
|
||||
* locks should be released here explicitly with properly order. */
|
||||
|
||||
/* POSIX's fcntl() locks should be restored after file was closed.
|
||||
* FIXME: This code should be rethinked and retested, since it will
|
||||
* executed in really rare cases.
|
||||
*
|
||||
* On the other hand, seems more reasonable to disallow multi-open feature
|
||||
* by default, and describe it as "use at your own risk". Currently
|
||||
* multi-open required only for libfpta's unit-tests. */
|
||||
|
||||
int rc = MDBX_SUCCESS;
|
||||
/* close clk and restore locks */
|
||||
if (env->me_lfd != INVALID_HANDLE_VALUE) {
|
||||
(void)close(env->me_lfd);
|
||||
env->me_lfd = INVALID_HANDLE_VALUE;
|
||||
if (inprocess_neighbor) {
|
||||
/* restore file-locks */
|
||||
if (rc == MDBX_SUCCESS)
|
||||
rc = mdbx_lck_op(inprocess_neighbor->me_lfd, F_SETLKW, F_RDLCK, 0, 1);
|
||||
if (rc == MDBX_SUCCESS)
|
||||
rc = mdbx_rpid_set(inprocess_neighbor);
|
||||
}
|
||||
}
|
||||
|
||||
/* close dxb and restore lock */
|
||||
if (env->me_fd != INVALID_HANDLE_VALUE) {
|
||||
(void)close(env->me_fd);
|
||||
env->me_fd = INVALID_HANDLE_VALUE;
|
||||
if (inprocess_neighbor && rc == MDBX_SUCCESS) {
|
||||
/* restore file-lock */
|
||||
rc = mdbx_lck_op(
|
||||
inprocess_neighbor->me_fd, F_SETLKW,
|
||||
(inprocess_neighbor->me_flags & MDBX_RDONLY) ? F_RDLCK : F_WRLCK,
|
||||
(inprocess_neighbor->me_lfd == INVALID_HANDLE_VALUE)
|
||||
? 0
|
||||
: inprocess_neighbor->me_pid,
|
||||
(inprocess_neighbor->me_lfd == INVALID_HANDLE_VALUE) ? OFF_T_MAX
|
||||
: 1);
|
||||
}
|
||||
}
|
||||
|
||||
if (inprocess_neighbor && rc != MDBX_SUCCESS) {
|
||||
inprocess_neighbor->me_flags |= MDBX_FATAL_ERROR;
|
||||
return rc;
|
||||
}
|
||||
}
|
||||
|
||||
return MDBX_SUCCESS;
|
||||
}
|
||||
|
||||
static int mdbx_robust_lock(MDBX_env *env, pthread_mutex_t *mutex) {
|
||||
int rc = pthread_mutex_lock(mutex);
|
||||
if (unlikely(rc != 0))
|
||||
rc = mdbx_mutex_failed(env, mutex, rc);
|
||||
return rc;
|
||||
}
|
||||
|
||||
static int mdbx_robust_trylock(MDBX_env *env, pthread_mutex_t *mutex) {
|
||||
int rc = pthread_mutex_trylock(mutex);
|
||||
if (unlikely(rc != 0 && rc != EBUSY))
|
||||
rc = mdbx_mutex_failed(env, mutex, rc);
|
||||
return (rc != EBUSY) ? rc : MDBX_BUSY;
|
||||
}
|
||||
|
||||
static int mdbx_robust_unlock(MDBX_env *env, pthread_mutex_t *mutex) {
|
||||
int rc = pthread_mutex_unlock(mutex);
|
||||
if (unlikely(rc != 0))
|
||||
rc = mdbx_mutex_failed(env, mutex, rc);
|
||||
return rc;
|
||||
}
|
||||
|
||||
MDBX_INTERNAL_FUNC int mdbx_rdt_lock(MDBX_env *env) {
|
||||
mdbx_trace(">>");
|
||||
int rc = mdbx_robust_lock(env, &env->me_lck->mti_rmutex);
|
||||
mdbx_trace("<< rc %d", rc);
|
||||
return rc;
|
||||
}
|
||||
|
||||
MDBX_INTERNAL_FUNC void mdbx_rdt_unlock(MDBX_env *env) {
|
||||
mdbx_trace(">>");
|
||||
int rc = mdbx_robust_unlock(env, &env->me_lck->mti_rmutex);
|
||||
mdbx_trace("<< rc %d", rc);
|
||||
if (unlikely(MDBX_IS_ERROR(rc)))
|
||||
mdbx_panic("%s() failed: errcode %d\n", mdbx_func_, rc);
|
||||
}
|
||||
|
||||
int mdbx_txn_lock(MDBX_env *env, bool dontwait) {
|
||||
mdbx_trace(">>");
|
||||
int rc = dontwait ? mdbx_robust_trylock(env, env->me_wmutex)
|
||||
: mdbx_robust_lock(env, env->me_wmutex);
|
||||
mdbx_trace("<< rc %d", rc);
|
||||
return MDBX_IS_ERROR(rc) ? rc : MDBX_SUCCESS;
|
||||
}
|
||||
|
||||
void mdbx_txn_unlock(MDBX_env *env) {
|
||||
mdbx_trace(">>");
|
||||
int rc = mdbx_robust_unlock(env, env->me_wmutex);
|
||||
mdbx_trace("<< rc %d", rc);
|
||||
if (unlikely(MDBX_IS_ERROR(rc)))
|
||||
mdbx_panic("%s() failed: errcode %d\n", mdbx_func_, rc);
|
||||
}
|
||||
|
||||
static int __cold internal_seize_lck(int lfd) {
|
||||
assert(lfd != INVALID_HANDLE_VALUE);
|
||||
|
||||
/* try exclusive access */
|
||||
int rc = mdbx_lck_exclusive(lfd, false);
|
||||
if (rc == 0)
|
||||
/* got exclusive */
|
||||
return MDBX_RESULT_TRUE;
|
||||
if (rc == EAGAIN || rc == EACCES || rc == EBUSY || rc == EWOULDBLOCK) {
|
||||
/* get shared access */
|
||||
rc = mdbx_lck_shared(lfd);
|
||||
if (rc == 0) {
|
||||
/* got shared, try exclusive again */
|
||||
rc = mdbx_lck_exclusive(lfd, true);
|
||||
if (rc == 0)
|
||||
/* now got exclusive */
|
||||
return MDBX_RESULT_TRUE;
|
||||
if (rc == EAGAIN || rc == EACCES || rc == EBUSY || rc == EWOULDBLOCK)
|
||||
/* unable exclusive, but stay shared */
|
||||
return MDBX_RESULT_FALSE;
|
||||
}
|
||||
}
|
||||
assert(MDBX_IS_ERROR(rc));
|
||||
return rc;
|
||||
}
|
||||
|
||||
MDBX_INTERNAL_FUNC int __cold mdbx_lck_seize(MDBX_env *env) {
|
||||
assert(env->me_fd != INVALID_HANDLE_VALUE);
|
||||
if (unlikely(op_setlk == 0))
|
||||
choice_fcntl();
|
||||
|
||||
if (env->me_lfd == INVALID_HANDLE_VALUE) {
|
||||
/* LY: without-lck mode (e.g. exclusive or on read-only filesystem) */
|
||||
int rc = mdbx_lck_op(env->me_fd, op_setlk,
|
||||
(env->me_flags & MDBX_RDONLY) ? F_RDLCK : F_WRLCK, 0,
|
||||
LCK_WHOLE);
|
||||
if (rc != 0) {
|
||||
mdbx_error("%s(%s) failed: errcode %u", mdbx_func_, "without-lck", rc);
|
||||
return rc;
|
||||
}
|
||||
return MDBX_RESULT_TRUE;
|
||||
}
|
||||
|
||||
if ((env->me_flags & MDBX_RDONLY) == 0) {
|
||||
/* Check that another process don't operates in without-lck mode. */
|
||||
int rc = mdbx_lck_op(env->me_fd, op_setlk, F_WRLCK, env->me_pid, 1);
|
||||
if (rc != 0) {
|
||||
mdbx_error("%s(%s) failed: errcode %u", mdbx_func_,
|
||||
"lock-against-without-lck", rc);
|
||||
return rc;
|
||||
}
|
||||
}
|
||||
|
||||
return internal_seize_lck(env->me_lfd);
|
||||
}
|
||||
|
||||
static int __cold mdbx_mutex_failed(MDBX_env *env, pthread_mutex_t *mutex,
|
||||
const int err) {
|
||||
int rc = err;
|
||||
#if MDBX_USE_ROBUST
|
||||
if (err == EOWNERDEAD) {
|
||||
/* We own the mutex. Clean up after dead previous owner. */
|
||||
|
||||
int rlocked = (env->me_lck && mutex == &env->me_lck->mti_rmutex);
|
||||
rc = MDBX_SUCCESS;
|
||||
if (!rlocked) {
|
||||
if (unlikely(env->me_txn)) {
|
||||
/* env is hosed if the dead thread was ours */
|
||||
env->me_flags |= MDBX_FATAL_ERROR;
|
||||
env->me_txn = NULL;
|
||||
rc = MDBX_PANIC;
|
||||
}
|
||||
}
|
||||
mdbx_notice("%cmutex owner died, %s", (rlocked ? 'r' : 'w'),
|
||||
(rc ? "this process' env is hosed" : "recovering"));
|
||||
|
||||
int check_rc = mdbx_reader_check0(env, rlocked, NULL);
|
||||
check_rc = (check_rc == MDBX_SUCCESS) ? MDBX_RESULT_TRUE : check_rc;
|
||||
|
||||
#if defined(__GLIBC__) && !__GLIBC_PREREQ(2, 12) && \
|
||||
!defined(pthread_mutex_consistent) && _POSIX_C_SOURCE < 200809L
|
||||
int mreco_rc = pthread_mutex_consistent_np(mutex);
|
||||
#else
|
||||
int mreco_rc = pthread_mutex_consistent(mutex);
|
||||
#endif
|
||||
check_rc = (mreco_rc == 0) ? check_rc : mreco_rc;
|
||||
|
||||
if (unlikely(mreco_rc))
|
||||
mdbx_error("mutex recovery failed, %s", mdbx_strerror(mreco_rc));
|
||||
|
||||
rc = (rc == MDBX_SUCCESS) ? check_rc : rc;
|
||||
if (MDBX_IS_ERROR(rc))
|
||||
pthread_mutex_unlock(mutex);
|
||||
return rc;
|
||||
}
|
||||
#else
|
||||
(void)mutex;
|
||||
#endif /* MDBX_USE_ROBUST */
|
||||
|
||||
mdbx_error("mutex (un)lock failed, %s", mdbx_strerror(err));
|
||||
if (rc != EDEADLK)
|
||||
env->me_flags |= MDBX_FATAL_ERROR;
|
||||
return rc;
|
||||
}
|
436
src/elements/lck-posix.c
Normal file
436
src/elements/lck-posix.c
Normal file
@@ -0,0 +1,436 @@
|
||||
/*
|
||||
* Copyright 2015-2019 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 "./internals.h"
|
||||
|
||||
/* Some platforms define the EOWNERDEAD error code
|
||||
* even though they don't support Robust Mutexes.
|
||||
* Compile with -DMDBX_USE_ROBUST=0. */
|
||||
#ifndef MDBX_USE_ROBUST
|
||||
#if (defined(EOWNERDEAD) || _POSIX_C_SOURCE >= 200809L) && !defined(__APPLE__)
|
||||
#define MDBX_USE_ROBUST 1
|
||||
#else
|
||||
#define MDBX_USE_ROBUST 0
|
||||
#endif
|
||||
#endif /* MDBX_USE_ROBUST */
|
||||
|
||||
/*----------------------------------------------------------------------------*/
|
||||
/* rthc */
|
||||
|
||||
static __cold __attribute__((__constructor__)) void
|
||||
mdbx_global_constructor(void) {
|
||||
mdbx_rthc_global_init();
|
||||
}
|
||||
|
||||
static __cold __attribute__((__destructor__)) void
|
||||
mdbx_global_destructor(void) {
|
||||
mdbx_rthc_global_dtor();
|
||||
}
|
||||
|
||||
/*----------------------------------------------------------------------------*/
|
||||
/* lck */
|
||||
|
||||
/* Описание реализации блокировок для POSIX:
|
||||
*
|
||||
* lck-файл отображается в память, в нём организуется таблица читателей и
|
||||
* размещаются совместно используемые posix-мьютексы (futex). Посредством
|
||||
* этих мьютексов (см struct MDBX_lockinfo) реализуются:
|
||||
* - Блокировка таблицы читателей для регистрации,
|
||||
* т.е. функции mdbx_rdt_lock() и mdbx_rdt_unlock().
|
||||
* - Блокировка БД для пишущих транзакций,
|
||||
* т.е. функции mdbx_txn_lock() и mdbx_txn_unlock().
|
||||
*
|
||||
* Остальной функционал реализуется отдельно посредством файловых блокировок:
|
||||
* - Первоначальный захват БД в режиме exclusive/shared и последующий перевод
|
||||
* в операционный режим, функции mdbx_lck_seize() и mdbx_lck_downgrade().
|
||||
* - Проверка присутствие процессов-читателей,
|
||||
* т.е. функции mdbx_rpid_set(), mdbx_rpid_clear() и mdbx_rpid_check().
|
||||
*
|
||||
* Для блокировки файлов Используется только fcntl(F_SETLK), так как:
|
||||
* - lockf() оперирует только эксклюзивной блокировкой и требует
|
||||
* открытия файла в RW-режиме.
|
||||
* - flock() не гарантирует атомарности при смене блокировок
|
||||
* и оперирует только всем файлом целиком.
|
||||
* - Для контроля процессов-читателей используются однобайтовые
|
||||
* range-блокировки lck-файла посредством fcntl(F_SETLK). При этом
|
||||
* в качестве позиции используется pid процесса-читателя.
|
||||
* - Для первоначального захвата и shared/exclusive выполняется блокировка
|
||||
* основного файла БД и при успехе lck-файла.
|
||||
*/
|
||||
|
||||
#ifndef OFF_T_MAX
|
||||
#define OFF_T_MAX \
|
||||
((sizeof(off_t) > 4 ? INT64_MAX : INT32_MAX) & ~(size_t)0xffff)
|
||||
#endif
|
||||
#ifndef PID_T_MAX
|
||||
#define PID_T_MAX INT_MAX
|
||||
#endif
|
||||
|
||||
#if defined(F_OFD_SETLK) && defined(F_OFD_SETLKW) && defined(F_OFD_GETLK)
|
||||
#define OP_SETLK F_OFD_SETLK
|
||||
#define OP_SETLKW F_OFD_SETLKW
|
||||
#define OP_GETLK F_OFD_GETLK
|
||||
#else
|
||||
#define OP_SETLK F_SETLK
|
||||
#define OP_SETLKW F_SETLKW
|
||||
#define OP_GETLK F_GETLK
|
||||
#endif /* OFD locks */
|
||||
|
||||
static int mdbx_lck_op(mdbx_filehandle_t fd, int cmd, short lck, off_t offset,
|
||||
off_t len) {
|
||||
for (;;) {
|
||||
struct flock lock_op;
|
||||
memset(&lock_op, 0, sizeof(lock_op));
|
||||
lock_op.l_type = lck;
|
||||
lock_op.l_whence = SEEK_SET;
|
||||
lock_op.l_start = offset;
|
||||
lock_op.l_len = len;
|
||||
if (fcntl(fd, cmd, &lock_op) == 0) {
|
||||
if (cmd == OP_GETLK) {
|
||||
/* 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). */
|
||||
return (lock_op.l_type == F_UNLCK) ? MDBX_RESULT_FALSE
|
||||
: MDBX_RESULT_TRUE;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
int rc = errno;
|
||||
if (rc != EINTR || cmd == F_SETLKW)
|
||||
return rc;
|
||||
}
|
||||
}
|
||||
|
||||
MDBX_INTERNAL_FUNC int mdbx_rpid_set(MDBX_env *env) {
|
||||
assert(env->me_lfd != INVALID_HANDLE_VALUE);
|
||||
assert(env->me_pid > 0 && env->me_pid <= PID_T_MAX);
|
||||
return mdbx_lck_op(env->me_lfd, OP_SETLK, F_WRLCK, env->me_pid, 1);
|
||||
}
|
||||
|
||||
MDBX_INTERNAL_FUNC int mdbx_rpid_clear(MDBX_env *env) {
|
||||
assert(env->me_lfd != INVALID_HANDLE_VALUE);
|
||||
assert(env->me_pid > 0 && env->me_pid <= PID_T_MAX);
|
||||
return mdbx_lck_op(env->me_lfd, OP_SETLKW, F_UNLCK, env->me_pid, 1);
|
||||
}
|
||||
|
||||
MDBX_INTERNAL_FUNC int mdbx_rpid_check(MDBX_env *env, mdbx_pid_t pid) {
|
||||
assert(env->me_lfd != INVALID_HANDLE_VALUE);
|
||||
assert(pid > 0 && pid <= PID_T_MAX);
|
||||
assert(PID_T_MAX < OFF_T_MAX);
|
||||
return mdbx_lck_op(env->me_lfd, OP_GETLK, F_WRLCK, pid, 1);
|
||||
}
|
||||
|
||||
MDBX_INTERNAL_FUNC int __cold mdbx_lck_seize(MDBX_env *env) {
|
||||
assert(env->me_fd != INVALID_HANDLE_VALUE);
|
||||
assert(env->me_pid > 0 && env->me_pid <= PID_T_MAX);
|
||||
|
||||
if (env->me_lfd == INVALID_HANDLE_VALUE) {
|
||||
/* LY: without-lck mode (e.g. exclusive or on read-only filesystem) */
|
||||
int rc = mdbx_lck_op(env->me_fd, OP_SETLK,
|
||||
(env->me_flags & MDBX_RDONLY) ? F_RDLCK : F_WRLCK, 0,
|
||||
OFF_T_MAX);
|
||||
if (rc != 0) {
|
||||
mdbx_error("%s(%s) failed: errcode %u", mdbx_func_, "without-lck", rc);
|
||||
return rc;
|
||||
}
|
||||
return MDBX_RESULT_TRUE;
|
||||
}
|
||||
|
||||
/* try exclusive access */
|
||||
int rc = mdbx_lck_op(env->me_fd, OP_SETLK,
|
||||
(env->me_flags & MDBX_RDONLY) ? F_RDLCK : F_WRLCK, 0,
|
||||
OFF_T_MAX);
|
||||
if (rc == 0) {
|
||||
continue_exclusive:
|
||||
/* got dxb-exclusive, continue lck-exclusive */
|
||||
rc = mdbx_lck_op(env->me_lfd, OP_SETLKW, F_WRLCK, 0, OFF_T_MAX);
|
||||
if (rc == 0) {
|
||||
/* got both exclusive */
|
||||
return MDBX_RESULT_TRUE;
|
||||
}
|
||||
mdbx_error("%s(%s) failed: errcode %u", mdbx_func_,
|
||||
"lck-after-dxb-exclusive", rc);
|
||||
assert(MDBX_IS_ERROR(rc));
|
||||
goto bailout;
|
||||
}
|
||||
|
||||
if (rc == EAGAIN || rc == EACCES || rc == EBUSY || rc == EWOULDBLOCK) {
|
||||
rc = mdbx_lck_op(env->me_fd, OP_SETLKW,
|
||||
(env->me_flags & MDBX_RDONLY) ? F_RDLCK : F_WRLCK,
|
||||
env->me_pid, 1);
|
||||
if (rc == 0) {
|
||||
/* got dxb-shared, try again dxb-exclusive */
|
||||
rc = mdbx_lck_op(env->me_fd, OP_SETLK,
|
||||
(env->me_flags & MDBX_RDONLY) ? F_RDLCK : F_WRLCK, 0,
|
||||
OFF_T_MAX);
|
||||
if (rc == 0)
|
||||
goto continue_exclusive;
|
||||
|
||||
/* continue lck-shared */
|
||||
rc = mdbx_lck_op(env->me_lfd, OP_SETLKW, F_RDLCK, 0, 1);
|
||||
if (rc == 0) {
|
||||
/* got both dxb and lck shared lock */
|
||||
return MDBX_RESULT_FALSE;
|
||||
}
|
||||
mdbx_error("%s(%s) failed: errcode %u", mdbx_func_, "lck-shared", rc);
|
||||
} else {
|
||||
mdbx_error("%s(%s) failed: errcode %u", mdbx_func_, "dxb-shared", rc);
|
||||
}
|
||||
assert(MDBX_IS_ERROR(rc));
|
||||
}
|
||||
|
||||
bailout:
|
||||
(void)mdbx_lck_op(env->me_lfd, OP_SETLK, F_UNLCK, 0, OFF_T_MAX);
|
||||
(void)mdbx_lck_op(env->me_fd, OP_SETLK, F_UNLCK, 0, OFF_T_MAX);
|
||||
assert(MDBX_IS_ERROR(rc));
|
||||
return rc;
|
||||
}
|
||||
|
||||
int mdbx_lck_downgrade(MDBX_env *env, bool complete) {
|
||||
assert(env->me_lfd != INVALID_HANDLE_VALUE);
|
||||
int rc = mdbx_lck_op(env->me_lfd, OP_SETLK, F_UNLCK, 1, OFF_T_MAX - 1);
|
||||
if (rc == 0)
|
||||
rc = mdbx_lck_op(env->me_lfd, OP_SETLKW, F_RDLCK, 0, 1);
|
||||
if (unlikely(rc != 0)) {
|
||||
mdbx_error("%s(%s) failed: errcode %u", mdbx_func_, "lck", rc);
|
||||
goto bailout;
|
||||
}
|
||||
if (complete) {
|
||||
rc = mdbx_lck_op(env->me_fd, OP_SETLK,
|
||||
(env->me_flags & MDBX_RDONLY) ? F_RDLCK : F_WRLCK,
|
||||
env->me_pid, 1);
|
||||
if (unlikely(rc != 0)) {
|
||||
mdbx_error("%s(%s) failed: errcode %u", mdbx_func_, "dxb", rc);
|
||||
goto bailout;
|
||||
}
|
||||
}
|
||||
return MDBX_SUCCESS;
|
||||
|
||||
bailout:
|
||||
(void)mdbx_lck_op(env->me_lfd, OP_SETLK, F_UNLCK, 0, OFF_T_MAX);
|
||||
(void)mdbx_lck_op(env->me_fd, OP_SETLK, F_UNLCK, 0, OFF_T_MAX);
|
||||
assert(MDBX_IS_ERROR(rc));
|
||||
return rc;
|
||||
}
|
||||
|
||||
/*---------------------------------------------------------------------------*/
|
||||
|
||||
static int mdbx_mutex_failed(MDBX_env *env, pthread_mutex_t *mutex,
|
||||
const int rc);
|
||||
|
||||
MDBX_INTERNAL_FUNC int __cold mdbx_lck_init(MDBX_env *env,
|
||||
int global_uniqueness_flag) {
|
||||
if (global_uniqueness_flag == MDBX_RESULT_FALSE)
|
||||
return MDBX_SUCCESS;
|
||||
|
||||
pthread_mutexattr_t ma;
|
||||
int rc = pthread_mutexattr_init(&ma);
|
||||
if (rc)
|
||||
return rc;
|
||||
|
||||
rc = pthread_mutexattr_setpshared(&ma, PTHREAD_PROCESS_SHARED);
|
||||
if (rc)
|
||||
goto bailout;
|
||||
|
||||
#if MDBX_USE_ROBUST
|
||||
rc = pthread_mutexattr_setrobust(&ma, PTHREAD_MUTEX_ROBUST);
|
||||
if (rc)
|
||||
goto bailout;
|
||||
#endif /* MDBX_USE_ROBUST */
|
||||
|
||||
#if _POSIX_C_SOURCE >= 199506L && !defined(MDBX_SAFE4QEMU)
|
||||
rc = pthread_mutexattr_setprotocol(&ma, PTHREAD_PRIO_INHERIT);
|
||||
if (rc == ENOTSUP)
|
||||
rc = pthread_mutexattr_setprotocol(&ma, PTHREAD_PRIO_NONE);
|
||||
if (rc)
|
||||
goto bailout;
|
||||
#endif /* PTHREAD_PRIO_INHERIT */
|
||||
|
||||
rc = pthread_mutexattr_settype(&ma, PTHREAD_MUTEX_ERRORCHECK);
|
||||
if (rc)
|
||||
goto bailout;
|
||||
|
||||
rc = pthread_mutex_init(&env->me_lck->mti_rmutex, &ma);
|
||||
if (rc)
|
||||
goto bailout;
|
||||
rc = pthread_mutex_init(&env->me_lck->mti_wmutex, &ma);
|
||||
|
||||
bailout:
|
||||
pthread_mutexattr_destroy(&ma);
|
||||
return rc;
|
||||
}
|
||||
|
||||
MDBX_INTERNAL_FUNC int __cold mdbx_lck_destroy(MDBX_env *env,
|
||||
MDBX_env *inprocess_neighbor) {
|
||||
/* File locks would be released (by kernel) while the file-descriptors
|
||||
* will be closed. But to avoid false-positive EDEADLK from the kernel,
|
||||
* locks should be released here explicitly with properly order. */
|
||||
|
||||
if (env->me_lfd != INVALID_HANDLE_VALUE && !inprocess_neighbor &&
|
||||
env->me_lck &&
|
||||
/* try get exclusive access */
|
||||
mdbx_lck_op(env->me_fd, OP_SETLK,
|
||||
(env->me_flags & MDBX_RDONLY) ? F_RDLCK : F_WRLCK, 0,
|
||||
OFF_T_MAX) == 0 &&
|
||||
mdbx_lck_op(env->me_lfd, OP_SETLK, F_WRLCK, 0, OFF_T_MAX) == 0) {
|
||||
mdbx_info("%s: got exclusive, drown mutexes", mdbx_func_);
|
||||
int rc = pthread_mutex_destroy(&env->me_lck->mti_rmutex);
|
||||
if (rc == 0)
|
||||
rc = pthread_mutex_destroy(&env->me_lck->mti_wmutex);
|
||||
assert(rc == 0);
|
||||
(void)rc;
|
||||
msync(env->me_lck, env->me_os_psize, MS_ASYNC);
|
||||
}
|
||||
|
||||
/* POSIX's fcntl() locks should be restored after file was closed.
|
||||
* FIXME: This code should be rethinked and retested, since it will executed
|
||||
* in really rare cases. For instance, this code could wait a lot, if other
|
||||
* process get exclusive access immediately after the close().
|
||||
*
|
||||
* On the other hand, seems more reasonable to disallow multi-open feature
|
||||
* by default, and describe it as "use at your own risk". Currently
|
||||
* multi-open required only for libfpta's unit-tests. */
|
||||
|
||||
int rc = MDBX_SUCCESS;
|
||||
/* close clk and restore locks */
|
||||
if (env->me_lfd != INVALID_HANDLE_VALUE) {
|
||||
(void)close(env->me_lfd);
|
||||
env->me_lfd = INVALID_HANDLE_VALUE;
|
||||
if (inprocess_neighbor) {
|
||||
/* restore file-locks */
|
||||
if (rc == MDBX_SUCCESS)
|
||||
rc = mdbx_lck_op(inprocess_neighbor->me_lfd, OP_SETLKW, F_RDLCK, 0, 1);
|
||||
if (rc == MDBX_SUCCESS)
|
||||
rc = mdbx_rpid_set(inprocess_neighbor);
|
||||
}
|
||||
}
|
||||
|
||||
/* close dxb and restore lock */
|
||||
if (env->me_fd != INVALID_HANDLE_VALUE) {
|
||||
(void)close(env->me_fd);
|
||||
env->me_fd = INVALID_HANDLE_VALUE;
|
||||
if (inprocess_neighbor && rc == MDBX_SUCCESS) {
|
||||
/* restore file-lock */
|
||||
rc = mdbx_lck_op(
|
||||
inprocess_neighbor->me_fd, OP_SETLKW,
|
||||
(inprocess_neighbor->me_flags & MDBX_RDONLY) ? F_RDLCK : F_WRLCK,
|
||||
(inprocess_neighbor->me_lfd == INVALID_HANDLE_VALUE)
|
||||
? 0
|
||||
: inprocess_neighbor->me_pid,
|
||||
(inprocess_neighbor->me_lfd == INVALID_HANDLE_VALUE) ? OFF_T_MAX : 1);
|
||||
}
|
||||
}
|
||||
|
||||
if (inprocess_neighbor && rc != MDBX_SUCCESS) {
|
||||
inprocess_neighbor->me_flags |= MDBX_FATAL_ERROR;
|
||||
return rc;
|
||||
}
|
||||
return MDBX_SUCCESS;
|
||||
}
|
||||
|
||||
static int mdbx_robust_lock(MDBX_env *env, pthread_mutex_t *mutex) {
|
||||
int rc = pthread_mutex_lock(mutex);
|
||||
if (unlikely(rc != 0))
|
||||
rc = mdbx_mutex_failed(env, mutex, rc);
|
||||
return rc;
|
||||
}
|
||||
|
||||
static int mdbx_robust_trylock(MDBX_env *env, pthread_mutex_t *mutex) {
|
||||
int rc = pthread_mutex_trylock(mutex);
|
||||
if (unlikely(rc != 0 && rc != EBUSY))
|
||||
rc = mdbx_mutex_failed(env, mutex, rc);
|
||||
return (rc != EBUSY) ? rc : MDBX_BUSY;
|
||||
}
|
||||
|
||||
static int mdbx_robust_unlock(MDBX_env *env, pthread_mutex_t *mutex) {
|
||||
int rc = pthread_mutex_unlock(mutex);
|
||||
if (unlikely(rc != 0))
|
||||
rc = mdbx_mutex_failed(env, mutex, rc);
|
||||
return rc;
|
||||
}
|
||||
|
||||
MDBX_INTERNAL_FUNC int mdbx_rdt_lock(MDBX_env *env) {
|
||||
mdbx_trace(">>");
|
||||
int rc = mdbx_robust_lock(env, &env->me_lck->mti_rmutex);
|
||||
mdbx_trace("<< rc %d", rc);
|
||||
return rc;
|
||||
}
|
||||
|
||||
MDBX_INTERNAL_FUNC void mdbx_rdt_unlock(MDBX_env *env) {
|
||||
mdbx_trace(">>");
|
||||
int rc = mdbx_robust_unlock(env, &env->me_lck->mti_rmutex);
|
||||
mdbx_trace("<< rc %d", rc);
|
||||
if (unlikely(MDBX_IS_ERROR(rc)))
|
||||
mdbx_panic("%s() failed: errcode %d\n", mdbx_func_, rc);
|
||||
}
|
||||
|
||||
int mdbx_txn_lock(MDBX_env *env, bool dontwait) {
|
||||
mdbx_trace(">>");
|
||||
int rc = dontwait ? mdbx_robust_trylock(env, env->me_wmutex)
|
||||
: mdbx_robust_lock(env, env->me_wmutex);
|
||||
mdbx_trace("<< rc %d", rc);
|
||||
return MDBX_IS_ERROR(rc) ? rc : MDBX_SUCCESS;
|
||||
}
|
||||
|
||||
void mdbx_txn_unlock(MDBX_env *env) {
|
||||
mdbx_trace(">>");
|
||||
int rc = mdbx_robust_unlock(env, env->me_wmutex);
|
||||
mdbx_trace("<< rc %d", rc);
|
||||
if (unlikely(MDBX_IS_ERROR(rc)))
|
||||
mdbx_panic("%s() failed: errcode %d\n", mdbx_func_, rc);
|
||||
}
|
||||
|
||||
static int __cold mdbx_mutex_failed(MDBX_env *env, pthread_mutex_t *mutex,
|
||||
const int err) {
|
||||
int rc = err;
|
||||
#if MDBX_USE_ROBUST
|
||||
if (err == EOWNERDEAD) {
|
||||
/* We own the mutex. Clean up after dead previous owner. */
|
||||
|
||||
int rlocked = (env->me_lck && mutex == &env->me_lck->mti_rmutex);
|
||||
rc = MDBX_SUCCESS;
|
||||
if (!rlocked) {
|
||||
if (unlikely(env->me_txn)) {
|
||||
/* env is hosed if the dead thread was ours */
|
||||
env->me_flags |= MDBX_FATAL_ERROR;
|
||||
env->me_txn = NULL;
|
||||
rc = MDBX_PANIC;
|
||||
}
|
||||
}
|
||||
mdbx_notice("%cmutex owner died, %s", (rlocked ? 'r' : 'w'),
|
||||
(rc ? "this process' env is hosed" : "recovering"));
|
||||
|
||||
int check_rc = mdbx_reader_check0(env, rlocked, NULL);
|
||||
check_rc = (check_rc == MDBX_SUCCESS) ? MDBX_RESULT_TRUE : check_rc;
|
||||
|
||||
int mreco_rc = pthread_mutex_consistent(mutex);
|
||||
check_rc = (mreco_rc == 0) ? check_rc : mreco_rc;
|
||||
|
||||
if (unlikely(mreco_rc))
|
||||
mdbx_error("mutex recovery failed, %s", mdbx_strerror(mreco_rc));
|
||||
|
||||
rc = (rc == MDBX_SUCCESS) ? check_rc : rc;
|
||||
if (MDBX_IS_ERROR(rc))
|
||||
pthread_mutex_unlock(mutex);
|
||||
return rc;
|
||||
}
|
||||
#else
|
||||
(void)mutex;
|
||||
#endif /* MDBX_USE_ROBUST */
|
||||
|
||||
mdbx_error("mutex (un)lock failed, %s", mdbx_strerror(err));
|
||||
if (rc != EDEADLK)
|
||||
env->me_flags |= MDBX_FATAL_ERROR;
|
||||
return rc;
|
||||
}
|
731
src/elements/lck-windows.c
Normal file
731
src/elements/lck-windows.c
Normal file
@@ -0,0 +1,731 @@
|
||||
/*
|
||||
* Copyright 2015-2019 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 "./internals.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
|
||||
*/
|
||||
|
||||
static void mdbx_winnt_import(void);
|
||||
|
||||
#ifdef MDBX_BUILD_DLL
|
||||
/* DEBUG/CHECKED builds still require MSVC's CRT for runtime checks.
|
||||
*
|
||||
* Therefore we don't define dll's entry point for debug/checked builds by MSVC.
|
||||
* In this case MSVC's will automatically use DllMainCRTStartup() from CRT
|
||||
* library, which also automatically call DllMain() from our mdbx.dll
|
||||
*
|
||||
* On the other side, for RELEASE builds
|
||||
* we explicitly define DllMain() as the entry point and don't linking with
|
||||
* any CRT libraries (IgnoreAllDefaultLibraries = Yes). */
|
||||
#if !defined(_MSC_VER) || defined(NDEBUG)
|
||||
#pragma comment(linker, "/ENTRY:DllMain")
|
||||
#endif
|
||||
|
||||
BOOL APIENTRY DllMain(HANDLE module, DWORD reason, LPVOID reserved)
|
||||
#else
|
||||
#if !MDBX_CONFIG_MANUAL_TLS_CALLBACK
|
||||
static
|
||||
#endif /* !MDBX_CONFIG_MANUAL_TLS_CALLBACK */
|
||||
void NTAPI
|
||||
mdbx_dll_callback(PVOID module, DWORD reason, PVOID reserved)
|
||||
#endif /* MDBX_BUILD_DLL */
|
||||
{
|
||||
(void)reserved;
|
||||
switch (reason) {
|
||||
case DLL_PROCESS_ATTACH:
|
||||
mdbx_winnt_import();
|
||||
mdbx_rthc_global_init();
|
||||
break;
|
||||
case DLL_PROCESS_DETACH:
|
||||
mdbx_rthc_global_dtor();
|
||||
break;
|
||||
|
||||
case DLL_THREAD_ATTACH:
|
||||
break;
|
||||
case DLL_THREAD_DETACH:
|
||||
mdbx_rthc_thread_dtor(module);
|
||||
break;
|
||||
}
|
||||
#ifdef MDBX_BUILD_DLL
|
||||
return TRUE;
|
||||
#endif
|
||||
}
|
||||
|
||||
#if !defined(MDBX_BUILD_DLL) && !MDBX_CONFIG_MANUAL_TLS_CALLBACK
|
||||
/* *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_anchor")
|
||||
/* 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_anchor")
|
||||
/* specific data-segment for WIN32 */
|
||||
# pragma data_seg(".CRT$XLB")
|
||||
# endif
|
||||
|
||||
__declspec(allocate(".CRT$XLB")) PIMAGE_TLS_CALLBACK mdbx_tls_anchor = mdbx_dll_callback;
|
||||
# pragma data_seg(pop)
|
||||
# pragma const_seg(pop)
|
||||
|
||||
#elif defined(__GNUC__)
|
||||
# ifdef _WIN64
|
||||
const
|
||||
# endif
|
||||
PIMAGE_TLS_CALLBACK mdbx_tls_anchor __attribute__((__section__(".CRT$XLB"), used)) = mdbx_dll_callback;
|
||||
#else
|
||||
# error FIXME
|
||||
#endif
|
||||
/* *INDENT-ON* */
|
||||
/* clang-format on */
|
||||
#endif /* !defined(MDBX_BUILD_DLL) && !MDBX_CONFIG_MANUAL_TLS_CALLBACK */
|
||||
|
||||
/*----------------------------------------------------------------------------*/
|
||||
|
||||
#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)
|
||||
#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, bool dontwait) {
|
||||
if (dontwait) {
|
||||
if (!TryEnterCriticalSection(&env->me_windowsbug_lock))
|
||||
return MDBX_BUSY;
|
||||
} else {
|
||||
EnterCriticalSection(&env->me_windowsbug_lock);
|
||||
}
|
||||
|
||||
if ((env->me_flags & MDBX_EXCLUSIVE) ||
|
||||
flock(env->me_fd,
|
||||
dontwait ? (LCK_EXCLUSIVE | LCK_DONTWAIT)
|
||||
: (LCK_EXCLUSIVE | LCK_WAITFOR),
|
||||
LCK_BODY))
|
||||
return MDBX_SUCCESS;
|
||||
int rc = GetLastError();
|
||||
LeaveCriticalSection(&env->me_windowsbug_lock);
|
||||
return (!dontwait || rc != ERROR_LOCK_VIOLATION) ? rc : MDBX_BUSY;
|
||||
}
|
||||
|
||||
void mdbx_txn_unlock(MDBX_env *env) {
|
||||
int rc =
|
||||
(env->me_flags & MDBX_EXCLUSIVE) ? TRUE : funlock(env->me_fd, LCK_BODY);
|
||||
LeaveCriticalSection(&env->me_windowsbug_lock);
|
||||
if (!rc)
|
||||
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 (sizeof(MDBX_lockinfo) - LCK_UP_OFFSET)
|
||||
#define LCK_LOWER LCK_LO_OFFSET, LCK_LO_LEN
|
||||
#define LCK_UPPER LCK_UP_OFFSET, LCK_UP_LEN
|
||||
|
||||
MDBX_INTERNAL_FUNC int mdbx_rdt_lock(MDBX_env *env) {
|
||||
mdbx_srwlock_AcquireShared(&env->me_remap_guard);
|
||||
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 ((env->me_flags & MDBX_EXCLUSIVE) ||
|
||||
flock(env->me_lfd, LCK_EXCLUSIVE | LCK_WAITFOR, LCK_UPPER))
|
||||
return MDBX_SUCCESS;
|
||||
|
||||
int rc = GetLastError();
|
||||
mdbx_srwlock_ReleaseShared(&env->me_remap_guard);
|
||||
return rc;
|
||||
}
|
||||
|
||||
MDBX_INTERNAL_FUNC 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 ((env->me_flags & MDBX_EXCLUSIVE) == 0 &&
|
||||
!funlock(env->me_lfd, LCK_UPPER))
|
||||
mdbx_panic("%s failed: errcode %u", mdbx_func_, GetLastError());
|
||||
}
|
||||
mdbx_srwlock_ReleaseShared(&env->me_remap_guard);
|
||||
}
|
||||
|
||||
static int suspend_and_append(mdbx_handle_array_t **array,
|
||||
const DWORD ThreadId) {
|
||||
const unsigned limit = (*array)->limit;
|
||||
if ((*array)->count == limit) {
|
||||
void *ptr = mdbx_realloc(
|
||||
(limit > ARRAY_LENGTH((*array)->handles))
|
||||
? *array
|
||||
: /* don't free initial array on the stack */ NULL,
|
||||
sizeof(mdbx_handle_array_t) +
|
||||
sizeof(HANDLE) * (limit * 2 - ARRAY_LENGTH((*array)->handles)));
|
||||
if (!ptr)
|
||||
return MDBX_ENOMEM;
|
||||
if (limit == ARRAY_LENGTH((*array)->handles))
|
||||
memcpy(ptr, *array, sizeof(mdbx_handle_array_t));
|
||||
*array = (mdbx_handle_array_t *)ptr;
|
||||
(*array)->limit = limit * 2;
|
||||
}
|
||||
|
||||
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 err;
|
||||
}
|
||||
|
||||
(*array)->handles[(*array)->count++] = hThread;
|
||||
return MDBX_SUCCESS;
|
||||
}
|
||||
|
||||
MDBX_INTERNAL_FUNC int
|
||||
mdbx_suspend_threads_before_remap(MDBX_env *env, mdbx_handle_array_t **array) {
|
||||
const mdbx_pid_t CurrentTid = GetCurrentThreadId();
|
||||
int rc;
|
||||
if (env->me_lck) {
|
||||
/* Scan LCK for threads of the current process */
|
||||
const MDBX_reader *const begin = env->me_lck->mti_readers;
|
||||
const MDBX_reader *const end = begin + env->me_lck->mti_numreaders;
|
||||
const mdbx_tid_t WriteTxnOwner = env->me_txn0 ? env->me_txn0->mt_owner : 0;
|
||||
for (const MDBX_reader *reader = begin; reader < end; ++reader) {
|
||||
if (reader->mr_pid != env->me_pid || !reader->mr_tid) {
|
||||
skip_lck:
|
||||
continue;
|
||||
}
|
||||
if (reader->mr_tid == CurrentTid || reader->mr_tid == WriteTxnOwner)
|
||||
goto skip_lck;
|
||||
if (env->me_flags & MDBX_NOTLS) {
|
||||
/* Skip duplicates in no-tls mode */
|
||||
for (const MDBX_reader *scan = reader; --scan >= begin;)
|
||||
if (scan->mr_tid == reader->mr_tid)
|
||||
goto skip_lck;
|
||||
}
|
||||
|
||||
rc = suspend_and_append(array, reader->mr_tid);
|
||||
if (rc != MDBX_SUCCESS) {
|
||||
bailout_lck:
|
||||
(void)mdbx_resume_threads_after_remap(*array);
|
||||
return rc;
|
||||
}
|
||||
}
|
||||
if (WriteTxnOwner && WriteTxnOwner != CurrentTid) {
|
||||
rc = suspend_and_append(array, WriteTxnOwner);
|
||||
if (rc != MDBX_SUCCESS)
|
||||
goto bailout_lck;
|
||||
}
|
||||
} else {
|
||||
/* Without LCK (i.e. read-only mode).
|
||||
* Walk thougth a snapshot of all running threads */
|
||||
mdbx_assert(env,
|
||||
env->me_txn0 == NULL || (env->me_flags & MDBX_EXCLUSIVE) != 0);
|
||||
const HANDLE hSnapshot = CreateToolhelp32Snapshot(TH32CS_SNAPTHREAD, 0);
|
||||
if (hSnapshot == INVALID_HANDLE_VALUE)
|
||||
return GetLastError();
|
||||
|
||||
THREADENTRY32 entry;
|
||||
entry.dwSize = sizeof(THREADENTRY32);
|
||||
|
||||
if (!Thread32First(hSnapshot, &entry)) {
|
||||
rc = GetLastError();
|
||||
bailout_toolhelp:
|
||||
CloseHandle(hSnapshot);
|
||||
(void)mdbx_resume_threads_after_remap(*array);
|
||||
return rc;
|
||||
}
|
||||
|
||||
do {
|
||||
if (entry.th32OwnerProcessID != env->me_pid ||
|
||||
entry.th32ThreadID == CurrentTid)
|
||||
continue;
|
||||
|
||||
rc = suspend_and_append(array, entry.th32ThreadID);
|
||||
if (rc != MDBX_SUCCESS)
|
||||
goto bailout_toolhelp;
|
||||
|
||||
} while (Thread32Next(hSnapshot, &entry));
|
||||
|
||||
rc = GetLastError();
|
||||
if (rc != ERROR_NO_MORE_FILES)
|
||||
goto bailout_toolhelp;
|
||||
CloseHandle(hSnapshot);
|
||||
}
|
||||
|
||||
return MDBX_SUCCESS;
|
||||
}
|
||||
|
||||
MDBX_INTERNAL_FUNC int
|
||||
mdbx_resume_threads_after_remap(mdbx_handle_array_t *array) {
|
||||
int rc = MDBX_SUCCESS;
|
||||
for (unsigned i = 0; i < array->count; ++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;
|
||||
}
|
||||
|
||||
/*----------------------------------------------------------------------------*/
|
||||
/* 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
|
||||
*/
|
||||
|
||||
static void lck_unlock(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);
|
||||
}
|
||||
}
|
||||
|
||||
MDBX_INTERNAL_FUNC int mdbx_lck_init(MDBX_env *env,
|
||||
int global_uniqueness_flag) {
|
||||
(void)env;
|
||||
(void)global_uniqueness_flag;
|
||||
return MDBX_SUCCESS;
|
||||
}
|
||||
|
||||
MDBX_INTERNAL_FUNC int mdbx_lck_destroy(MDBX_env *env,
|
||||
MDBX_env *inprocess_neighbor) {
|
||||
(void)inprocess_neighbor;
|
||||
lck_unlock(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;
|
||||
}
|
||||
|
||||
MDBX_INTERNAL_FUNC int mdbx_lck_seize(MDBX_env *env) {
|
||||
int rc;
|
||||
|
||||
assert(env->me_fd != INVALID_HANDLE_VALUE);
|
||||
if (env->me_flags & MDBX_EXCLUSIVE)
|
||||
return MDBX_RESULT_TRUE /* nope since files were must be opened
|
||||
non-shareable */
|
||||
;
|
||||
|
||||
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);
|
||||
lck_unlock(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;
|
||||
}
|
||||
|
||||
MDBX_INTERNAL_FUNC 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);
|
||||
|
||||
if (env->me_flags & MDBX_EXCLUSIVE)
|
||||
return MDBX_SUCCESS /* nope since files were must be opened non-shareable */
|
||||
;
|
||||
|
||||
/* 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 */;
|
||||
}
|
||||
|
||||
/*----------------------------------------------------------------------------*/
|
||||
/* reader checking (by pid) */
|
||||
|
||||
MDBX_INTERNAL_FUNC int mdbx_rpid_set(MDBX_env *env) {
|
||||
(void)env;
|
||||
return MDBX_SUCCESS;
|
||||
}
|
||||
|
||||
MDBX_INTERNAL_FUNC 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. */
|
||||
MDBX_INTERNAL_FUNC int mdbx_rpid_check(MDBX_env *env, mdbx_pid_t pid) {
|
||||
(void)env;
|
||||
HANDLE hProcess = OpenProcess(SYNCHRONIZE, FALSE, pid);
|
||||
int rc;
|
||||
if (likely(hProcess)) {
|
||||
rc = WaitForSingleObject(hProcess, 0);
|
||||
if (unlikely(rc == WAIT_FAILED))
|
||||
rc = GetLastError();
|
||||
CloseHandle(hProcess);
|
||||
} else {
|
||||
rc = GetLastError();
|
||||
}
|
||||
|
||||
switch (rc) {
|
||||
case ERROR_INVALID_PARAMETER:
|
||||
/* pid seems 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;
|
||||
}
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
// 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;
|
||||
}
|
||||
|
||||
MDBX_srwlock_function mdbx_srwlock_Init, mdbx_srwlock_AcquireShared,
|
||||
mdbx_srwlock_ReleaseShared, mdbx_srwlock_AcquireExclusive,
|
||||
mdbx_srwlock_ReleaseExclusive;
|
||||
|
||||
/*----------------------------------------------------------------------------*/
|
||||
|
||||
static DWORD WINAPI stub_DiscardVirtualMemory(PVOID VirtualAddress,
|
||||
SIZE_T Size) {
|
||||
return VirtualAlloc(VirtualAddress, Size, MEM_RESET, PAGE_NOACCESS)
|
||||
? ERROR_SUCCESS
|
||||
: GetLastError();
|
||||
}
|
||||
|
||||
/*----------------------------------------------------------------------------*/
|
||||
#ifndef MDBX_ALLOY
|
||||
MDBX_GetFileInformationByHandleEx mdbx_GetFileInformationByHandleEx;
|
||||
MDBX_GetVolumeInformationByHandleW mdbx_GetVolumeInformationByHandleW;
|
||||
MDBX_GetFinalPathNameByHandleW mdbx_GetFinalPathNameByHandleW;
|
||||
MDBX_SetFileInformationByHandle mdbx_SetFileInformationByHandle;
|
||||
MDBX_PrefetchVirtualMemory mdbx_PrefetchVirtualMemory;
|
||||
MDBX_DiscardVirtualMemory mdbx_DiscardVirtualMemory;
|
||||
MDBX_NtFsControlFile mdbx_NtFsControlFile;
|
||||
#endif /* MDBX_ALLOY */
|
||||
|
||||
static void mdbx_winnt_import(void) {
|
||||
const HINSTANCE hKernel32dll = GetModuleHandleA("kernel32.dll");
|
||||
const MDBX_srwlock_function init =
|
||||
(MDBX_srwlock_function)GetProcAddress(hKernel32dll, "InitializeSRWLock");
|
||||
if (init != NULL) {
|
||||
mdbx_srwlock_Init = init;
|
||||
mdbx_srwlock_AcquireShared = (MDBX_srwlock_function)GetProcAddress(
|
||||
hKernel32dll, "AcquireSRWLockShared");
|
||||
mdbx_srwlock_ReleaseShared = (MDBX_srwlock_function)GetProcAddress(
|
||||
hKernel32dll, "ReleaseSRWLockShared");
|
||||
mdbx_srwlock_AcquireExclusive = (MDBX_srwlock_function)GetProcAddress(
|
||||
hKernel32dll, "AcquireSRWLockExclusive");
|
||||
mdbx_srwlock_ReleaseExclusive = (MDBX_srwlock_function)GetProcAddress(
|
||||
hKernel32dll, "ReleaseSRWLockExclusive");
|
||||
} else {
|
||||
mdbx_srwlock_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;
|
||||
}
|
||||
|
||||
#define GET_KERNEL32_PROC(ENTRY) \
|
||||
mdbx_##ENTRY = (MDBX_##ENTRY)GetProcAddress(hKernel32dll, #ENTRY)
|
||||
|
||||
GET_KERNEL32_PROC(GetFileInformationByHandleEx);
|
||||
GET_KERNEL32_PROC(GetVolumeInformationByHandleW);
|
||||
GET_KERNEL32_PROC(GetFinalPathNameByHandleW);
|
||||
GET_KERNEL32_PROC(SetFileInformationByHandle);
|
||||
GET_KERNEL32_PROC(PrefetchVirtualMemory);
|
||||
GET_KERNEL32_PROC(DiscardVirtualMemory);
|
||||
if (!mdbx_DiscardVirtualMemory)
|
||||
mdbx_DiscardVirtualMemory = stub_DiscardVirtualMemory;
|
||||
|
||||
const HINSTANCE hNtdll = GetModuleHandleA("ntdll.dll");
|
||||
mdbx_NtFsControlFile =
|
||||
(MDBX_NtFsControlFile)GetProcAddress(hNtdll, "NtFsControlFile");
|
||||
}
|
1244
src/elements/ntdll.def
Normal file
1244
src/elements/ntdll.def
Normal file
File diff suppressed because it is too large
Load Diff
1337
src/elements/osal.c
Normal file
1337
src/elements/osal.c
Normal file
File diff suppressed because it is too large
Load Diff
947
src/elements/osal.h
Normal file
947
src/elements/osal.h
Normal file
@@ -0,0 +1,947 @@
|
||||
/* https://en.wikipedia.org/wiki/Operating_system_abstraction_layer */
|
||||
|
||||
/*
|
||||
* Copyright 2015-2019 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>.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
/*----------------------------------------------------------------------------*/
|
||||
/* Microsoft compiler generates a lot of warning for self includes... */
|
||||
|
||||
#ifdef _MSC_VER
|
||||
#pragma warning(push, 1)
|
||||
#pragma warning(disable : 4548) /* expression before comma has no effect; \
|
||||
expected expression with side - effect */
|
||||
#pragma warning(disable : 4530) /* C++ exception handler used, but unwind \
|
||||
* semantics are not enabled. Specify /EHsc */
|
||||
#pragma warning(disable : 4577) /* 'noexcept' used with no exception handling \
|
||||
* mode specified; termination on exception is \
|
||||
* not guaranteed. Specify /EHsc */
|
||||
#endif /* _MSC_VER (warnings) */
|
||||
|
||||
#if defined(_WIN32) || defined(_WIN64)
|
||||
#if !defined(_CRT_SECURE_NO_WARNINGS)
|
||||
#define _CRT_SECURE_NO_WARNINGS
|
||||
#endif
|
||||
#if !defined(_NO_CRT_STDIO_INLINE) && defined(MDBX_BUILD_DLL)
|
||||
#define _NO_CRT_STDIO_INLINE
|
||||
#endif
|
||||
#endif /* Windows */
|
||||
|
||||
/*----------------------------------------------------------------------------*/
|
||||
/* C99 includes */
|
||||
#include <inttypes.h>
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include <assert.h>
|
||||
#include <fcntl.h>
|
||||
#include <limits.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <time.h>
|
||||
|
||||
/* C11 stdalign.h */
|
||||
#if __has_include(<stdalign.h>)
|
||||
#include <stdalign.h>
|
||||
#elif defined(__STDC_VERSION__) && __STDC_VERSION__ >= 201112L
|
||||
#define alignas(N) _Alignas(N)
|
||||
#elif defined(_MSC_VER)
|
||||
#define alignas(N) __declspec(align(N))
|
||||
#elif __has_attribute(__aligned__) || defined(__GNUC__)
|
||||
#define alignas(N) __attribute__((__aligned__(N)))
|
||||
#else
|
||||
#error "FIXME: Required _alignas() or equivalent."
|
||||
#endif
|
||||
|
||||
/*----------------------------------------------------------------------------*/
|
||||
/* Systems includes */
|
||||
|
||||
#if defined(__FreeBSD__) || defined(__NetBSD__) || defined(__OpenBSD__) || \
|
||||
defined(__BSD__) || defined(__NETBSD__) || defined(__bsdi__) || \
|
||||
defined(__DragonFly__) || defined(__APPLE__) || defined(__MACH__)
|
||||
#include <sys/cdefs.h>
|
||||
#else
|
||||
#include <malloc.h>
|
||||
#ifndef _POSIX_C_SOURCE
|
||||
#ifdef _POSIX_SOURCE
|
||||
#define _POSIX_C_SOURCE 1
|
||||
#else
|
||||
#define _POSIX_C_SOURCE 0
|
||||
#endif
|
||||
#endif
|
||||
#endif /* !xBSD */
|
||||
|
||||
#ifndef _XOPEN_SOURCE
|
||||
#define _XOPEN_SOURCE 0
|
||||
#endif
|
||||
|
||||
#if defined(_WIN32) || defined(_WIN64)
|
||||
#define WIN32_LEAN_AND_MEAN
|
||||
#include <tlhelp32.h>
|
||||
#include <windows.h>
|
||||
#include <winnt.h>
|
||||
#include <winternl.h>
|
||||
#define HAVE_SYS_STAT_H
|
||||
#define HAVE_SYS_TYPES_H
|
||||
typedef HANDLE mdbx_thread_t;
|
||||
typedef unsigned mdbx_thread_key_t;
|
||||
#define MDBX_OSAL_SECTION HANDLE
|
||||
#define MAP_FAILED NULL
|
||||
#define HIGH_DWORD(v) ((DWORD)((sizeof(v) > 4) ? ((uint64_t)(v) >> 32) : 0))
|
||||
#define THREAD_CALL WINAPI
|
||||
#define THREAD_RESULT DWORD
|
||||
typedef struct {
|
||||
HANDLE mutex;
|
||||
HANDLE event;
|
||||
} mdbx_condmutex_t;
|
||||
typedef CRITICAL_SECTION mdbx_fastmutex_t;
|
||||
|
||||
#ifdef MDBX_AVOID_CRT
|
||||
#ifndef mdbx_malloc
|
||||
static inline void *mdbx_malloc(size_t bytes) {
|
||||
return LocalAlloc(LMEM_FIXED, bytes);
|
||||
}
|
||||
#endif /* mdbx_malloc */
|
||||
|
||||
#ifndef mdbx_calloc
|
||||
static inline void *mdbx_calloc(size_t nelem, size_t size) {
|
||||
return LocalAlloc(LMEM_FIXED | LMEM_ZEROINIT, nelem * size);
|
||||
}
|
||||
#endif /* mdbx_calloc */
|
||||
|
||||
#ifndef mdbx_realloc
|
||||
static inline void *mdbx_realloc(void *ptr, size_t bytes) {
|
||||
return LocalReAlloc(ptr, bytes, LMEM_MOVEABLE);
|
||||
}
|
||||
#endif /* mdbx_realloc */
|
||||
|
||||
#ifndef mdbx_free
|
||||
#define mdbx_free LocalFree
|
||||
#endif /* mdbx_free */
|
||||
#else
|
||||
#define mdbx_malloc malloc
|
||||
#define mdbx_calloc calloc
|
||||
#define mdbx_realloc realloc
|
||||
#define mdbx_free free
|
||||
#define mdbx_strdup _strdup
|
||||
#endif /* MDBX_AVOID_CRT */
|
||||
|
||||
#ifndef snprintf
|
||||
#define snprintf _snprintf /* ntdll */
|
||||
#endif
|
||||
|
||||
#ifndef vsnprintf
|
||||
#define vsnprintf _vsnprintf /* ntdll */
|
||||
#endif
|
||||
|
||||
#else /*----------------------------------------------------------------------*/
|
||||
|
||||
#include <pthread.h>
|
||||
#include <signal.h>
|
||||
#include <sys/file.h>
|
||||
#include <sys/mman.h>
|
||||
#include <sys/param.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/uio.h>
|
||||
#include <unistd.h>
|
||||
typedef pthread_t mdbx_thread_t;
|
||||
typedef pthread_key_t mdbx_thread_key_t;
|
||||
#define INVALID_HANDLE_VALUE (-1)
|
||||
#define THREAD_CALL
|
||||
#define THREAD_RESULT void *
|
||||
typedef struct {
|
||||
pthread_mutex_t mutex;
|
||||
pthread_cond_t cond;
|
||||
} mdbx_condmutex_t;
|
||||
typedef pthread_mutex_t mdbx_fastmutex_t;
|
||||
#define mdbx_malloc malloc
|
||||
#define mdbx_calloc calloc
|
||||
#define mdbx_realloc realloc
|
||||
#define mdbx_free free
|
||||
#define mdbx_strdup strdup
|
||||
#endif /* Platform */
|
||||
|
||||
/* *INDENT-OFF* */
|
||||
/* clang-format off */
|
||||
#if defined(HAVE_SYS_STAT_H) || __has_include(<sys/stat.h>)
|
||||
#include <sys/stat.h>
|
||||
#endif
|
||||
#if defined(HAVE_SYS_TYPES_H) || __has_include(<sys/types.h>)
|
||||
#include <sys/types.h>
|
||||
#endif
|
||||
#if defined(HAVE_SYS_FILE_H) || __has_include(<sys/file.h>)
|
||||
#include <sys/file.h>
|
||||
#endif
|
||||
/* *INDENT-ON* */
|
||||
/* clang-format on */
|
||||
|
||||
#ifndef SSIZE_MAX
|
||||
#define SSIZE_MAX INTPTR_MAX
|
||||
#endif
|
||||
|
||||
#if !defined(MADV_DODUMP) && defined(MADV_CORE)
|
||||
#define MADV_DODUMP MADV_CORE
|
||||
#endif /* MADV_CORE -> MADV_DODUMP */
|
||||
|
||||
#if !defined(MADV_DONTDUMP) && defined(MADV_NOCORE)
|
||||
#define MADV_DONTDUMP MADV_NOCORE
|
||||
#endif /* MADV_NOCORE -> MADV_DONTDUMP */
|
||||
|
||||
#ifndef MADV_REMOVE_OR_FREE_OR_DONTNEED
|
||||
#ifdef MADV_REMOVE
|
||||
#define MADV_REMOVE_OR_FREE_OR_DONTNEED MADV_REMOVE
|
||||
#elif defined(MADV_FREE)
|
||||
#define MADV_REMOVE_OR_FREE_OR_DONTNEED MADV_FREE
|
||||
#elif defined(MADV_DONTNEED)
|
||||
#define MADV_REMOVE_OR_FREE_OR_DONTNEED MADV_DONTNEED
|
||||
#endif
|
||||
#endif /* MADV_REMOVE_OR_FREE_OR_DONTNEED */
|
||||
|
||||
#if defined(i386) || defined(__386) || defined(__i386) || defined(__i386__) || \
|
||||
defined(i486) || defined(__i486) || defined(__i486__) || \
|
||||
defined(i586) | defined(__i586) || defined(__i586__) || defined(i686) || \
|
||||
defined(__i686) || defined(__i686__) || defined(_M_IX86) || \
|
||||
defined(_X86_) || defined(__THW_INTEL__) || defined(__I86__) || \
|
||||
defined(__INTEL__) || defined(__x86_64) || defined(__x86_64__) || \
|
||||
defined(__amd64__) || defined(__amd64) || defined(_M_X64) || \
|
||||
defined(_M_AMD64) || defined(__IA32__) || defined(__INTEL__)
|
||||
#ifndef __ia32__
|
||||
/* LY: define neutral __ia32__ for x86 and x86-64 archs */
|
||||
#define __ia32__ 1
|
||||
#endif /* __ia32__ */
|
||||
#if !defined(__amd64__) && (defined(__x86_64) || defined(__x86_64__) || \
|
||||
defined(__amd64) || defined(_M_X64))
|
||||
/* LY: define trusty __amd64__ for all AMD64/x86-64 arch */
|
||||
#define __amd64__ 1
|
||||
#endif /* __amd64__ */
|
||||
#endif /* all x86 */
|
||||
|
||||
#if !defined(UNALIGNED_OK)
|
||||
#if (defined(__ia32__) || defined(__e2k__) || \
|
||||
defined(__ARM_FEATURE_UNALIGNED)) && \
|
||||
!defined(__ALIGNED__)
|
||||
#define UNALIGNED_OK 1
|
||||
#else
|
||||
#define UNALIGNED_OK 0
|
||||
#endif
|
||||
#endif /* UNALIGNED_OK */
|
||||
|
||||
#if (-6 & 5) || CHAR_BIT != 8 || UINT_MAX < 0xffffffff || ULONG_MAX % 0xFFFF
|
||||
#error \
|
||||
"Sanity checking failed: Two's complement, reasonably sized integer types"
|
||||
#endif
|
||||
|
||||
/*----------------------------------------------------------------------------*/
|
||||
/* Compiler's includes for builtins/intrinsics */
|
||||
|
||||
#if defined(_MSC_VER) || defined(__INTEL_COMPILER)
|
||||
#include <intrin.h>
|
||||
#elif __GNUC_PREREQ(4, 4) || defined(__clang__)
|
||||
#if defined(__ia32__) || defined(__e2k__)
|
||||
#include <x86intrin.h>
|
||||
#endif /* __ia32__ */
|
||||
#if defined(__ia32__)
|
||||
#include <cpuid.h>
|
||||
#endif /* __ia32__ */
|
||||
#elif defined(__SUNPRO_C) || defined(__sun) || defined(sun)
|
||||
#include <mbarrier.h>
|
||||
#elif (defined(_HPUX_SOURCE) || defined(__hpux) || defined(__HP_aCC)) && \
|
||||
(defined(HP_IA64) || defined(__ia64))
|
||||
#include <machine/sys/inline.h>
|
||||
#elif defined(__IBMC__) && defined(__powerpc)
|
||||
#include <atomic.h>
|
||||
#elif defined(_AIX)
|
||||
#include <builtins.h>
|
||||
#include <sys/atomic_op.h>
|
||||
#elif (defined(__osf__) && defined(__DECC)) || defined(__alpha)
|
||||
#include <c_asm.h>
|
||||
#include <machine/builtins.h>
|
||||
#elif defined(__MWERKS__)
|
||||
/* CodeWarrior - troubles ? */
|
||||
#pragma gcc_extensions
|
||||
#elif defined(__SNC__)
|
||||
/* Sony PS3 - troubles ? */
|
||||
#elif defined(__hppa__) || defined(__hppa)
|
||||
#include <machine/inline.h>
|
||||
#else
|
||||
#error Unsupported C compiler, please use GNU C 4.4 or newer
|
||||
#endif /* Compiler */
|
||||
|
||||
/*----------------------------------------------------------------------------*/
|
||||
/* Byteorder */
|
||||
|
||||
#if !defined(__BYTE_ORDER__) || !defined(__ORDER_LITTLE_ENDIAN__) || \
|
||||
!defined(__ORDER_BIG_ENDIAN__)
|
||||
|
||||
/* *INDENT-OFF* */
|
||||
/* clang-format off */
|
||||
#if defined(__GLIBC__) || defined(__GNU_LIBRARY__) || defined(__ANDROID__) || \
|
||||
defined(HAVE_ENDIAN_H) || __has_include(<endian.h>)
|
||||
#include <endian.h>
|
||||
#elif defined(__APPLE__) || defined(__MACH__) || defined(__OpenBSD__) || \
|
||||
defined(HAVE_MACHINE_ENDIAN_H) || __has_include(<machine/endian.h>)
|
||||
#include <machine/endian.h>
|
||||
#elif defined(HAVE_SYS_ISA_DEFS_H) || __has_include(<sys/isa_defs.h>)
|
||||
#include <sys/isa_defs.h>
|
||||
#elif (defined(HAVE_SYS_TYPES_H) && defined(HAVE_SYS_ENDIAN_H)) || \
|
||||
(__has_include(<sys/types.h>) && __has_include(<sys/endian.h>))
|
||||
#include <sys/endian.h>
|
||||
#include <sys/types.h>
|
||||
#elif defined(__bsdi__) || defined(__DragonFly__) || defined(__FreeBSD__) || \
|
||||
defined(__NETBSD__) || defined(__NetBSD__) || \
|
||||
defined(HAVE_SYS_PARAM_H) || __has_include(<sys/param.h>)
|
||||
#include <sys/param.h>
|
||||
#endif /* OS */
|
||||
/* *INDENT-ON* */
|
||||
/* clang-format on */
|
||||
|
||||
#if defined(__BYTE_ORDER) && defined(__LITTLE_ENDIAN) && defined(__BIG_ENDIAN)
|
||||
#define __ORDER_LITTLE_ENDIAN__ __LITTLE_ENDIAN
|
||||
#define __ORDER_BIG_ENDIAN__ __BIG_ENDIAN
|
||||
#define __BYTE_ORDER__ __BYTE_ORDER
|
||||
#elif defined(_BYTE_ORDER) && defined(_LITTLE_ENDIAN) && defined(_BIG_ENDIAN)
|
||||
#define __ORDER_LITTLE_ENDIAN__ _LITTLE_ENDIAN
|
||||
#define __ORDER_BIG_ENDIAN__ _BIG_ENDIAN
|
||||
#define __BYTE_ORDER__ _BYTE_ORDER
|
||||
#else
|
||||
#define __ORDER_LITTLE_ENDIAN__ 1234
|
||||
#define __ORDER_BIG_ENDIAN__ 4321
|
||||
|
||||
#if defined(__LITTLE_ENDIAN__) || \
|
||||
(defined(_LITTLE_ENDIAN) && !defined(_BIG_ENDIAN)) || \
|
||||
defined(__ARMEL__) || defined(__THUMBEL__) || defined(__AARCH64EL__) || \
|
||||
defined(__MIPSEL__) || defined(_MIPSEL) || defined(__MIPSEL) || \
|
||||
defined(_M_ARM) || defined(_M_ARM64) || defined(__e2k__) || \
|
||||
defined(__elbrus_4c__) || defined(__elbrus_8c__) || defined(__bfin__) || \
|
||||
defined(__BFIN__) || defined(__ia64__) || defined(_IA64) || \
|
||||
defined(__IA64__) || defined(__ia64) || defined(_M_IA64) || \
|
||||
defined(__itanium__) || defined(__ia32__) || defined(__CYGWIN__) || \
|
||||
defined(_WIN64) || defined(_WIN32) || defined(__TOS_WIN__) || \
|
||||
defined(__WINDOWS__)
|
||||
#define __BYTE_ORDER__ __ORDER_LITTLE_ENDIAN__
|
||||
|
||||
#elif defined(__BIG_ENDIAN__) || \
|
||||
(defined(_BIG_ENDIAN) && !defined(_LITTLE_ENDIAN)) || \
|
||||
defined(__ARMEB__) || defined(__THUMBEB__) || defined(__AARCH64EB__) || \
|
||||
defined(__MIPSEB__) || defined(_MIPSEB) || defined(__MIPSEB) || \
|
||||
defined(__m68k__) || defined(M68000) || defined(__hppa__) || \
|
||||
defined(__hppa) || defined(__HPPA__) || defined(__sparc__) || \
|
||||
defined(__sparc) || defined(__370__) || defined(__THW_370__) || \
|
||||
defined(__s390__) || defined(__s390x__) || defined(__SYSC_ZARCH__)
|
||||
#define __BYTE_ORDER__ __ORDER_BIG_ENDIAN__
|
||||
|
||||
#else
|
||||
#error __BYTE_ORDER__ should be defined.
|
||||
#endif /* Arch */
|
||||
|
||||
#endif
|
||||
#endif /* __BYTE_ORDER__ || __ORDER_LITTLE_ENDIAN__ || __ORDER_BIG_ENDIAN__ */
|
||||
|
||||
/*----------------------------------------------------------------------------*/
|
||||
/* Memory/Compiler barriers, cache coherence */
|
||||
|
||||
static __inline void mdbx_compiler_barrier(void) {
|
||||
#if defined(__clang__) || defined(__GNUC__)
|
||||
__asm__ __volatile__("" ::: "memory");
|
||||
#elif defined(_MSC_VER)
|
||||
_ReadWriteBarrier();
|
||||
#elif defined(__INTEL_COMPILER) /* LY: Intel Compiler may mimic GCC and MSC */
|
||||
__memory_barrier();
|
||||
if (type > MDBX_BARRIER_COMPILER)
|
||||
#if defined(__ia64__) || defined(__ia64) || defined(_M_IA64)
|
||||
__mf();
|
||||
#elif defined(__i386__) || defined(__x86_64__)
|
||||
_mm_mfence();
|
||||
#else
|
||||
#error "Unknown target for Intel Compiler, please report to us."
|
||||
#endif
|
||||
#elif defined(__SUNPRO_C) || defined(__sun) || defined(sun)
|
||||
__compiler_barrier();
|
||||
#elif (defined(_HPUX_SOURCE) || defined(__hpux) || defined(__HP_aCC)) && \
|
||||
(defined(HP_IA64) || defined(__ia64))
|
||||
_Asm_sched_fence(/* LY: no-arg meaning 'all expect ALU', e.g. 0x3D3D */);
|
||||
#elif defined(_AIX) || defined(__ppc__) || defined(__powerpc__) || \
|
||||
defined(__ppc64__) || defined(__powerpc64__)
|
||||
__fence();
|
||||
#else
|
||||
#error "Could not guess the kind of compiler, please report to us."
|
||||
#endif
|
||||
}
|
||||
|
||||
static __inline void mdbx_memory_barrier(void) {
|
||||
#if __has_extension(c_atomic) || __has_extension(cxx_atomic)
|
||||
__c11_atomic_thread_fence(__ATOMIC_SEQ_CST);
|
||||
#elif defined(__ATOMIC_SEQ_CST)
|
||||
__atomic_thread_fence(__ATOMIC_SEQ_CST);
|
||||
#elif defined(__clang__) || defined(__GNUC__)
|
||||
__sync_synchronize();
|
||||
#elif defined(_MSC_VER)
|
||||
MemoryBarrier();
|
||||
#elif defined(__INTEL_COMPILER) /* LY: Intel Compiler may mimic GCC and MSC */
|
||||
#if defined(__ia64__) || defined(__ia64) || defined(_M_IA64)
|
||||
__mf();
|
||||
#elif defined(__i386__) || defined(__x86_64__)
|
||||
_mm_mfence();
|
||||
#else
|
||||
#error "Unknown target for Intel Compiler, please report to us."
|
||||
#endif
|
||||
#elif defined(__SUNPRO_C) || defined(__sun) || defined(sun)
|
||||
__machine_rw_barrier();
|
||||
#elif (defined(_HPUX_SOURCE) || defined(__hpux) || defined(__HP_aCC)) && \
|
||||
(defined(HP_IA64) || defined(__ia64))
|
||||
_Asm_mf();
|
||||
#elif defined(_AIX) || defined(__ppc__) || defined(__powerpc__) || \
|
||||
defined(__ppc64__) || defined(__powerpc64__)
|
||||
__lwsync();
|
||||
#else
|
||||
#error "Could not guess the kind of compiler, please report to us."
|
||||
#endif
|
||||
}
|
||||
|
||||
/*----------------------------------------------------------------------------*/
|
||||
/* Cache coherence and invalidation */
|
||||
|
||||
#ifndef MDBX_CPU_WRITEBACK_IS_COHERENT
|
||||
#if defined(__ia32__) || defined(__e2k__) || defined(__hppa) || \
|
||||
defined(__hppa__)
|
||||
#define MDBX_CPU_WRITEBACK_IS_COHERENT 1
|
||||
#else
|
||||
#define MDBX_CPU_WRITEBACK_IS_COHERENT 0
|
||||
#endif
|
||||
#endif /* MDBX_CPU_WRITEBACK_IS_COHERENT */
|
||||
|
||||
#ifndef MDBX_CACHELINE_SIZE
|
||||
#if defined(SYSTEM_CACHE_ALIGNMENT_SIZE)
|
||||
#define MDBX_CACHELINE_SIZE SYSTEM_CACHE_ALIGNMENT_SIZE
|
||||
#elif defined(__ia64__) || defined(__ia64) || defined(_M_IA64)
|
||||
#define MDBX_CACHELINE_SIZE 128
|
||||
#else
|
||||
#define MDBX_CACHELINE_SIZE 64
|
||||
#endif
|
||||
#endif /* MDBX_CACHELINE_SIZE */
|
||||
|
||||
#if MDBX_CPU_WRITEBACK_IS_COHERENT
|
||||
#define mdbx_flush_noncoherent_cpu_writeback() mdbx_compiler_barrier()
|
||||
#else
|
||||
#define mdbx_flush_noncoherent_cpu_writeback() mdbx_memory_barrier()
|
||||
#endif
|
||||
|
||||
#if __has_include(<sys/cachectl.h>)
|
||||
#include <sys/cachectl.h>
|
||||
#elif defined(__mips) || defined(__mips__) || defined(__mips64) || \
|
||||
defined(__mips64__) || defined(_M_MRX000) || defined(_MIPS_) || \
|
||||
defined(__MWERKS__) || defined(__sgi)
|
||||
/* MIPS should have explicit cache control */
|
||||
#include <sys/cachectl.h>
|
||||
#endif
|
||||
|
||||
#ifndef MDBX_CPU_CACHE_MMAP_NONCOHERENT
|
||||
#if defined(__mips) || defined(__mips__) || defined(__mips64) || \
|
||||
defined(__mips64__) || defined(_M_MRX000) || defined(_MIPS_) || \
|
||||
defined(__MWERKS__) || defined(__sgi)
|
||||
/* MIPS has cache coherency issues. */
|
||||
#define MDBX_CPU_CACHE_MMAP_NONCOHERENT 1
|
||||
#else
|
||||
/* LY: assume no relevant mmap/dcache issues. */
|
||||
#define MDBX_CPU_CACHE_MMAP_NONCOHERENT 0
|
||||
#endif
|
||||
#endif /* ndef MDBX_CPU_CACHE_MMAP_NONCOHERENT */
|
||||
|
||||
static __inline void mdbx_invalidate_mmap_noncoherent_cache(void *addr,
|
||||
size_t nbytes) {
|
||||
#if MDBX_CPU_CACHE_MMAP_NONCOHERENT
|
||||
#ifdef DCACHE
|
||||
/* MIPS has cache coherency issues.
|
||||
* Note: for any nbytes >= on-chip cache size, entire is flushed. */
|
||||
cacheflush(addr, nbytes, DCACHE);
|
||||
#else
|
||||
#error "Oops, cacheflush() not available"
|
||||
#endif /* DCACHE */
|
||||
#else /* MDBX_CPU_CACHE_MMAP_NONCOHERENT */
|
||||
(void)addr;
|
||||
(void)nbytes;
|
||||
#endif /* MDBX_CPU_CACHE_MMAP_NONCOHERENT */
|
||||
}
|
||||
|
||||
/*----------------------------------------------------------------------------*/
|
||||
/* libc compatibility stuff */
|
||||
|
||||
#if (!defined(__GLIBC__) && __GLIBC_PREREQ(2, 1)) && \
|
||||
(defined(_GNU_SOURCE) || defined(_BSD_SOURCE))
|
||||
#define mdbx_asprintf asprintf
|
||||
#define mdbx_vasprintf vasprintf
|
||||
#else
|
||||
MDBX_INTERNAL_FUNC __printf_args(2, 3) int __maybe_unused
|
||||
mdbx_asprintf(char **strp, const char *fmt, ...);
|
||||
MDBX_INTERNAL_FUNC int mdbx_vasprintf(char **strp, const char *fmt, va_list ap);
|
||||
#endif
|
||||
|
||||
/*----------------------------------------------------------------------------*/
|
||||
/* OS abstraction layer stuff */
|
||||
|
||||
/* max bytes to write in one call */
|
||||
#define MAX_WRITE UINT32_C(0x3fff0000)
|
||||
|
||||
#if defined(__linux__) || defined(__gnu_linux__)
|
||||
MDBX_INTERNAL_VAR uint32_t mdbx_linux_kernel_version;
|
||||
#endif /* Linux */
|
||||
|
||||
/* Get the size of a memory page for the system.
|
||||
* This is the basic size that the platform's memory manager uses, and is
|
||||
* fundamental to the use of memory-mapped files. */
|
||||
static __inline size_t mdbx_syspagesize(void) {
|
||||
#if defined(_WIN32) || defined(_WIN64)
|
||||
SYSTEM_INFO si;
|
||||
GetSystemInfo(&si);
|
||||
return si.dwPageSize;
|
||||
#else
|
||||
return sysconf(_SC_PAGE_SIZE);
|
||||
#endif
|
||||
}
|
||||
|
||||
#ifndef mdbx_strdup
|
||||
LIBMDBX_API char *mdbx_strdup(const char *str);
|
||||
#endif
|
||||
|
||||
static __inline int mdbx_get_errno(void) {
|
||||
#if defined(_WIN32) || defined(_WIN64)
|
||||
DWORD rc = GetLastError();
|
||||
#else
|
||||
int rc = errno;
|
||||
#endif
|
||||
return rc;
|
||||
}
|
||||
|
||||
#ifndef mdbx_memalign_alloc
|
||||
MDBX_INTERNAL_FUNC int mdbx_memalign_alloc(size_t alignment, size_t bytes,
|
||||
void **result);
|
||||
#endif
|
||||
#ifndef mdbx_memalign_free
|
||||
MDBX_INTERNAL_FUNC void mdbx_memalign_free(void *ptr);
|
||||
#endif
|
||||
|
||||
MDBX_INTERNAL_FUNC int mdbx_condmutex_init(mdbx_condmutex_t *condmutex);
|
||||
MDBX_INTERNAL_FUNC int mdbx_condmutex_lock(mdbx_condmutex_t *condmutex);
|
||||
MDBX_INTERNAL_FUNC int mdbx_condmutex_unlock(mdbx_condmutex_t *condmutex);
|
||||
MDBX_INTERNAL_FUNC int mdbx_condmutex_signal(mdbx_condmutex_t *condmutex);
|
||||
MDBX_INTERNAL_FUNC int mdbx_condmutex_wait(mdbx_condmutex_t *condmutex);
|
||||
MDBX_INTERNAL_FUNC int mdbx_condmutex_destroy(mdbx_condmutex_t *condmutex);
|
||||
|
||||
MDBX_INTERNAL_FUNC int mdbx_fastmutex_init(mdbx_fastmutex_t *fastmutex);
|
||||
MDBX_INTERNAL_FUNC int mdbx_fastmutex_acquire(mdbx_fastmutex_t *fastmutex);
|
||||
MDBX_INTERNAL_FUNC int mdbx_fastmutex_release(mdbx_fastmutex_t *fastmutex);
|
||||
MDBX_INTERNAL_FUNC int mdbx_fastmutex_destroy(mdbx_fastmutex_t *fastmutex);
|
||||
|
||||
MDBX_INTERNAL_FUNC int mdbx_pwritev(mdbx_filehandle_t fd, struct iovec *iov,
|
||||
int iovcnt, uint64_t offset,
|
||||
size_t expected_written);
|
||||
MDBX_INTERNAL_FUNC int mdbx_pread(mdbx_filehandle_t fd, void *buf, size_t count,
|
||||
uint64_t offset);
|
||||
MDBX_INTERNAL_FUNC int mdbx_pwrite(mdbx_filehandle_t fd, const void *buf,
|
||||
size_t count, uint64_t offset);
|
||||
|
||||
MDBX_INTERNAL_FUNC int
|
||||
mdbx_thread_create(mdbx_thread_t *thread,
|
||||
THREAD_RESULT(THREAD_CALL *start_routine)(void *),
|
||||
void *arg);
|
||||
MDBX_INTERNAL_FUNC int mdbx_thread_join(mdbx_thread_t thread);
|
||||
|
||||
enum mdbx_syncmode_bits {
|
||||
MDBX_SYNC_DATA = 1,
|
||||
MDBX_SYNC_SIZE = 2,
|
||||
MDBX_SYNC_IODQ = 4
|
||||
};
|
||||
|
||||
MDBX_INTERNAL_FUNC int mdbx_filesync(mdbx_filehandle_t fd,
|
||||
enum mdbx_syncmode_bits mode_bits);
|
||||
MDBX_INTERNAL_FUNC int mdbx_ftruncate(mdbx_filehandle_t fd, uint64_t length);
|
||||
MDBX_INTERNAL_FUNC int mdbx_fseek(mdbx_filehandle_t fd, uint64_t pos);
|
||||
MDBX_INTERNAL_FUNC int mdbx_filesize(mdbx_filehandle_t fd, uint64_t *length);
|
||||
MDBX_INTERNAL_FUNC int mdbx_openfile(const char *pathname, int flags,
|
||||
mode_t mode, mdbx_filehandle_t *fd,
|
||||
bool exclusive);
|
||||
MDBX_INTERNAL_FUNC int mdbx_closefile(mdbx_filehandle_t fd);
|
||||
MDBX_INTERNAL_FUNC int mdbx_removefile(const char *pathname);
|
||||
|
||||
typedef struct mdbx_mmap_param {
|
||||
union {
|
||||
void *address;
|
||||
uint8_t *dxb;
|
||||
struct MDBX_lockinfo *lck;
|
||||
};
|
||||
mdbx_filehandle_t fd;
|
||||
size_t length; /* mapping length, but NOT a size of file or DB */
|
||||
#if defined(_WIN32) || defined(_WIN64)
|
||||
size_t current; /* mapped region size, e.g. file and DB */
|
||||
uint64_t filesize;
|
||||
#endif
|
||||
#ifdef MDBX_OSAL_SECTION
|
||||
MDBX_OSAL_SECTION section;
|
||||
#endif
|
||||
} mdbx_mmap_t;
|
||||
|
||||
MDBX_INTERNAL_FUNC int mdbx_mmap(int flags, mdbx_mmap_t *map, size_t must,
|
||||
size_t limit);
|
||||
MDBX_INTERNAL_FUNC int mdbx_munmap(mdbx_mmap_t *map);
|
||||
MDBX_INTERNAL_FUNC int mdbx_mresize(int flags, mdbx_mmap_t *map, size_t current,
|
||||
size_t wanna);
|
||||
#if defined(_WIN32) || defined(_WIN64)
|
||||
typedef struct {
|
||||
unsigned limit, count;
|
||||
HANDLE handles[31];
|
||||
} mdbx_handle_array_t;
|
||||
MDBX_INTERNAL_FUNC int
|
||||
mdbx_suspend_threads_before_remap(MDBX_env *env, mdbx_handle_array_t **array);
|
||||
MDBX_INTERNAL_FUNC int
|
||||
mdbx_resume_threads_after_remap(mdbx_handle_array_t *array);
|
||||
#endif /* Windows */
|
||||
MDBX_INTERNAL_FUNC int mdbx_msync(mdbx_mmap_t *map, size_t offset,
|
||||
size_t length, int async);
|
||||
MDBX_INTERNAL_FUNC int mdbx_check4nonlocal(mdbx_filehandle_t handle, int flags);
|
||||
|
||||
static __inline mdbx_pid_t mdbx_getpid(void) {
|
||||
#if defined(_WIN32) || defined(_WIN64)
|
||||
return GetCurrentProcessId();
|
||||
#else
|
||||
return getpid();
|
||||
#endif
|
||||
}
|
||||
|
||||
static __inline mdbx_tid_t mdbx_thread_self(void) {
|
||||
#if defined(_WIN32) || defined(_WIN64)
|
||||
return GetCurrentThreadId();
|
||||
#else
|
||||
return pthread_self();
|
||||
#endif
|
||||
}
|
||||
|
||||
MDBX_INTERNAL_FUNC void __maybe_unused mdbx_osal_jitter(bool tiny);
|
||||
MDBX_INTERNAL_FUNC uint64_t mdbx_osal_monotime(void);
|
||||
MDBX_INTERNAL_FUNC uint64_t
|
||||
mdbx_osal_16dot16_to_monotime(uint32_t seconds_16dot16);
|
||||
|
||||
/*----------------------------------------------------------------------------*/
|
||||
/* lck stuff */
|
||||
|
||||
#if defined(_WIN32) || defined(_WIN64)
|
||||
#undef MDBX_OSAL_LOCK
|
||||
#define MDBX_OSAL_LOCK_SIGN UINT32_C(0xF10C)
|
||||
#else
|
||||
#define MDBX_OSAL_LOCK pthread_mutex_t
|
||||
#define MDBX_OSAL_LOCK_SIGN UINT32_C(0x8017)
|
||||
#endif /* MDBX_OSAL_LOCK */
|
||||
|
||||
/// \brief Инициализация объектов синхронизации связанных с экземпляром MDBX_env
|
||||
/// как общик в LCK-файле, так и внутри текущего процесса.
|
||||
/// \param
|
||||
/// global_uniqueness_flag = true - означает что сейчас в системе нет других
|
||||
/// процессов работающих с БД и LCK-файлом. Соответственно функция ДОЛЖНА
|
||||
/// инициализировать разделяемые объекты синхронизации расположенные
|
||||
/// в отображенном в память LCK-файле.
|
||||
/// global_uniqueness_flag = false - означает что в системе есть хотя-бы
|
||||
/// один другой процесс уже работающий с БД и LCK-файлом, в том числе
|
||||
/// БД уже может быть открыта текущим процессом. Соответственно функция
|
||||
/// НЕ должна инициализировать уже используемые разделяемые объекты
|
||||
/// синхронизации расположенные в отображенном в память LCK-файле.
|
||||
/// \return Код ошибки или 0 в случае успеха.
|
||||
MDBX_INTERNAL_FUNC int mdbx_lck_init(MDBX_env *env, int global_uniqueness_flag);
|
||||
|
||||
/// \brief Отключение от общих межпроцесных объектов и разрушение объектов
|
||||
/// синхронизации внутри текущего процесса связанных с экземпляром MDBX_env.
|
||||
/// \param
|
||||
/// inprocess_neighbor = NULL - если в текущем процессе нет других экземпляров
|
||||
/// MDBX_env связанных с закрываемой БД. Соответственно функция ДОЛЖНА
|
||||
/// своими средствами проверить, есть ли другие процесса работающие с БД
|
||||
/// и LCK-файлом, и в зависимости от этого разрушить или сохранить бъекты
|
||||
/// синхронизации расположенные в отображенном в память LCK-файле.
|
||||
/// inprocess_neighbor = не-NULL - тогда он указывает на другой (любой
|
||||
/// в случае нескольких) экземпляр MDBX_env работающей с БД и LCK-файлом
|
||||
/// внутри текущего процесса. Соответственно, функция НЕ должна пытаться
|
||||
/// захватывать эксклюзивную блокировки и/или пытаться разрушить общие
|
||||
/// объекты синхронизации связанные с БД и LCK-файлом. Кроме этого,
|
||||
/// реализация ДОЛЖНА обеспечить корректность работы других экземпляров
|
||||
/// MDBX_env внутри процесса - например, восстановить POSIX-fcntl блокировки
|
||||
/// после закрытия файловых дескрипторов.
|
||||
/// \return Код ошибки (MDBX_PANIC) или 0 в случае успеха.
|
||||
MDBX_INTERNAL_FUNC int mdbx_lck_destroy(MDBX_env *env,
|
||||
MDBX_env *inprocess_neighbor);
|
||||
|
||||
/// \brief Подключение к общим межпроцесным объектам блокировки с попыткой
|
||||
/// захвата блокировки максимального уровня (разделяемой при недоступности
|
||||
/// эксклюзивной).
|
||||
/// В зависимости от реализации и/или платформы (Windows) может
|
||||
/// захватывать блокировку не-операционного супер-уровня (например, для
|
||||
/// инициализации разделяемых объектов синхронизации), которая затем будет
|
||||
/// понижена до операционно-эксклюзивной или разделяемой посредством
|
||||
/// явного вызова mdbx_lck_downgrade().
|
||||
/// \return
|
||||
/// MDBX_RESULT_TRUE (-1) - если удалось захватить эксклюзивную блокировку и,
|
||||
/// следовательно, текущий процесс является первым и единственным
|
||||
/// после предыдущего использования БД.
|
||||
/// MDBX_RESULT_FALSE (0) - если удалось захватить только разделяемую
|
||||
/// блокировку и, следовательно, БД уже открыта и используется другими
|
||||
/// процессами.
|
||||
/// Иначе (не 0 и не -1) - код ошибки.
|
||||
MDBX_INTERNAL_FUNC int mdbx_lck_seize(MDBX_env *env);
|
||||
|
||||
/// \brief Снижает уровень первоначальной захваченной блокировки до
|
||||
/// операционного уровня определяемого аргументом.
|
||||
/// \param
|
||||
/// complete = TRUE - понижение до разделяемой блокировки.
|
||||
/// complete = FALSE - понижение до эксклюзивной операционной блокировки.
|
||||
/// \return Код ошибки или 0 в случае успеха.
|
||||
MDBX_INTERNAL_FUNC int mdbx_lck_downgrade(MDBX_env *env, bool complete);
|
||||
|
||||
/// \brief Блокирует lck-файл и/или таблицу читателей для (де)регистрации.
|
||||
/// \return Код ошибки или 0 в случае успеха.
|
||||
MDBX_INTERNAL_FUNC int mdbx_rdt_lock(MDBX_env *env);
|
||||
|
||||
/// \brief Разблокирует lck-файл и/или таблицу читателей после (де)регистрации.
|
||||
MDBX_INTERNAL_FUNC void mdbx_rdt_unlock(MDBX_env *env);
|
||||
|
||||
/// \brief Захватывает блокировку для изменения БД (при старте пишущей
|
||||
/// транзакции). Транзакции чтения при этом никак не блокируются.
|
||||
/// Объявлена без MDBX_INTERNAL_FUNC так как используется в mdbx_chk.
|
||||
/// \return Код ошибки или 0 в случае успеха.
|
||||
int mdbx_txn_lock(MDBX_env *env, bool dontwait);
|
||||
|
||||
/// \brief Освобождает блокировку по окончанию изменения БД (после завершения
|
||||
/// пишущей транзакции).
|
||||
/// Объявлена без MDBX_INTERNAL_FUNC так как используется в mdbx_chk.
|
||||
void mdbx_txn_unlock(MDBX_env *env);
|
||||
|
||||
/// \brief Устанавливает alive-флажок присутствия (индицирующую блокировку)
|
||||
/// читателя для pid текущего процесса. Функции может выполнить не более
|
||||
/// необходимого минимума для корректной работы mdbx_rpid_check() в других
|
||||
/// процессах.
|
||||
/// \return Код ошибки или 0 в случае успеха.
|
||||
MDBX_INTERNAL_FUNC int mdbx_rpid_set(MDBX_env *env);
|
||||
|
||||
/// \brief Снимает alive-флажок присутствия (индицирующую блокировку)
|
||||
/// читателя для pid текущего процесса. Функции может выполнить не более
|
||||
/// необходимого минимума для корректной работы mdbx_rpid_check() в других
|
||||
/// процессах.
|
||||
/// \return Код ошибки или 0 в случае успеха.
|
||||
MDBX_INTERNAL_FUNC int mdbx_rpid_clear(MDBX_env *env);
|
||||
|
||||
/// \brief Проверяет жив ли процесс-читатель с заданным pid
|
||||
/// по alive-флажку присутствия (индицирующей блокировку),
|
||||
/// либо любым другим способом.
|
||||
/// \return
|
||||
/// MDBX_RESULT_TRUE (-1) - если процесс-читатель с соответствующим pid жив
|
||||
/// и работает с БД (индицирующая блокировка присутствует).
|
||||
/// MDBX_RESULT_FALSE (0) - если процесс-читатель с соответствующим pid
|
||||
/// отсутствует или не работает с БД (индицирующая блокировка отсутствует).
|
||||
/// Иначе (не 0 и не -1) - код ошибки.
|
||||
MDBX_INTERNAL_FUNC int mdbx_rpid_check(MDBX_env *env, mdbx_pid_t pid);
|
||||
|
||||
#if defined(_WIN32) || defined(_WIN64)
|
||||
typedef union MDBX_srwlock {
|
||||
struct {
|
||||
long volatile readerCount;
|
||||
long volatile writerCount;
|
||||
};
|
||||
RTL_SRWLOCK native;
|
||||
} MDBX_srwlock;
|
||||
|
||||
typedef void(WINAPI *MDBX_srwlock_function)(MDBX_srwlock *);
|
||||
MDBX_INTERNAL_VAR MDBX_srwlock_function mdbx_srwlock_Init,
|
||||
mdbx_srwlock_AcquireShared, mdbx_srwlock_ReleaseShared,
|
||||
mdbx_srwlock_AcquireExclusive, mdbx_srwlock_ReleaseExclusive;
|
||||
|
||||
typedef BOOL(WINAPI *MDBX_GetFileInformationByHandleEx)(
|
||||
_In_ HANDLE hFile, _In_ FILE_INFO_BY_HANDLE_CLASS FileInformationClass,
|
||||
_Out_ LPVOID lpFileInformation, _In_ DWORD dwBufferSize);
|
||||
MDBX_INTERNAL_VAR MDBX_GetFileInformationByHandleEx
|
||||
mdbx_GetFileInformationByHandleEx;
|
||||
|
||||
typedef BOOL(WINAPI *MDBX_GetVolumeInformationByHandleW)(
|
||||
_In_ HANDLE hFile, _Out_opt_ LPWSTR lpVolumeNameBuffer,
|
||||
_In_ DWORD nVolumeNameSize, _Out_opt_ LPDWORD lpVolumeSerialNumber,
|
||||
_Out_opt_ LPDWORD lpMaximumComponentLength,
|
||||
_Out_opt_ LPDWORD lpFileSystemFlags,
|
||||
_Out_opt_ LPWSTR lpFileSystemNameBuffer, _In_ DWORD nFileSystemNameSize);
|
||||
MDBX_INTERNAL_VAR MDBX_GetVolumeInformationByHandleW
|
||||
mdbx_GetVolumeInformationByHandleW;
|
||||
|
||||
typedef DWORD(WINAPI *MDBX_GetFinalPathNameByHandleW)(_In_ HANDLE hFile,
|
||||
_Out_ LPWSTR lpszFilePath,
|
||||
_In_ DWORD cchFilePath,
|
||||
_In_ DWORD dwFlags);
|
||||
MDBX_INTERNAL_VAR MDBX_GetFinalPathNameByHandleW mdbx_GetFinalPathNameByHandleW;
|
||||
|
||||
typedef BOOL(WINAPI *MDBX_SetFileInformationByHandle)(
|
||||
_In_ HANDLE hFile, _In_ FILE_INFO_BY_HANDLE_CLASS FileInformationClass,
|
||||
_Out_ LPVOID lpFileInformation, _In_ DWORD dwBufferSize);
|
||||
MDBX_INTERNAL_VAR MDBX_SetFileInformationByHandle
|
||||
mdbx_SetFileInformationByHandle;
|
||||
|
||||
typedef NTSTATUS(NTAPI *MDBX_NtFsControlFile)(
|
||||
IN HANDLE FileHandle, IN OUT HANDLE Event,
|
||||
IN OUT PVOID /* PIO_APC_ROUTINE */ ApcRoutine, IN OUT PVOID ApcContext,
|
||||
OUT PIO_STATUS_BLOCK IoStatusBlock, IN ULONG FsControlCode,
|
||||
IN OUT PVOID InputBuffer, IN ULONG InputBufferLength,
|
||||
OUT OPTIONAL PVOID OutputBuffer, IN ULONG OutputBufferLength);
|
||||
MDBX_INTERNAL_VAR MDBX_NtFsControlFile mdbx_NtFsControlFile;
|
||||
|
||||
#if !defined(_WIN32_WINNT_WIN8) || _WIN32_WINNT < _WIN32_WINNT_WIN8
|
||||
typedef struct _WIN32_MEMORY_RANGE_ENTRY {
|
||||
PVOID VirtualAddress;
|
||||
SIZE_T NumberOfBytes;
|
||||
} WIN32_MEMORY_RANGE_ENTRY, *PWIN32_MEMORY_RANGE_ENTRY;
|
||||
#endif /* Windows 8.x */
|
||||
|
||||
typedef BOOL(WINAPI *MDBX_PrefetchVirtualMemory)(
|
||||
HANDLE hProcess, ULONG_PTR NumberOfEntries,
|
||||
PWIN32_MEMORY_RANGE_ENTRY VirtualAddresses, ULONG Flags);
|
||||
MDBX_INTERNAL_VAR MDBX_PrefetchVirtualMemory mdbx_PrefetchVirtualMemory;
|
||||
|
||||
typedef DWORD(WINAPI *MDBX_DiscardVirtualMemory)(PVOID VirtualAddress,
|
||||
SIZE_T Size);
|
||||
MDBX_INTERNAL_VAR MDBX_DiscardVirtualMemory mdbx_DiscardVirtualMemory;
|
||||
|
||||
#endif /* Windows */
|
||||
|
||||
/*----------------------------------------------------------------------------*/
|
||||
/* Atomics */
|
||||
|
||||
#if !defined(__cplusplus) && (__STDC_VERSION__ >= 201112L) && \
|
||||
!defined(__STDC_NO_ATOMICS__) && \
|
||||
(__GNUC_PREREQ(4, 9) || __CLANG_PREREQ(3, 8) || \
|
||||
!(defined(__GNUC__) || defined(__clang__)))
|
||||
#include <stdatomic.h>
|
||||
#elif defined(__GNUC__) || defined(__clang__)
|
||||
/* LY: nothing required */
|
||||
#elif defined(_MSC_VER)
|
||||
#pragma warning(disable : 4163) /* 'xyz': not available as an intrinsic */
|
||||
#pragma warning(disable : 4133) /* 'function': incompatible types - from \
|
||||
'size_t' to 'LONGLONG' */
|
||||
#pragma warning(disable : 4244) /* 'return': conversion from 'LONGLONG' to \
|
||||
'std::size_t', possible loss of data */
|
||||
#pragma warning(disable : 4267) /* 'function': conversion from 'size_t' to \
|
||||
'long', possible loss of data */
|
||||
#pragma intrinsic(_InterlockedExchangeAdd, _InterlockedCompareExchange)
|
||||
#pragma intrinsic(_InterlockedExchangeAdd64, _InterlockedCompareExchange64)
|
||||
#elif defined(__APPLE__)
|
||||
#include <libkern/OSAtomic.h>
|
||||
#else
|
||||
#error FIXME atomic-ops
|
||||
#endif
|
||||
|
||||
static __inline uint32_t mdbx_atomic_add32(volatile uint32_t *p, uint32_t v) {
|
||||
#if !defined(__cplusplus) && defined(ATOMIC_VAR_INIT)
|
||||
assert(atomic_is_lock_free(p));
|
||||
return atomic_fetch_add((_Atomic uint32_t *)p, v);
|
||||
#elif defined(__GNUC__) || defined(__clang__)
|
||||
return __sync_fetch_and_add(p, v);
|
||||
#else
|
||||
#ifdef _MSC_VER
|
||||
STATIC_ASSERT(sizeof(volatile long) == sizeof(volatile uint32_t));
|
||||
return _InterlockedExchangeAdd((volatile long *)p, v);
|
||||
#endif
|
||||
#ifdef __APPLE__
|
||||
return OSAtomicAdd32(v, (volatile int32_t *)p);
|
||||
#endif
|
||||
#endif
|
||||
}
|
||||
|
||||
static __inline uint64_t mdbx_atomic_add64(volatile uint64_t *p, uint64_t v) {
|
||||
#if !defined(__cplusplus) && defined(ATOMIC_VAR_INIT)
|
||||
assert(atomic_is_lock_free(p));
|
||||
return atomic_fetch_add((_Atomic uint64_t *)p, v);
|
||||
#elif defined(__GNUC__) || defined(__clang__)
|
||||
return __sync_fetch_and_add(p, v);
|
||||
#else
|
||||
#ifdef _MSC_VER
|
||||
#ifdef _WIN64
|
||||
return _InterlockedExchangeAdd64((volatile int64_t *)p, v);
|
||||
#else
|
||||
return InterlockedExchangeAdd64((volatile int64_t *)p, v);
|
||||
#endif
|
||||
#endif /* _MSC_VER */
|
||||
#ifdef __APPLE__
|
||||
return OSAtomicAdd64(v, (volatile int64_t *)p);
|
||||
#endif
|
||||
#endif
|
||||
}
|
||||
|
||||
#define mdbx_atomic_sub32(p, v) mdbx_atomic_add32(p, 0 - (v))
|
||||
#define mdbx_atomic_sub64(p, v) mdbx_atomic_add64(p, 0 - (v))
|
||||
|
||||
static __inline bool mdbx_atomic_compare_and_swap32(volatile uint32_t *p,
|
||||
uint32_t c, uint32_t v) {
|
||||
#if !defined(__cplusplus) && defined(ATOMIC_VAR_INIT)
|
||||
assert(atomic_is_lock_free(p));
|
||||
return atomic_compare_exchange_strong((_Atomic uint32_t *)p, &c, v);
|
||||
#elif defined(__GNUC__) || defined(__clang__)
|
||||
return __sync_bool_compare_and_swap(p, c, v);
|
||||
#else
|
||||
#ifdef _MSC_VER
|
||||
STATIC_ASSERT(sizeof(volatile long) == sizeof(volatile uint32_t));
|
||||
return c == _InterlockedCompareExchange((volatile long *)p, v, c);
|
||||
#endif
|
||||
#ifdef __APPLE__
|
||||
return c == OSAtomicCompareAndSwap32Barrier(c, v, (volatile int32_t *)p);
|
||||
#endif
|
||||
#endif
|
||||
}
|
||||
|
||||
static __inline bool mdbx_atomic_compare_and_swap64(volatile uint64_t *p,
|
||||
uint64_t c, uint64_t v) {
|
||||
#if !defined(__cplusplus) && defined(ATOMIC_VAR_INIT)
|
||||
assert(atomic_is_lock_free(p));
|
||||
return atomic_compare_exchange_strong((_Atomic uint64_t *)p, &c, v);
|
||||
#elif defined(__GNUC__) || defined(__clang__)
|
||||
return __sync_bool_compare_and_swap(p, c, v);
|
||||
#else
|
||||
#ifdef _MSC_VER
|
||||
return c == _InterlockedCompareExchange64((volatile int64_t *)p, v, c);
|
||||
#endif
|
||||
#ifdef __APPLE__
|
||||
return c == OSAtomicCompareAndSwap64Barrier(c, v, (volatile uint64_t *)p);
|
||||
#endif
|
||||
#endif
|
||||
}
|
||||
|
||||
/*----------------------------------------------------------------------------*/
|
||||
|
||||
#if defined(_MSC_VER) && _MSC_VER >= 1900
|
||||
/* LY: MSVC 2015/2017/2019 has buggy/inconsistent PRIuPTR/PRIxPTR macros
|
||||
* for internal format-args checker. */
|
||||
#undef PRIuPTR
|
||||
#undef PRIiPTR
|
||||
#undef PRIdPTR
|
||||
#undef PRIxPTR
|
||||
#define PRIuPTR "Iu"
|
||||
#define PRIiPTR "Ii"
|
||||
#define PRIdPTR "Id"
|
||||
#define PRIxPTR "Ix"
|
||||
#define PRIuSIZE "zu"
|
||||
#define PRIiSIZE "zi"
|
||||
#define PRIdSIZE "zd"
|
||||
#define PRIxSIZE "zx"
|
||||
#endif /* fix PRI*PTR for _MSC_VER */
|
||||
|
||||
#ifndef PRIuSIZE
|
||||
#define PRIuSIZE PRIuPTR
|
||||
#define PRIiSIZE PRIiPTR
|
||||
#define PRIdSIZE PRIdPTR
|
||||
#define PRIxSIZE PRIxPTR
|
||||
#endif /* PRI*SIZE macros for MSVC */
|
||||
|
||||
#ifdef _MSC_VER
|
||||
#pragma warning(pop)
|
||||
#endif
|
34
src/elements/version.c.in
Normal file
34
src/elements/version.c.in
Normal file
@@ -0,0 +1,34 @@
|
||||
/*
|
||||
* Copyright 2015-2019 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 "./internals.h"
|
||||
|
||||
#if MDBX_VERSION_MAJOR != 0 || MDBX_VERSION_MINOR != 3
|
||||
#error "API version mismatch!"
|
||||
#endif
|
||||
|
||||
#define MDBX_VERSION_RELEASE 0
|
||||
#define MDBX_VERSION_REVISION 0
|
||||
|
||||
/*LIBMDBX_EXPORTS*/ const mdbx_version_info mdbx_version = {
|
||||
MDBX_VERSION_MAJOR,
|
||||
MDBX_VERSION_MINOR,
|
||||
MDBX_VERSION_RELEASE,
|
||||
MDBX_VERSION_REVISION,
|
||||
{"@MDBX_GIT_TIMESTAMP@", "@MDBX_GIT_TREE@", "@MDBX_GIT_COMMIT@",
|
||||
"@MDBX_GIT_DESCRIBE@"}};
|
||||
|
||||
/*LIBMDBX_EXPORTS*/ const mdbx_build_info mdbx_build = {
|
||||
"@MDBX_BUILD_TIMESTAMP@", "@MDBX_BUILD_TARGET@", "@MDBX_BUILD_OPTIONS@",
|
||||
"@MDBX_BUILD_COMPILER@", "@MDBX_BUILD_FLAGS@"};
|
Reference in New Issue
Block a user