mirror of
https://github.com/isar/libmdbx.git
synced 2024-10-30 11:29:19 +08:00
299 lines
8.4 KiB
C++
299 lines
8.4 KiB
C++
/*
|
|
* Copyright 2017 Leonid Yuriev <leo@yuriev.ru>
|
|
* and other libmdbx authors: please see AUTHORS file.
|
|
* All rights reserved.
|
|
*
|
|
* Redistribution and use in source and binary forms, with or without
|
|
* modification, are permitted only as authorized by the OpenLDAP
|
|
* Public License.
|
|
*
|
|
* A copy of this license is available in the file LICENSE in the
|
|
* top-level directory of the distribution or, alternatively, at
|
|
* <http://www.OpenLDAP.org/license.html>.
|
|
*/
|
|
|
|
#include "test.h"
|
|
|
|
static std::vector<HANDLE> events;
|
|
static HANDLE hBarrierSemaphore, hBarrierEvent;
|
|
|
|
static int waitstatus2errcode(DWORD result) {
|
|
switch (result) {
|
|
case WAIT_OBJECT_0:
|
|
return MDB_SUCCESS;
|
|
case WAIT_FAILED:
|
|
return GetLastError();
|
|
case WAIT_ABANDONED:
|
|
return ERROR_ABANDONED_WAIT_0;
|
|
case WAIT_IO_COMPLETION:
|
|
return ERROR_USER_APC;
|
|
case WAIT_TIMEOUT:
|
|
return ERROR_TIMEOUT;
|
|
default:
|
|
return ERROR_UNHANDLED_ERROR;
|
|
}
|
|
}
|
|
|
|
void osal_wait4barrier(void) {
|
|
DWORD rc = WaitForSingleObject(hBarrierSemaphore, 0);
|
|
switch (rc) {
|
|
default:
|
|
failure_perror("WaitForSingleObject(BarrierSemaphore)",
|
|
waitstatus2errcode(rc));
|
|
case WAIT_OBJECT_0:
|
|
rc = WaitForSingleObject(hBarrierEvent, INFINITE);
|
|
if (rc != WAIT_OBJECT_0)
|
|
failure_perror("WaitForSingleObject(BarrierEvent)",
|
|
waitstatus2errcode(rc));
|
|
break;
|
|
case WAIT_TIMEOUT:
|
|
if (!SetEvent(hBarrierEvent))
|
|
failure_perror("SetEvent(BarrierEvent)", GetLastError());
|
|
break;
|
|
}
|
|
}
|
|
|
|
static HANDLE make_inharitable(HANDLE hHandle) {
|
|
assert(hHandle != NULL && hHandle != INVALID_HANDLE_VALUE);
|
|
if (!DuplicateHandle(GetCurrentProcess(), hHandle, GetCurrentProcess(),
|
|
&hHandle, 0, TRUE,
|
|
DUPLICATE_CLOSE_SOURCE | DUPLICATE_SAME_ACCESS))
|
|
failure_perror("DuplicateHandle()", GetLastError());
|
|
return hHandle;
|
|
}
|
|
|
|
void osal_setup(const std::vector<actor_config> &actors) {
|
|
assert(events.empty());
|
|
const size_t n = actors.size() + 1;
|
|
events.reserve(n);
|
|
|
|
for (size_t i = 0; i < n; ++i) {
|
|
HANDLE hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
|
|
if (!hEvent)
|
|
failure_perror("CreateEvent()", GetLastError());
|
|
hEvent = make_inharitable(hEvent);
|
|
log_trace("osal_setup: event %zu -> %p", i, hEvent);
|
|
events.push_back(hEvent);
|
|
}
|
|
|
|
hBarrierSemaphore = CreateSemaphore(NULL, 0, (LONG)actors.size(), NULL);
|
|
if (!hBarrierSemaphore)
|
|
failure_perror("CreateSemaphore(BarrierSemaphore)", GetLastError());
|
|
hBarrierSemaphore = make_inharitable(hBarrierSemaphore);
|
|
|
|
hBarrierEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
|
|
if (!hBarrierEvent)
|
|
failure_perror("CreateEvent(BarrierEvent)", GetLastError());
|
|
hBarrierEvent = make_inharitable(hBarrierEvent);
|
|
}
|
|
|
|
void osal_broadcast(unsigned id) {
|
|
log_trace("osal_broadcast: event %u", id);
|
|
if (!SetEvent(events.at(id)))
|
|
failure_perror("SetEvent()", GetLastError());
|
|
}
|
|
|
|
int osal_waitfor(unsigned id) {
|
|
log_trace("osal_waitfor: event %u", id);
|
|
DWORD rc = WaitForSingleObject(events.at(id), INFINITE);
|
|
return waitstatus2errcode(rc);
|
|
}
|
|
|
|
mdbx_pid_t osal_getpid(void) { return GetCurrentProcessId(); }
|
|
|
|
int osal_delay(unsigned seconds) {
|
|
Sleep(seconds * 1000u);
|
|
return 0;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
const std::string
|
|
actor_config::osal_serialize(simple_checksum &checksum) const {
|
|
checksum.push(hBarrierSemaphore);
|
|
checksum.push(hBarrierEvent);
|
|
|
|
HANDLE hWait = INVALID_HANDLE_VALUE;
|
|
if (wait4id) {
|
|
hWait = events.at(wait4id);
|
|
checksum.push(hWait);
|
|
}
|
|
|
|
HANDLE hSignal = INVALID_HANDLE_VALUE;
|
|
if (wanna_event4signalling()) {
|
|
hSignal = events.at(id);
|
|
checksum.push(hSignal);
|
|
}
|
|
|
|
return format("%p.%p.%p.%p", hBarrierSemaphore, hBarrierEvent, hWait,
|
|
hSignal);
|
|
}
|
|
|
|
bool actor_config::osal_deserialize(const char *str, const char *end,
|
|
simple_checksum &checksum) {
|
|
|
|
std::string copy(str, end - str);
|
|
TRACE(">> osal_deserialize(%s)\n", copy.c_str());
|
|
|
|
assert(hBarrierSemaphore == 0);
|
|
assert(hBarrierEvent == 0);
|
|
assert(events.empty());
|
|
|
|
HANDLE hWait, hSignal;
|
|
if (sscanf_s(copy.c_str(), "%p.%p.%p.%p", &hBarrierSemaphore, &hBarrierEvent,
|
|
&hWait, &hSignal) != 4) {
|
|
TRACE("<< osal_deserialize: failed\n");
|
|
return false;
|
|
}
|
|
|
|
checksum.push(hBarrierSemaphore);
|
|
checksum.push(hBarrierEvent);
|
|
|
|
if (wait4id) {
|
|
checksum.push(hWait);
|
|
events[wait4id] = hWait;
|
|
}
|
|
|
|
if (wanna_event4signalling()) {
|
|
checksum.push(hSignal);
|
|
events[id] = hSignal;
|
|
}
|
|
|
|
TRACE("<< osal_deserialize: OK\n");
|
|
return true;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
typedef std::pair<HANDLE, actor_status> child;
|
|
static std::unordered_map<mdbx_pid_t, child> childs;
|
|
|
|
int osal_actor_start(const actor_config &config, mdbx_pid_t &pid) {
|
|
if (childs.size() == MAXIMUM_WAIT_OBJECTS)
|
|
failure("Could't manage more that %u actors on Windows\n",
|
|
MAXIMUM_WAIT_OBJECTS);
|
|
|
|
_flushall();
|
|
|
|
STARTUPINFOA StartupInfo;
|
|
GetStartupInfoA(&StartupInfo);
|
|
|
|
char exename[_MAX_PATH];
|
|
DWORD exename_size = sizeof(exename);
|
|
if (!QueryFullProcessImageNameA(GetCurrentProcess(), 0, exename,
|
|
&exename_size))
|
|
failure_perror("QueryFullProcessImageName()", GetLastError());
|
|
|
|
std::string cmdline = "test_mdbx.child " + thunk_param(config);
|
|
|
|
PROCESS_INFORMATION ProcessInformation;
|
|
if (!CreateProcessA(exename, const_cast<char *>(cmdline.c_str()),
|
|
NULL, // Retuned process handle is not inheritable.
|
|
NULL, // Retuned thread handle is not inheritable.
|
|
TRUE, // Child inherits all inheritable handles.
|
|
NORMAL_PRIORITY_CLASS | INHERIT_PARENT_AFFINITY,
|
|
NULL, // Inherit the parent's environment.
|
|
NULL, // Inherit the parent's current directory.
|
|
&StartupInfo, &ProcessInformation))
|
|
return GetLastError();
|
|
|
|
CloseHandle(ProcessInformation.hThread);
|
|
pid = ProcessInformation.dwProcessId;
|
|
childs[pid] = std::make_pair(ProcessInformation.hProcess, as_running);
|
|
return 0;
|
|
}
|
|
|
|
actor_status osal_actor_info(const mdbx_pid_t pid) {
|
|
actor_status status = childs.at(pid).second;
|
|
if (status > as_running)
|
|
return status;
|
|
|
|
DWORD ExitCode;
|
|
if (!GetExitCodeProcess(childs.at(pid).first, &ExitCode))
|
|
failure_perror("GetExitCodeProcess()", GetLastError());
|
|
|
|
switch (ExitCode) {
|
|
case STILL_ACTIVE:
|
|
return as_running;
|
|
case EXIT_SUCCESS:
|
|
status = as_successful;
|
|
break;
|
|
// case EXCEPTION_BREAKPOINT:
|
|
case EXCEPTION_SINGLE_STEP:
|
|
status = as_debuging;
|
|
break;
|
|
case STATUS_CONTROL_C_EXIT:
|
|
case EXCEPTION_NONCONTINUABLE_EXCEPTION:
|
|
status = as_killed;
|
|
break;
|
|
default:
|
|
status = as_failed;
|
|
break;
|
|
}
|
|
|
|
childs.at(pid).second = status;
|
|
return status;
|
|
}
|
|
|
|
void osal_killall_actors(void) {
|
|
for (auto &pair : childs)
|
|
TerminateProcess(pair.second.first, STATUS_CONTROL_C_EXIT);
|
|
}
|
|
|
|
int osal_actor_poll(mdbx_pid_t &pid, unsigned timeout) {
|
|
std::vector<HANDLE> handles;
|
|
handles.reserve(childs.size());
|
|
for (const auto &pair : childs)
|
|
if (pair.second.second <= as_running)
|
|
handles.push_back(pair.second.first);
|
|
|
|
DWORD rc =
|
|
MsgWaitForMultipleObjectsEx((DWORD)handles.size(), &handles[0],
|
|
(timeout > 60) ? 60 * 1000 : timeout * 1000,
|
|
QS_ALLINPUT | QS_ALLPOSTMESSAGE, 0);
|
|
|
|
if (rc >= WAIT_OBJECT_0 && rc < WAIT_OBJECT_0 + handles.size()) {
|
|
pid = 0;
|
|
for (const auto &pair : childs)
|
|
if (pair.second.first == handles[rc - WAIT_OBJECT_0]) {
|
|
pid = pair.first;
|
|
break;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
if (rc == WAIT_TIMEOUT) {
|
|
pid = 0;
|
|
return 0;
|
|
}
|
|
|
|
return waitstatus2errcode(rc);
|
|
}
|
|
|
|
void osal_yield(void) { SwitchToThread(); }
|
|
|
|
void osal_udelay(unsigned us) {
|
|
chrono::time until, now = chrono::now_motonic();
|
|
until.fixedpoint = now.fixedpoint + chrono::from_us(us).fixedpoint;
|
|
|
|
static unsigned threshold_us;
|
|
if (threshold_us == 0) {
|
|
ULONGLONG InterruptTimePrecise_100ns;
|
|
QueryInterruptTimePrecise(&InterruptTimePrecise_100ns);
|
|
threshold_us = InterruptTimePrecise_100ns / 5;
|
|
assert(threshold_us > 0);
|
|
}
|
|
|
|
do {
|
|
if (us > threshold_us && us > 1000) {
|
|
DWORD rc = SleepEx(us / 1000, TRUE);
|
|
if (rc)
|
|
failure_perror("SleepEx()", waitstatus2errcode(rc));
|
|
us = 0;
|
|
}
|
|
|
|
YieldProcessor();
|
|
now = chrono::now_motonic();
|
|
} while (now.fixedpoint < until.fixedpoint);
|
|
}
|