mirror of
https://github.com/decompals/wibo.git
synced 2025-12-13 15:16:27 +00:00
Implement async (overlapped) I/O with io_uring
This commit is contained in:
@@ -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);
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
53
dll/kernel32/overlapped_util.h
Normal file
53
dll/kernel32/overlapped_util.h
Normal 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
|
||||
@@ -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;
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
|
||||
Reference in New Issue
Block a user