summaryrefslogtreecommitdiff
path: root/libs/libmdbx/src/test/osal-windows.cc
diff options
context:
space:
mode:
authorGeorge Hazan <ghazan@miranda.im>2018-07-11 12:29:02 +0300
committerGeorge Hazan <ghazan@miranda.im>2018-07-11 12:29:10 +0300
commit92a5c27c4ea88f2f7c4810c789f7b5aebe06d03c (patch)
treee24f375b06c781077d22028a5d86f2d56585dc6c /libs/libmdbx/src/test/osal-windows.cc
parent09693d256f7a0ec0c24dce56f9aca87d3a01856d (diff)
libmdbx extracted to separate dll
Diffstat (limited to 'libs/libmdbx/src/test/osal-windows.cc')
-rw-r--r--libs/libmdbx/src/test/osal-windows.cc398
1 files changed, 398 insertions, 0 deletions
diff --git a/libs/libmdbx/src/test/osal-windows.cc b/libs/libmdbx/src/test/osal-windows.cc
new file mode 100644
index 0000000000..109c835a96
--- /dev/null
+++ b/libs/libmdbx/src/test/osal-windows.cc
@@ -0,0 +1,398 @@
+/*
+ * Copyright 2017-2018 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::unordered_map<unsigned, HANDLE> events;
+static HANDLE hBarrierSemaphore, hBarrierEvent;
+
+static int waitstatus2errcode(DWORD result) {
+ switch (result) {
+ case WAIT_OBJECT_0:
+ return MDBX_SUCCESS;
+ case WAIT_FAILED:
+ return GetLastError();
+ case WAIT_ABANDONED:
+ return ERROR_ABANDONED_WAIT_0;
+ case WAIT_IO_COMPLETION:
+ return ERROR_USER_APC;
+ case WAIT_TIMEOUT:
+ return ERROR_TIMEOUT;
+ default:
+ return ERROR_UNHANDLED_ERROR;
+ }
+}
+
+void osal_wait4barrier(void) {
+ 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 (unsigned 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 %" PRIuPTR " -> %p", i, hEvent);
+ events[i] = 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(actor_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[actor_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;
+
+static void ArgvQuote(std::string &CommandLine, const std::string &Argument,
+ bool Force = false)
+
+/*++
+
+https://blogs.msdn.microsoft.com/twistylittlepassagesallalike/2011/04/23/everyone-quotes-command-line-arguments-the-wrong-way/
+
+Routine Description:
+
+ This routine appends the given argument to a command line such
+ that CommandLineToArgvW will return the argument string unchanged.
+ Arguments in a command line should be separated by spaces; this
+ function does not add these spaces.
+
+Arguments:
+
+ Argument - Supplies the argument to encode.
+
+ CommandLine - Supplies the command line to which we append the encoded
+argument string.
+
+ Force - Supplies an indication of whether we should quote
+ the argument even if it does not contain any characters that would
+ ordinarily require quoting.
+
+Return Value:
+
+ None.
+
+Environment:
+
+ Arbitrary.
+
+--*/
+
+{
+ //
+ // Unless we're told otherwise, don't quote unless we actually
+ // need to do so --- hopefully avoid problems if programs won't
+ // parse quotes properly
+ //
+
+ if (Force == false && Argument.empty() == false &&
+ Argument.find_first_of(" \t\n\v\"") == Argument.npos) {
+ CommandLine.append(Argument);
+ } else {
+ CommandLine.push_back('"');
+
+ for (auto It = Argument.begin();; ++It) {
+ unsigned NumberBackslashes = 0;
+
+ while (It != Argument.end() && *It == '\\') {
+ ++It;
+ ++NumberBackslashes;
+ }
+
+ if (It == Argument.end()) {
+ //
+ // Escape all backslashes, but let the terminating
+ // double quotation mark we add below be interpreted
+ // as a metacharacter.
+ //
+ CommandLine.append(NumberBackslashes * 2, '\\');
+ break;
+ } else if (*It == L'"') {
+ //
+ // Escape all backslashes and the following
+ // double quotation mark.
+ //
+ CommandLine.append(NumberBackslashes * 2 + 1, '\\');
+ CommandLine.push_back(*It);
+ } else {
+ //
+ // Backslashes aren't special here.
+ //
+ CommandLine.append(NumberBackslashes, '\\');
+ CommandLine.push_back(*It);
+ }
+ }
+
+ CommandLine.push_back('"');
+ }
+}
+
+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 ";
+ ArgvQuote(cmdline, 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) {
+#if 1
+ unsigned timeslice_ms = 1;
+ while (timeBeginPeriod(timeslice_ms) == TIMERR_NOCANDO)
+ ++timeslice_ms;
+ threshold_us = timeslice_ms * 1500u;
+#else
+ ULONGLONG InterruptTimePrecise_100ns;
+ QueryInterruptTimePrecise(&InterruptTimePrecise_100ns);
+ threshold_us = InterruptTimePrecise_100ns / 5;
+#endif
+ 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);
+}
+
+bool osal_istty(int fd) { return _isatty(fd) != 0; }
+
+std::string osal_tempdir(void) {
+ char buf[MAX_PATH + 1];
+ DWORD len = GetTempPathA(sizeof(buf), buf);
+ return std::string(buf, len);
+}