Implement async (overlapped) I/O with io_uring

This commit is contained in:
2025-10-06 23:51:09 -06:00
parent f366e77956
commit df36de18bf
21 changed files with 909 additions and 143 deletions

View File

@@ -1,12 +1,14 @@
#include "fileapi.h"
#include "access.h"
#include "async_io.h"
#include "common.h"
#include "context.h"
#include "errors.h"
#include "files.h"
#include "handles.h"
#include "internal.h"
#include "overlapped_util.h"
#include "strutil.h"
#include "timeutil.h"
@@ -291,7 +293,7 @@ template <typename FindData> void populateFromStat(const FindSearchEntry &entry,
template <typename FindData> void populateFindData(const FindSearchEntry &entry, FindData &out) {
resetFindDataStruct(out);
std::string nativePath = entry.fullPath.empty() ? std::string() : entry.fullPath.u8string();
std::string nativePath = entry.fullPath.empty() ? std::string() : entry.fullPath.string();
struct stat st{};
if (!nativePath.empty() && stat(nativePath.c_str(), &st) == 0) {
populateFromStat(entry, st, out);
@@ -548,26 +550,6 @@ bool tryOpenConsoleDevice(DWORD dwDesiredAccess, DWORD dwShareMode, DWORD dwCrea
namespace kernel32 {
namespace {
void signalOverlappedEvent(OVERLAPPED *ov) {
if (ov && ov->hEvent) {
if (auto ev = wibo::handles().getAs<EventObject>(ov->hEvent)) {
ev->set();
}
}
}
void resetOverlappedEvent(OVERLAPPED *ov) {
if (ov && ov->hEvent) {
if (auto ev = wibo::handles().getAs<EventObject>(ov->hEvent)) {
ev->reset();
}
}
}
} // namespace
DWORD WIN_FUNC GetFileAttributesA(LPCSTR lpFileName) {
HOST_CONTEXT_GUARD();
if (!lpFileName) {
@@ -743,7 +725,27 @@ BOOL WIN_FUNC WriteFile(HANDLE hFile, LPCVOID lpBuffer, DWORD nNumberOfBytesToWr
lpOverlapped->Internal = STATUS_PENDING;
lpOverlapped->InternalHigh = 0;
updateFilePointer = !file->overlapped;
resetOverlappedEvent(lpOverlapped);
detail::resetOverlappedEvent(lpOverlapped);
if (file->overlapped) {
if (nNumberOfBytesToWrite == 0) {
lpOverlapped->Internal = STATUS_SUCCESS;
lpOverlapped->InternalHigh = 0;
detail::signalOverlappedEvent(lpOverlapped);
if (lpNumberOfBytesWritten) {
*lpNumberOfBytesWritten = 0;
}
return TRUE;
}
auto asyncFile = file.clone();
if (async_io::queueWrite(std::move(asyncFile), lpOverlapped, lpBuffer, nNumberOfBytesToWrite, offset,
file->isPipe)) {
if (lpNumberOfBytesWritten) {
*lpNumberOfBytesWritten = 0;
}
wibo::lastError = ERROR_IO_PENDING;
return FALSE;
}
}
}
auto io = files::write(file.get(), lpBuffer, nNumberOfBytesToWrite, offset, updateFilePointer);
@@ -762,7 +764,7 @@ BOOL WIN_FUNC WriteFile(HANDLE hFile, LPCVOID lpBuffer, DWORD nNumberOfBytesToWr
if (lpOverlapped != nullptr) {
lpOverlapped->Internal = completionStatus;
lpOverlapped->InternalHigh = io.bytesTransferred;
signalOverlappedEvent(lpOverlapped);
detail::signalOverlappedEvent(lpOverlapped);
}
return io.unixError == 0;
@@ -820,7 +822,25 @@ BOOL WIN_FUNC ReadFile(HANDLE hFile, LPVOID lpBuffer, DWORD nNumberOfBytesToRead
lpOverlapped->Internal = STATUS_PENDING;
lpOverlapped->InternalHigh = 0;
updateFilePointer = !file->overlapped;
resetOverlappedEvent(lpOverlapped);
detail::resetOverlappedEvent(lpOverlapped);
if (file->overlapped) {
if (nNumberOfBytesToRead == 0) {
lpOverlapped->Internal = STATUS_SUCCESS;
lpOverlapped->InternalHigh = 0;
detail::signalOverlappedEvent(lpOverlapped);
if (lpNumberOfBytesRead) {
*lpNumberOfBytesRead = 0;
}
return TRUE;
}
if (async_io::queueRead(file.clone(), lpOverlapped, lpBuffer, nNumberOfBytesToRead, offset, file->isPipe)) {
if (lpNumberOfBytesRead) {
*lpNumberOfBytesRead = 0;
}
wibo::lastError = ERROR_IO_PENDING;
return FALSE;
}
}
}
auto io = files::read(file.get(), lpBuffer, nNumberOfBytesToRead, offset, updateFilePointer);
@@ -829,17 +849,18 @@ BOOL WIN_FUNC ReadFile(HANDLE hFile, LPVOID lpBuffer, DWORD nNumberOfBytesToRead
completionStatus = wibo::statusFromErrno(io.unixError);
wibo::lastError = wibo::winErrorFromErrno(io.unixError);
} else if (io.reachedEnd && io.bytesTransferred == 0) {
completionStatus = STATUS_END_OF_FILE;
if (file->isPipe) {
completionStatus = STATUS_PIPE_BROKEN;
wibo::lastError = ERROR_BROKEN_PIPE;
if (lpOverlapped != nullptr) {
lpOverlapped->Internal = completionStatus;
lpOverlapped->InternalHigh = 0;
signalOverlappedEvent(lpOverlapped);
detail::signalOverlappedEvent(lpOverlapped);
}
DEBUG_LOG("-> ERROR_BROKEN_PIPE\n");
return FALSE;
}
completionStatus = STATUS_END_OF_FILE;
}
if (lpNumberOfBytesRead && (!file->overlapped || lpOverlapped == nullptr)) {
@@ -849,7 +870,7 @@ BOOL WIN_FUNC ReadFile(HANDLE hFile, LPVOID lpBuffer, DWORD nNumberOfBytesToRead
if (lpOverlapped != nullptr) {
lpOverlapped->Internal = completionStatus;
lpOverlapped->InternalHigh = io.bytesTransferred;
signalOverlappedEvent(lpOverlapped);
detail::signalOverlappedEvent(lpOverlapped);
}
DEBUG_LOG("-> %u bytes read, error %d\n", io.bytesTransferred, io.unixError == 0 ? 0 : wibo::lastError);

View File

@@ -98,7 +98,7 @@ struct MutexObject final : WaitableObject {
unsigned int recursionCount = 0;
bool abandoned = false; // Owner exited without releasing
MutexObject() : WaitableObject(kType) { signaled.store(true, std::memory_order_relaxed); }
MutexObject() : WaitableObject(kType) { signaled = true; }
void noteOwnerExit(/*pthread_t tid or emu tid*/) {
std::lock_guard lk(m);
@@ -106,8 +106,9 @@ struct MutexObject final : WaitableObject {
ownerValid = false;
recursionCount = 0;
abandoned = true;
signaled.store(true, std::memory_order_release);
signaled = true;
cv.notify_one();
notifyWaiters(true);
}
}
};
@@ -123,7 +124,7 @@ struct EventObject final : WaitableObject {
bool resetAll = false;
{
std::lock_guard lk(m);
signaled.store(true, std::memory_order_release);
signaled = true;
resetAll = manualReset;
}
if (resetAll) {
@@ -131,11 +132,12 @@ struct EventObject final : WaitableObject {
} else {
cv.notify_one();
}
notifyWaiters(false);
}
void reset() {
std::lock_guard lk(m);
signaled.store(false, std::memory_order_release);
signaled = false;
}
};

View File

@@ -2,6 +2,7 @@
#include "context.h"
#include "errors.h"
#include "overlapped_util.h"
#include "synchapi.h"
namespace kernel32 {
@@ -15,8 +16,11 @@ BOOL WIN_FUNC GetOverlappedResult(HANDLE hFile, LPOVERLAPPED lpOverlapped, LPDWO
wibo::lastError = ERROR_INVALID_PARAMETER;
return FALSE;
}
if (bWait && lpOverlapped->Internal == STATUS_PENDING && lpOverlapped->hEvent) {
WaitForSingleObject(lpOverlapped->hEvent, INFINITE);
if (bWait && lpOverlapped->Internal == STATUS_PENDING &&
kernel32::detail::shouldSignalOverlappedEvent(lpOverlapped)) {
if (HANDLE waitHandle = kernel32::detail::normalizedOverlappedEventHandle(lpOverlapped)) {
WaitForSingleObject(waitHandle, INFINITE);
}
}
const auto status = static_cast<NTSTATUS>(lpOverlapped->Internal);
@@ -29,15 +33,11 @@ BOOL WIN_FUNC GetOverlappedResult(HANDLE hFile, LPOVERLAPPED lpOverlapped, LPDWO
*lpNumberOfBytesTransferred = static_cast<DWORD>(lpOverlapped->InternalHigh);
}
if (status == STATUS_SUCCESS) {
DWORD error = wibo::winErrorFromNtStatus(status);
if (error == ERROR_SUCCESS) {
return TRUE;
}
if (status == STATUS_END_OF_FILE) {
wibo::lastError = ERROR_HANDLE_EOF;
return FALSE;
}
wibo::lastError = status;
wibo::lastError = error;
return FALSE;
}

View File

@@ -0,0 +1,53 @@
#pragma once
#include "context.h"
#include "handles.h"
#include "internal.h"
#include "minwinbase.h"
#include <cstdint>
namespace kernel32::detail {
inline bool shouldSignalOverlappedEvent(const OVERLAPPED *ov) {
if (!ov) {
return false;
}
auto raw = reinterpret_cast<uintptr_t>(ov->hEvent);
return (raw & 1U) == 0 && raw != 0;
}
inline HANDLE normalizedOverlappedEventHandle(const OVERLAPPED *ov) {
if (!ov) {
return nullptr;
}
auto raw = reinterpret_cast<uintptr_t>(ov->hEvent);
raw &= ~static_cast<uintptr_t>(1);
return reinterpret_cast<HANDLE>(raw);
}
inline void signalOverlappedEvent(OVERLAPPED *ov) {
if (!shouldSignalOverlappedEvent(ov)) {
return;
}
HANDLE handle = normalizedOverlappedEventHandle(ov);
if (handle) {
if (auto ev = wibo::handles().getAs<EventObject>(handle)) {
ev->set();
}
}
}
inline void resetOverlappedEvent(OVERLAPPED *ov) {
if (!ov) {
return;
}
HANDLE handle = normalizedOverlappedEventHandle(ov);
if (handle) {
if (auto ev = wibo::handles().getAs<EventObject>(handle)) {
ev->reset();
}
}
}
} // namespace kernel32::detail

View File

@@ -106,7 +106,7 @@ void threadCleanup(void *param) {
}
{
std::lock_guard lk(obj->m);
obj->signaled.store(true, std::memory_order_release);
obj->signaled = true;
// Exit code set before pthread_exit
}
g_currentThreadObject = nullptr;
@@ -114,6 +114,7 @@ void threadCleanup(void *param) {
wibo::setThreadTibForHost(nullptr);
// TODO: mark mutexes owned by this thread as abandoned
obj->cv.notify_all();
obj->notifyWaiters(false);
detail::deref(obj);
}
@@ -328,10 +329,10 @@ BOOL WIN_FUNC TerminateProcess(HANDLE hProcess, UINT uExitCode) {
wibo::lastError = ERROR_INVALID_HANDLE;
return FALSE;
}
if (process->signaled.load(std::memory_order_acquire)) {
std::lock_guard lk(process->m);
if (process->signaled) {
return TRUE;
}
std::lock_guard lk(process->m);
if (syscall(SYS_pidfd_send_signal, process->pidfd, SIGKILL, nullptr, 0) != 0) {
int err = errno;
DEBUG_LOG("TerminateProcess: pidfd_send_signal(%d) failed: %s\n", process->pidfd, strerror(err));
@@ -368,8 +369,8 @@ BOOL WIN_FUNC GetExitCodeProcess(HANDLE hProcess, LPDWORD lpExitCode) {
return FALSE;
}
DWORD exitCode = STILL_ACTIVE;
if (process->signaled.load(std::memory_order_acquire)) {
std::lock_guard lk(process->m);
std::lock_guard lk(process->m);
if (process->signaled) {
exitCode = process->exitCode;
}
*lpExitCode = exitCode;

View File

@@ -7,9 +7,12 @@
#include "internal.h"
#include "strutil.h"
#include <algorithm>
#include <chrono>
#include <cstring>
#include <limits>
#include <mutex>
#include <optional>
#include <pthread.h>
#include <string>
#include <sys/wait.h>
@@ -34,6 +37,93 @@ void makeWideNameFromAnsi(LPCSTR ansiName, std::vector<uint16_t> &outWide) {
outWide = stringToWideString(ansiName);
}
struct WaitBlock {
explicit WaitBlock(bool waitAllIn, DWORD count) : waitAll(waitAllIn != FALSE), satisfied(count, false) {}
static void notify(void *context, WaitableObject *obj, DWORD index, bool abandoned) {
auto *self = static_cast<WaitBlock *>(context);
if (self) {
self->handleSignal(obj, index, abandoned, true);
}
}
void noteInitial(WaitableObject *obj, DWORD index, bool abandoned) { handleSignal(obj, index, abandoned, false); }
bool isCompleted(DWORD &outResult) {
std::lock_guard lk(mutex);
if (!completed) {
return false;
}
outResult = result;
return true;
}
bool waitUntil(const std::optional<std::chrono::steady_clock::time_point> &deadline, DWORD &outResult) {
std::unique_lock lk(mutex);
if (!completed) {
if (deadline) {
if (!cv.wait_until(lk, *deadline, [&] { return completed; })) {
return false;
}
} else {
cv.wait(lk, [&] { return completed; });
}
}
outResult = result;
return true;
}
void handleSignal(WaitableObject *obj, DWORD index, bool abandoned, bool fromWaiter) {
if (!obj) {
return;
}
bool notify = false;
{
std::lock_guard lk(mutex);
if (index >= satisfied.size()) {
return;
}
if (satisfied[index]) {
// Already satisfied; nothing to do aside from cleanup below.
} else if (!completed) {
satisfied[index] = true;
if (waitAll) {
if (abandoned) {
result = WAIT_ABANDONED + index;
completed = true;
notify = true;
} else if (std::all_of(satisfied.begin(), satisfied.end(), [](bool v) { return v; })) {
result = WAIT_OBJECT_0;
completed = true;
notify = true;
}
} else {
result = abandoned ? (WAIT_ABANDONED + index) : (WAIT_OBJECT_0 + index);
completed = true;
notify = true;
}
}
}
// Always unregister once we've observed a signal for this waiter.
if (fromWaiter) {
obj->unregisterWaiter(this);
} else if (!waitAll || satisfied[index]) {
// Initial state satisfaction can drop registration immediately.
obj->unregisterWaiter(this);
}
if (notify) {
cv.notify_all();
}
}
const bool waitAll;
std::vector<bool> satisfied;
bool completed = false;
DWORD result = WAIT_TIMEOUT;
std::mutex mutex;
std::condition_variable cv;
};
} // namespace
namespace kernel32 {
@@ -61,7 +151,7 @@ HANDLE WIN_FUNC CreateMutexW(LPSECURITY_ATTRIBUTES lpMutexAttributes, BOOL bInit
mu->owner = pthread_self();
mu->ownerValid = true;
mu->recursionCount = 1;
mu->signaled.store(false, std::memory_order_release);
mu->signaled = false;
}
return mu;
});
@@ -102,12 +192,13 @@ BOOL WIN_FUNC ReleaseMutex(HANDLE hMutex) {
}
if (--mu->recursionCount == 0) {
mu->ownerValid = false;
mu->signaled.store(true, std::memory_order_release);
mu->signaled = true;
notify = true;
}
}
if (notify) {
mu->cv.notify_one();
mu->notifyWaiters(false);
}
return TRUE;
}
@@ -125,7 +216,7 @@ HANDLE WIN_FUNC CreateEventW(LPSECURITY_ATTRIBUTES lpEventAttributes, BOOL bManu
}
auto [ev, created] = wibo::g_namespace.getOrCreate(name, [&]() {
auto e = new EventObject(bManualReset);
e->signaled.store(bInitialState, std::memory_order_relaxed);
e->signaled = bInitialState;
return e;
});
if (!ev) {
@@ -200,6 +291,7 @@ BOOL WIN_FUNC ReleaseSemaphore(HANDLE hSemaphore, LONG lReleaseCount, PLONG lpPr
}
LONG prev = 0;
bool shouldNotifyWaitBlocks = false;
{
std::lock_guard lk(sem->m);
if (lpPreviousCount) {
@@ -210,11 +302,15 @@ BOOL WIN_FUNC ReleaseSemaphore(HANDLE hSemaphore, LONG lReleaseCount, PLONG lpPr
return FALSE;
}
sem->count += lReleaseCount;
sem->signaled.store(sem->count > 0, std::memory_order_release);
sem->signaled = sem->count > 0;
shouldNotifyWaitBlocks = sem->count > 0;
}
for (LONG i = 0; i < lReleaseCount; ++i) {
sem->cv.notify_one();
}
if (shouldNotifyWaitBlocks) {
sem->notifyWaiters(false);
}
if (lpPreviousCount) {
*lpPreviousCount = prev;
@@ -279,12 +375,12 @@ DWORD WIN_FUNC WaitForSingleObject(HANDLE hHandle, DWORD dwMilliseconds) {
case ObjectType::Event: {
auto ev = std::move(obj).downcast<EventObject>();
std::unique_lock lk(ev->m);
bool ok = doWait(lk, ev->cv, [&] { return ev->signaled.load(std::memory_order_acquire); });
bool ok = doWait(lk, ev->cv, [&] { return ev->signaled; });
if (!ok) {
return WAIT_TIMEOUT;
}
if (!ev->manualReset) {
ev->signaled.store(false, std::memory_order_release);
ev->signaled = false;
}
return WAIT_OBJECT_0;
}
@@ -297,7 +393,7 @@ DWORD WIN_FUNC WaitForSingleObject(HANDLE hHandle, DWORD dwMilliseconds) {
}
--sem->count;
if (sem->count == 0) {
sem->signaled.store(false, std::memory_order_release);
sem->signaled = false;
}
return WAIT_OBJECT_0;
}
@@ -322,7 +418,7 @@ DWORD WIN_FUNC WaitForSingleObject(HANDLE hHandle, DWORD dwMilliseconds) {
mu->owner = self;
mu->ownerValid = true;
mu->recursionCount = 1;
mu->signaled.store(false, std::memory_order_release);
mu->signaled = false;
return ret;
}
case ObjectType::Thread: {
@@ -333,7 +429,7 @@ DWORD WIN_FUNC WaitForSingleObject(HANDLE hHandle, DWORD dwMilliseconds) {
// Windows actually allows you to wait on your own thread, but why bother?
return WAIT_TIMEOUT;
}
bool ok = doWait(lk, th->cv, [&] { return th->signaled.load(std::memory_order_acquire); });
bool ok = doWait(lk, th->cv, [&] { return th->signaled; });
return ok ? WAIT_OBJECT_0 : WAIT_TIMEOUT;
}
case ObjectType::Process: {
@@ -343,7 +439,7 @@ DWORD WIN_FUNC WaitForSingleObject(HANDLE hHandle, DWORD dwMilliseconds) {
// Windows actually allows you to wait on your own process, but why bother?
return WAIT_TIMEOUT;
}
bool ok = doWait(lk, po->cv, [&] { return po->signaled.load(std::memory_order_acquire); });
bool ok = doWait(lk, po->cv, [&] { return po->signaled; });
return ok ? WAIT_OBJECT_0 : WAIT_TIMEOUT;
}
default:
@@ -352,6 +448,101 @@ DWORD WIN_FUNC WaitForSingleObject(HANDLE hHandle, DWORD dwMilliseconds) {
}
}
DWORD WIN_FUNC WaitForMultipleObjects(DWORD nCount, const HANDLE *lpHandles, BOOL bWaitAll, DWORD dwMilliseconds) {
HOST_CONTEXT_GUARD();
DEBUG_LOG("WaitForMultipleObjects(%u, %p, %d, %u)\n", nCount, lpHandles, static_cast<int>(bWaitAll),
dwMilliseconds);
if (nCount == 0 || nCount > MAXIMUM_WAIT_OBJECTS || !lpHandles) {
wibo::lastError = ERROR_INVALID_PARAMETER;
return WAIT_FAILED;
}
std::vector<Pin<WaitableObject>> objects(nCount);
for (DWORD i = 0; i < nCount; ++i) {
HandleMeta meta{};
auto obj = wibo::handles().getAs<WaitableObject>(lpHandles[i], &meta);
if (!obj) {
wibo::lastError = ERROR_INVALID_HANDLE;
return WAIT_FAILED;
}
objects[i] = std::move(obj);
}
WaitBlock block(bWaitAll, nCount);
for (DWORD i = 0; i < objects.size(); ++i) {
objects[i]->registerWaiter(&block, i, &WaitBlock::notify);
}
for (DWORD i = 0; i < objects.size(); ++i) {
auto *obj = objects[i].get();
bool isSignaled = obj->signaled;
bool isAbandoned = false;
if (auto *mu = detail::castTo<MutexObject>(obj)) {
isAbandoned = mu->abandoned;
}
if (isSignaled) {
block.noteInitial(obj, i, isAbandoned);
}
}
DWORD waitResult = WAIT_TIMEOUT;
if (!block.isCompleted(waitResult)) {
if (dwMilliseconds == 0) {
waitResult = WAIT_TIMEOUT;
} else {
std::optional<std::chrono::steady_clock::time_point> deadline;
if (dwMilliseconds != INFINITE) {
deadline =
std::chrono::steady_clock::now() + std::chrono::milliseconds(static_cast<uint64_t>(dwMilliseconds));
}
DWORD signaledResult = WAIT_TIMEOUT;
bool completed = block.waitUntil(deadline, signaledResult);
if (completed) {
waitResult = signaledResult;
} else {
waitResult = WAIT_TIMEOUT;
}
}
}
for (const auto &object : objects) {
object->unregisterWaiter(&block);
}
if (waitResult == WAIT_TIMEOUT) {
return WAIT_TIMEOUT;
}
if (waitResult == WAIT_FAILED) {
return WAIT_FAILED;
}
auto consume = [&](DWORD index) {
if (index < nCount) {
WaitForSingleObject(lpHandles[index], 0);
}
};
if (bWaitAll) {
if (waitResult == WAIT_OBJECT_0) {
for (DWORD i = 0; i < nCount; ++i) {
consume(i);
}
} else if (waitResult >= WAIT_ABANDONED && waitResult < WAIT_ABANDONED + nCount) {
consume(waitResult - WAIT_ABANDONED);
}
} else {
if (waitResult >= WAIT_OBJECT_0 && waitResult < WAIT_OBJECT_0 + nCount) {
consume(waitResult - WAIT_OBJECT_0);
} else if (waitResult >= WAIT_ABANDONED && waitResult < WAIT_ABANDONED + nCount) {
consume(waitResult - WAIT_ABANDONED);
}
}
return waitResult;
}
void WIN_FUNC InitializeCriticalSection(LPCRITICAL_SECTION lpCriticalSection) {
HOST_CONTEXT_GUARD();
VERBOSE_LOG("STUB: InitializeCriticalSection(%p)\n", lpCriticalSection);

View File

@@ -8,6 +8,7 @@ constexpr DWORD WAIT_ABANDONED = 0x00000080;
constexpr DWORD WAIT_TIMEOUT = 0x00000102;
constexpr DWORD WAIT_FAILED = 0xFFFFFFFF;
constexpr DWORD INFINITE = 0xFFFFFFFF;
constexpr DWORD MAXIMUM_WAIT_OBJECTS = 64;
constexpr DWORD INIT_ONCE_CHECK_ONLY = 0x00000001UL;
constexpr DWORD INIT_ONCE_ASYNC = 0x00000002UL;
@@ -89,6 +90,7 @@ HANDLE WIN_FUNC CreateSemaphoreW(LPSECURITY_ATTRIBUTES lpSemaphoreAttributes, LO
LPCWSTR lpName);
BOOL WIN_FUNC ReleaseSemaphore(HANDLE hSemaphore, LONG lReleaseCount, PLONG lpPreviousCount);
DWORD WIN_FUNC WaitForSingleObject(HANDLE hHandle, DWORD dwMilliseconds);
DWORD WIN_FUNC WaitForMultipleObjects(DWORD nCount, const HANDLE *lpHandles, BOOL bWaitAll, DWORD dwMilliseconds);
void WIN_FUNC InitializeCriticalSection(LPCRITICAL_SECTION lpCriticalSection);
BOOL WIN_FUNC InitializeCriticalSectionEx(LPCRITICAL_SECTION lpCriticalSection, DWORD dwSpinCount, DWORD Flags);
BOOL WIN_FUNC InitializeCriticalSectionAndSpinCount(LPCRITICAL_SECTION lpCriticalSection, DWORD dwSpinCount);