mirror of
https://github.com/decompals/wibo.git
synced 2025-12-13 23:26:17 +00:00
Fix GetOverlappedResult without event & improve overlapped handling
This commit is contained in:
@@ -729,9 +729,7 @@ BOOL WIN_FUNC WriteFile(HANDLE hFile, LPCVOID lpBuffer, DWORD nNumberOfBytesToWr
|
|||||||
detail::resetOverlappedEvent(lpOverlapped);
|
detail::resetOverlappedEvent(lpOverlapped);
|
||||||
if (file->overlapped) {
|
if (file->overlapped) {
|
||||||
if (nNumberOfBytesToWrite == 0) {
|
if (nNumberOfBytesToWrite == 0) {
|
||||||
lpOverlapped->Internal = STATUS_SUCCESS;
|
detail::signalOverlappedEvent(file.get(), lpOverlapped, STATUS_SUCCESS, 0);
|
||||||
lpOverlapped->InternalHigh = 0;
|
|
||||||
detail::signalOverlappedEvent(lpOverlapped);
|
|
||||||
if (lpNumberOfBytesWritten) {
|
if (lpNumberOfBytesWritten) {
|
||||||
*lpNumberOfBytesWritten = 0;
|
*lpNumberOfBytesWritten = 0;
|
||||||
}
|
}
|
||||||
@@ -749,7 +747,7 @@ BOOL WIN_FUNC WriteFile(HANDLE hFile, LPCVOID lpBuffer, DWORD nNumberOfBytesToWr
|
|||||||
}
|
}
|
||||||
|
|
||||||
auto io = files::write(file.get(), lpBuffer, nNumberOfBytesToWrite, offset, updateFilePointer);
|
auto io = files::write(file.get(), lpBuffer, nNumberOfBytesToWrite, offset, updateFilePointer);
|
||||||
DWORD completionStatus = STATUS_SUCCESS;
|
NTSTATUS completionStatus = STATUS_SUCCESS;
|
||||||
if (io.unixError != 0) {
|
if (io.unixError != 0) {
|
||||||
completionStatus = wibo::statusFromErrno(io.unixError);
|
completionStatus = wibo::statusFromErrno(io.unixError);
|
||||||
wibo::lastError = wibo::winErrorFromErrno(io.unixError);
|
wibo::lastError = wibo::winErrorFromErrno(io.unixError);
|
||||||
@@ -761,11 +759,7 @@ BOOL WIN_FUNC WriteFile(HANDLE hFile, LPCVOID lpBuffer, DWORD nNumberOfBytesToWr
|
|||||||
*lpNumberOfBytesWritten = static_cast<DWORD>(io.bytesTransferred);
|
*lpNumberOfBytesWritten = static_cast<DWORD>(io.bytesTransferred);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (lpOverlapped != nullptr) {
|
detail::signalOverlappedEvent(file.get(), lpOverlapped, completionStatus, io.bytesTransferred);
|
||||||
lpOverlapped->Internal = completionStatus;
|
|
||||||
lpOverlapped->InternalHigh = io.bytesTransferred;
|
|
||||||
detail::signalOverlappedEvent(lpOverlapped);
|
|
||||||
}
|
|
||||||
|
|
||||||
return io.unixError == 0;
|
return io.unixError == 0;
|
||||||
}
|
}
|
||||||
@@ -825,9 +819,7 @@ BOOL WIN_FUNC ReadFile(HANDLE hFile, LPVOID lpBuffer, DWORD nNumberOfBytesToRead
|
|||||||
detail::resetOverlappedEvent(lpOverlapped);
|
detail::resetOverlappedEvent(lpOverlapped);
|
||||||
if (file->overlapped) {
|
if (file->overlapped) {
|
||||||
if (nNumberOfBytesToRead == 0) {
|
if (nNumberOfBytesToRead == 0) {
|
||||||
lpOverlapped->Internal = STATUS_SUCCESS;
|
detail::signalOverlappedEvent(file.get(), lpOverlapped, STATUS_SUCCESS, 0);
|
||||||
lpOverlapped->InternalHigh = 0;
|
|
||||||
detail::signalOverlappedEvent(lpOverlapped);
|
|
||||||
if (lpNumberOfBytesRead) {
|
if (lpNumberOfBytesRead) {
|
||||||
*lpNumberOfBytesRead = 0;
|
*lpNumberOfBytesRead = 0;
|
||||||
}
|
}
|
||||||
@@ -845,7 +837,7 @@ BOOL WIN_FUNC ReadFile(HANDLE hFile, LPVOID lpBuffer, DWORD nNumberOfBytesToRead
|
|||||||
}
|
}
|
||||||
|
|
||||||
auto io = files::read(file.get(), lpBuffer, nNumberOfBytesToRead, offset, updateFilePointer);
|
auto io = files::read(file.get(), lpBuffer, nNumberOfBytesToRead, offset, updateFilePointer);
|
||||||
DWORD completionStatus = STATUS_SUCCESS;
|
NTSTATUS completionStatus = STATUS_SUCCESS;
|
||||||
if (io.unixError != 0) {
|
if (io.unixError != 0) {
|
||||||
completionStatus = wibo::statusFromErrno(io.unixError);
|
completionStatus = wibo::statusFromErrno(io.unixError);
|
||||||
wibo::lastError = wibo::winErrorFromErrno(io.unixError);
|
wibo::lastError = wibo::winErrorFromErrno(io.unixError);
|
||||||
@@ -853,11 +845,7 @@ BOOL WIN_FUNC ReadFile(HANDLE hFile, LPVOID lpBuffer, DWORD nNumberOfBytesToRead
|
|||||||
if (file->isPipe) {
|
if (file->isPipe) {
|
||||||
completionStatus = STATUS_PIPE_BROKEN;
|
completionStatus = STATUS_PIPE_BROKEN;
|
||||||
wibo::lastError = ERROR_BROKEN_PIPE;
|
wibo::lastError = ERROR_BROKEN_PIPE;
|
||||||
if (lpOverlapped != nullptr) {
|
detail::signalOverlappedEvent(file.get(), lpOverlapped, completionStatus, 0);
|
||||||
lpOverlapped->Internal = completionStatus;
|
|
||||||
lpOverlapped->InternalHigh = 0;
|
|
||||||
detail::signalOverlappedEvent(lpOverlapped);
|
|
||||||
}
|
|
||||||
DEBUG_LOG("-> ERROR_BROKEN_PIPE\n");
|
DEBUG_LOG("-> ERROR_BROKEN_PIPE\n");
|
||||||
return FALSE;
|
return FALSE;
|
||||||
}
|
}
|
||||||
@@ -868,11 +856,7 @@ BOOL WIN_FUNC ReadFile(HANDLE hFile, LPVOID lpBuffer, DWORD nNumberOfBytesToRead
|
|||||||
*lpNumberOfBytesRead = static_cast<DWORD>(io.bytesTransferred);
|
*lpNumberOfBytesRead = static_cast<DWORD>(io.bytesTransferred);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (lpOverlapped != nullptr) {
|
detail::signalOverlappedEvent(file.get(), lpOverlapped, completionStatus, io.bytesTransferred);
|
||||||
lpOverlapped->Internal = completionStatus;
|
|
||||||
lpOverlapped->InternalHigh = io.bytesTransferred;
|
|
||||||
detail::signalOverlappedEvent(lpOverlapped);
|
|
||||||
}
|
|
||||||
|
|
||||||
DEBUG_LOG("-> %u bytes read, error %d\n", io.bytesTransferred, io.unixError == 0 ? 0 : wibo::lastError);
|
DEBUG_LOG("-> %u bytes read, error %d\n", io.bytesTransferred, io.unixError == 0 ? 0 : wibo::lastError);
|
||||||
return io.unixError == 0;
|
return io.unixError == 0;
|
||||||
|
|||||||
@@ -4,6 +4,7 @@
|
|||||||
#include "handles.h"
|
#include "handles.h"
|
||||||
#include "mimalloc.h"
|
#include "mimalloc.h"
|
||||||
|
|
||||||
|
#include <condition_variable>
|
||||||
#include <pthread.h>
|
#include <pthread.h>
|
||||||
|
|
||||||
namespace kernel32 {
|
namespace kernel32 {
|
||||||
@@ -30,6 +31,8 @@ struct FileObject : FsObject {
|
|||||||
bool overlapped = false;
|
bool overlapped = false;
|
||||||
bool appendOnly = false;
|
bool appendOnly = false;
|
||||||
bool isPipe = false;
|
bool isPipe = false;
|
||||||
|
// Used to notify overlapped operations without an event handle
|
||||||
|
std::condition_variable overlappedCv;
|
||||||
|
|
||||||
explicit FileObject(int fd) : FsObject(kType, fd) {
|
explicit FileObject(int fd) : FsObject(kType, fd) {
|
||||||
if (fd >= 0) {
|
if (fd >= 0) {
|
||||||
@@ -99,18 +102,6 @@ struct MutexObject final : WaitableObject {
|
|||||||
bool abandoned = false; // Owner exited without releasing
|
bool abandoned = false; // Owner exited without releasing
|
||||||
|
|
||||||
MutexObject() : WaitableObject(kType) { signaled = true; }
|
MutexObject() : WaitableObject(kType) { signaled = true; }
|
||||||
|
|
||||||
void noteOwnerExit(/*pthread_t tid or emu tid*/) {
|
|
||||||
std::lock_guard lk(m);
|
|
||||||
if (ownerValid /* && matches tid if you store emu id */) {
|
|
||||||
ownerValid = false;
|
|
||||||
recursionCount = 0;
|
|
||||||
abandoned = true;
|
|
||||||
signaled = true;
|
|
||||||
cv.notify_one();
|
|
||||||
notifyWaiters(true);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
struct EventObject final : WaitableObject {
|
struct EventObject final : WaitableObject {
|
||||||
|
|||||||
@@ -2,24 +2,32 @@
|
|||||||
|
|
||||||
#include "context.h"
|
#include "context.h"
|
||||||
#include "errors.h"
|
#include "errors.h"
|
||||||
|
#include "internal.h"
|
||||||
#include "overlapped_util.h"
|
#include "overlapped_util.h"
|
||||||
#include "synchapi.h"
|
#include "synchapi.h"
|
||||||
|
|
||||||
|
#include <mutex>
|
||||||
|
|
||||||
namespace kernel32 {
|
namespace kernel32 {
|
||||||
|
|
||||||
BOOL WIN_FUNC GetOverlappedResult(HANDLE hFile, LPOVERLAPPED lpOverlapped, LPDWORD lpNumberOfBytesTransferred,
|
BOOL WIN_FUNC GetOverlappedResult(HANDLE hFile, LPOVERLAPPED lpOverlapped, LPDWORD lpNumberOfBytesTransferred,
|
||||||
BOOL bWait) {
|
BOOL bWait) {
|
||||||
HOST_CONTEXT_GUARD();
|
HOST_CONTEXT_GUARD();
|
||||||
DEBUG_LOG("GetOverlappedResult(%p, %p, %p, %d)\n", hFile, lpOverlapped, lpNumberOfBytesTransferred, bWait);
|
DEBUG_LOG("GetOverlappedResult(%p, %p, %p, %d)\n", hFile, lpOverlapped, lpNumberOfBytesTransferred, bWait);
|
||||||
(void)hFile;
|
|
||||||
if (!lpOverlapped) {
|
if (!lpOverlapped) {
|
||||||
wibo::lastError = ERROR_INVALID_PARAMETER;
|
wibo::lastError = ERROR_INVALID_PARAMETER;
|
||||||
return FALSE;
|
return FALSE;
|
||||||
}
|
}
|
||||||
if (bWait && lpOverlapped->Internal == STATUS_PENDING &&
|
|
||||||
kernel32::detail::shouldSignalOverlappedEvent(lpOverlapped)) {
|
if (bWait && lpOverlapped->Internal == STATUS_PENDING) {
|
||||||
if (HANDLE waitHandle = kernel32::detail::normalizedOverlappedEventHandle(lpOverlapped)) {
|
if (HANDLE waitHandle = kernel32::detail::normalizedOverlappedEventHandle(lpOverlapped)) {
|
||||||
WaitForSingleObject(waitHandle, INFINITE);
|
WaitForSingleObject(waitHandle, INFINITE);
|
||||||
|
} else if (auto file = wibo::handles().getAs<FileObject>(hFile)) {
|
||||||
|
std::unique_lock lk(file->m);
|
||||||
|
file->overlappedCv.wait(lk, [&] { return lpOverlapped->Internal != STATUS_PENDING; });
|
||||||
|
} else {
|
||||||
|
wibo::lastError = ERROR_INVALID_HANDLE;
|
||||||
|
return FALSE;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -25,7 +25,7 @@ namespace kernel32 {
|
|||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
|
|
||||||
class NamedPipeInstance;
|
struct NamedPipeInstance;
|
||||||
|
|
||||||
void configureInheritability(int fd, bool inherit) {
|
void configureInheritability(int fd, bool inherit) {
|
||||||
if (fd < 0) {
|
if (fd < 0) {
|
||||||
@@ -112,17 +112,17 @@ struct NamedPipeState : ObjectBase {
|
|||||||
DWORD defaultTimeout = 0;
|
DWORD defaultTimeout = 0;
|
||||||
DWORD maxInstances = PIPE_UNLIMITED_INSTANCES;
|
DWORD maxInstances = PIPE_UNLIMITED_INSTANCES;
|
||||||
uint32_t instanceCount = 0;
|
uint32_t instanceCount = 0;
|
||||||
std::vector<class NamedPipeInstance *> instances;
|
std::vector<NamedPipeInstance *> instances;
|
||||||
|
|
||||||
explicit NamedPipeState(std::string k) : ObjectBase(kType), key(std::move(k)) {}
|
explicit NamedPipeState(std::string k) : ObjectBase(kType), key(std::move(k)) {}
|
||||||
~NamedPipeState() override { wibo::g_namespace.remove(this); }
|
~NamedPipeState() override { wibo::g_namespace.remove(this); }
|
||||||
|
|
||||||
void registerInstance(class NamedPipeInstance *inst) {
|
void registerInstance(NamedPipeInstance *inst) {
|
||||||
std::lock_guard lk(mutex);
|
std::lock_guard lk(mutex);
|
||||||
instances.push_back(inst);
|
instances.push_back(inst);
|
||||||
}
|
}
|
||||||
|
|
||||||
void unregisterInstance(class NamedPipeInstance *inst) {
|
void unregisterInstance(NamedPipeInstance *inst) {
|
||||||
std::lock_guard lk(mutex);
|
std::lock_guard lk(mutex);
|
||||||
auto it = std::find(instances.begin(), instances.end(), inst);
|
auto it = std::find(instances.begin(), instances.end(), inst);
|
||||||
if (it != instances.end()) {
|
if (it != instances.end()) {
|
||||||
@@ -194,12 +194,8 @@ struct NamedPipeInstance final : FileObject {
|
|||||||
std::lock_guard lk(connectMutex);
|
std::lock_guard lk(connectMutex);
|
||||||
localCompanion = companionFd;
|
localCompanion = companionFd;
|
||||||
companionFd = -1;
|
companionFd = -1;
|
||||||
if (pendingOverlapped) {
|
kernel32::detail::signalOverlappedEvent(this, pendingOverlapped, STATUS_PIPE_BROKEN, 0);
|
||||||
pendingOverlapped->Internal = STATUS_PIPE_BROKEN;
|
|
||||||
pendingOverlapped->InternalHigh = 0;
|
|
||||||
kernel32::detail::signalOverlappedEvent(pendingOverlapped);
|
|
||||||
pendingOverlapped = nullptr;
|
pendingOverlapped = nullptr;
|
||||||
}
|
|
||||||
connectPending = false;
|
connectPending = false;
|
||||||
connectCv.notify_all();
|
connectCv.notify_all();
|
||||||
}
|
}
|
||||||
@@ -208,8 +204,6 @@ struct NamedPipeInstance final : FileObject {
|
|||||||
}
|
}
|
||||||
if (state) {
|
if (state) {
|
||||||
state->unregisterInstance(this);
|
state->unregisterInstance(this);
|
||||||
}
|
|
||||||
if (state) {
|
|
||||||
state->releaseInstance();
|
state->releaseInstance();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -233,24 +227,19 @@ struct NamedPipeInstance final : FileObject {
|
|||||||
}
|
}
|
||||||
|
|
||||||
int takeCompanion() {
|
int takeCompanion() {
|
||||||
std::unique_lock lk(connectMutex);
|
std::lock_guard lk(connectMutex);
|
||||||
if (companionFd < 0 || clientConnected) {
|
if (companionFd < 0 || clientConnected) {
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
int fd = companionFd;
|
int fd = companionFd;
|
||||||
companionFd = -1;
|
companionFd = -1;
|
||||||
clientConnected = true;
|
clientConnected = true;
|
||||||
if (pendingOverlapped) {
|
kernel32::detail::signalOverlappedEvent(this, pendingOverlapped, STATUS_SUCCESS, 0);
|
||||||
pendingOverlapped->Internal = STATUS_SUCCESS;
|
|
||||||
pendingOverlapped->InternalHigh = 0;
|
|
||||||
kernel32::detail::signalOverlappedEvent(pendingOverlapped);
|
|
||||||
pendingOverlapped = nullptr;
|
pendingOverlapped = nullptr;
|
||||||
}
|
|
||||||
if (connectPending) {
|
if (connectPending) {
|
||||||
connectPending = false;
|
connectPending = false;
|
||||||
connectCv.notify_all();
|
connectCv.notify_all();
|
||||||
}
|
}
|
||||||
lk.unlock();
|
|
||||||
return fd;
|
return fd;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include "context.h"
|
#include "errors.h"
|
||||||
#include "handles.h"
|
#include "handles.h"
|
||||||
#include "internal.h"
|
#include "internal.h"
|
||||||
#include "minwinbase.h"
|
#include "minwinbase.h"
|
||||||
@@ -9,41 +9,30 @@
|
|||||||
|
|
||||||
namespace kernel32::detail {
|
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) {
|
inline HANDLE normalizedOverlappedEventHandle(const OVERLAPPED *ov) {
|
||||||
if (!ov) {
|
if (!ov || (reinterpret_cast<uintptr_t>(ov->hEvent) & 1U) != 0) {
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
auto raw = reinterpret_cast<uintptr_t>(ov->hEvent);
|
return ov->hEvent;
|
||||||
raw &= ~static_cast<uintptr_t>(1);
|
|
||||||
return reinterpret_cast<HANDLE>(raw);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
inline void signalOverlappedEvent(OVERLAPPED *ov) {
|
inline void signalOverlappedEvent(FileObject *file, OVERLAPPED *ov, NTSTATUS status, size_t bytesTransferred) {
|
||||||
if (!shouldSignalOverlappedEvent(ov)) {
|
if (ov) {
|
||||||
return;
|
ov->Internal = status;
|
||||||
|
ov->InternalHigh = static_cast<ULONG_PTR>(bytesTransferred);
|
||||||
}
|
}
|
||||||
HANDLE handle = normalizedOverlappedEventHandle(ov);
|
if (HANDLE handle = normalizedOverlappedEventHandle(ov)) {
|
||||||
if (handle) {
|
|
||||||
if (auto ev = wibo::handles().getAs<EventObject>(handle)) {
|
if (auto ev = wibo::handles().getAs<EventObject>(handle)) {
|
||||||
ev->set();
|
ev->set();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (file) {
|
||||||
|
file->overlappedCv.notify_all();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
inline void resetOverlappedEvent(OVERLAPPED *ov) {
|
inline void resetOverlappedEvent(OVERLAPPED *ov) {
|
||||||
if (!ov) {
|
if (HANDLE handle = normalizedOverlappedEventHandle(ov)) {
|
||||||
return;
|
|
||||||
}
|
|
||||||
HANDLE handle = normalizedOverlappedEventHandle(ov);
|
|
||||||
if (handle) {
|
|
||||||
if (auto ev = wibo::handles().getAs<EventObject>(handle)) {
|
if (auto ev = wibo::handles().getAs<EventObject>(handle)) {
|
||||||
ev->reset();
|
ev->reset();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -176,11 +176,7 @@ void ThreadPoolBackend::workerLoop() {
|
|||||||
|
|
||||||
void ThreadPoolBackend::processRequest(const AsyncRequest &req) {
|
void ThreadPoolBackend::processRequest(const AsyncRequest &req) {
|
||||||
if (!req.file || !req.file->valid()) {
|
if (!req.file || !req.file->valid()) {
|
||||||
if (req.overlapped) {
|
kernel32::detail::signalOverlappedEvent(req.file.get(), req.overlapped, STATUS_INVALID_HANDLE, 0);
|
||||||
req.overlapped->Internal = STATUS_INVALID_HANDLE;
|
|
||||||
req.overlapped->InternalHigh = 0;
|
|
||||||
kernel32::detail::signalOverlappedEvent(req.overlapped);
|
|
||||||
}
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -206,11 +202,7 @@ void ThreadPoolBackend::processRequest(const AsyncRequest &req) {
|
|||||||
completionStatus = STATUS_END_OF_FILE;
|
completionStatus = STATUS_END_OF_FILE;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (req.overlapped) {
|
kernel32::detail::signalOverlappedEvent(req.file.get(), req.overlapped, completionStatus, bytesTransferred);
|
||||||
req.overlapped->Internal = completionStatus;
|
|
||||||
req.overlapped->InternalHigh = bytesTransferred;
|
|
||||||
kernel32::detail::signalOverlappedEvent(req.overlapped);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|||||||
@@ -201,30 +201,25 @@ void IoUringBackend::handleCompletion(struct io_uring_cqe *cqe) {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
OVERLAPPED *ov = req->overlapped;
|
NTSTATUS completionStatus = STATUS_SUCCESS;
|
||||||
if (ov) {
|
size_t bytesTransferred = 0;
|
||||||
if (cqe->res >= 0) {
|
if (cqe->res >= 0) {
|
||||||
ov->InternalHigh = static_cast<ULONG_PTR>(cqe->res);
|
bytesTransferred = static_cast<size_t>(cqe->res);
|
||||||
if (req->kind == AsyncRequest::Kind::Read && cqe->res == 0) {
|
if (req->kind == AsyncRequest::Kind::Read && cqe->res == 0) {
|
||||||
ov->Internal = req->isPipe ? STATUS_PIPE_BROKEN : STATUS_END_OF_FILE;
|
completionStatus = req->isPipe ? STATUS_PIPE_BROKEN : STATUS_END_OF_FILE;
|
||||||
} else {
|
|
||||||
ov->Internal = STATUS_SUCCESS;
|
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
int err = -cqe->res;
|
int err = -cqe->res;
|
||||||
ov->InternalHigh = 0;
|
|
||||||
if (err == EPIPE) {
|
if (err == EPIPE) {
|
||||||
ov->Internal = STATUS_PIPE_BROKEN;
|
completionStatus = STATUS_PIPE_BROKEN;
|
||||||
} else {
|
} else {
|
||||||
NTSTATUS status = wibo::statusFromErrno(err);
|
completionStatus = wibo::statusFromErrno(err);
|
||||||
if (status == STATUS_SUCCESS) {
|
if (completionStatus == STATUS_SUCCESS) {
|
||||||
status = STATUS_UNEXPECTED_IO_ERROR;
|
completionStatus = STATUS_UNEXPECTED_IO_ERROR;
|
||||||
}
|
|
||||||
ov->Internal = status;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
kernel32::detail::signalOverlappedEvent(ov);
|
|
||||||
}
|
}
|
||||||
|
kernel32::detail::signalOverlappedEvent(req->file.get(), req->overlapped, completionStatus, bytesTransferred);
|
||||||
|
|
||||||
delete req;
|
delete req;
|
||||||
mPending.fetch_sub(1, std::memory_order_acq_rel);
|
mPending.fetch_sub(1, std::memory_order_acq_rel);
|
||||||
|
|||||||
@@ -5,11 +5,87 @@
|
|||||||
#define STATUS_PENDING ((DWORD)0x00000103)
|
#define STATUS_PENDING ((DWORD)0x00000103)
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
static const char *kFilename = "overlapped_test.tmp";
|
static char g_tempFilename[MAX_PATH];
|
||||||
|
|
||||||
|
struct GetOverlappedWaitArgs {
|
||||||
|
HANDLE handle;
|
||||||
|
OVERLAPPED *ov;
|
||||||
|
DWORD bytesTransferred;
|
||||||
|
BOOL result;
|
||||||
|
DWORD error;
|
||||||
|
};
|
||||||
|
|
||||||
|
static DWORD WINAPI getoverlapped_wait_thread(LPVOID param) {
|
||||||
|
struct GetOverlappedWaitArgs *args = (struct GetOverlappedWaitArgs *)param;
|
||||||
|
args->bytesTransferred = 0xFFFFFFFFu;
|
||||||
|
SetLastError(0xDEADBEEFu);
|
||||||
|
args->result = GetOverlappedResult(args->handle, args->ov, &args->bytesTransferred, TRUE);
|
||||||
|
args->error = args->result ? ERROR_SUCCESS : GetLastError();
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct SyncReaderArgs {
|
||||||
|
HANDLE pipe;
|
||||||
|
OVERLAPPED *ov;
|
||||||
|
HANDLE startedEvent;
|
||||||
|
DWORD expectedBytes;
|
||||||
|
DWORD bytesRead;
|
||||||
|
BOOL readSucceeded;
|
||||||
|
};
|
||||||
|
|
||||||
|
static DWORD WINAPI sync_reader_thread(LPVOID param) {
|
||||||
|
struct SyncReaderArgs *args = (struct SyncReaderArgs *)param;
|
||||||
|
char buffer[32] = {0};
|
||||||
|
args->ov->Internal = STATUS_PENDING;
|
||||||
|
args->ov->InternalHigh = 0;
|
||||||
|
TEST_CHECK(SetEvent(args->startedEvent));
|
||||||
|
args->bytesRead = 0;
|
||||||
|
args->readSucceeded = ReadFile(args->pipe, buffer, args->expectedBytes, &args->bytesRead, args->ov);
|
||||||
|
return args->readSucceeded ? 0 : GetLastError();
|
||||||
|
}
|
||||||
|
|
||||||
|
struct SyncWriterArgs {
|
||||||
|
const char *pipeName;
|
||||||
|
HANDLE serverReadyEvent;
|
||||||
|
};
|
||||||
|
|
||||||
|
static DWORD WINAPI sync_writer_thread(LPVOID param) {
|
||||||
|
struct SyncWriterArgs *args = (struct SyncWriterArgs *)param;
|
||||||
|
TEST_CHECK(WaitForSingleObject(args->serverReadyEvent, 1000) == WAIT_OBJECT_0);
|
||||||
|
HANDLE client = CreateFileA(args->pipeName, GENERIC_WRITE, 0, NULL, OPEN_EXISTING, 0, NULL);
|
||||||
|
TEST_CHECK_MSG(client != INVALID_HANDLE_VALUE, "CreateFileA(client) failed: %lu", GetLastError());
|
||||||
|
Sleep(200);
|
||||||
|
static const char msg[] = "READY";
|
||||||
|
DWORD written = 0;
|
||||||
|
TEST_CHECK(WriteFile(client, msg, (DWORD)(sizeof(msg) - 1), &written, NULL));
|
||||||
|
TEST_CHECK_EQ(sizeof(msg) - 1, written);
|
||||||
|
CloseHandle(client);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct ManualEventWriterArgs {
|
||||||
|
const char *pipeName;
|
||||||
|
HANDLE serverReadyEvent;
|
||||||
|
HANDLE proceedEvent;
|
||||||
|
};
|
||||||
|
|
||||||
|
static DWORD WINAPI manual_event_writer_thread(LPVOID param) {
|
||||||
|
struct ManualEventWriterArgs *args = (struct ManualEventWriterArgs *)param;
|
||||||
|
TEST_CHECK(WaitForSingleObject(args->serverReadyEvent, 1000) == WAIT_OBJECT_0);
|
||||||
|
HANDLE client = CreateFileA(args->pipeName, GENERIC_WRITE, 0, NULL, OPEN_EXISTING, 0, NULL);
|
||||||
|
TEST_CHECK_MSG(client != INVALID_HANDLE_VALUE, "CreateFileA(client) failed: %lu", GetLastError());
|
||||||
|
TEST_CHECK(WaitForSingleObject(args->proceedEvent, 1000) == WAIT_OBJECT_0);
|
||||||
|
static const char payload[] = "PING!";
|
||||||
|
DWORD written = 0;
|
||||||
|
TEST_CHECK(WriteFile(client, payload, (DWORD)(sizeof(payload) - 1), &written, NULL));
|
||||||
|
TEST_CHECK_EQ(sizeof(payload) - 1, written);
|
||||||
|
CloseHandle(client);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
static void write_fixture_file(void) {
|
static void write_fixture_file(void) {
|
||||||
HANDLE file = CreateFileA(kFilename, GENERIC_WRITE | GENERIC_READ, 0, NULL,
|
HANDLE file = CreateFileA(g_tempFilename, GENERIC_WRITE | GENERIC_READ, 0, NULL, CREATE_ALWAYS,
|
||||||
CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
|
FILE_ATTRIBUTE_NORMAL, NULL);
|
||||||
TEST_CHECK(file != INVALID_HANDLE_VALUE);
|
TEST_CHECK(file != INVALID_HANDLE_VALUE);
|
||||||
|
|
||||||
const char contents[] = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ";
|
const char contents[] = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ";
|
||||||
@@ -20,8 +96,7 @@ static void write_fixture_file(void) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
static void test_synchronous_overlapped_read(void) {
|
static void test_synchronous_overlapped_read(void) {
|
||||||
HANDLE file = CreateFileA(kFilename, GENERIC_READ, 0, NULL,
|
HANDLE file = CreateFileA(g_tempFilename, GENERIC_READ, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
|
||||||
OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
|
|
||||||
TEST_CHECK(file != INVALID_HANDLE_VALUE);
|
TEST_CHECK(file != INVALID_HANDLE_VALUE);
|
||||||
|
|
||||||
OVERLAPPED ov = {0};
|
OVERLAPPED ov = {0};
|
||||||
@@ -38,18 +113,17 @@ static void test_synchronous_overlapped_read(void) {
|
|||||||
TEST_CHECK_EQ(12, (int)pos);
|
TEST_CHECK_EQ(12, (int)pos);
|
||||||
|
|
||||||
unsigned long long trackedOffset = ((unsigned long long)ov.OffsetHigh << 32) | ov.Offset;
|
unsigned long long trackedOffset = ((unsigned long long)ov.OffsetHigh << 32) | ov.Offset;
|
||||||
// Wine leaves OVERLAPPED.Offset unchanged for synchronous handles, even though the Win32 docs state the runtime should advance both the
|
// Wine leaves OVERLAPPED.Offset unchanged for synchronous handles, even though the Win32 docs state the runtime
|
||||||
// file pointer and the OVERLAPPED offsets. We intentionally skip asserting the
|
// should advance both the file pointer and the OVERLAPPED offsets. We intentionally skip asserting the offset here
|
||||||
// offset here so the fixture passes under both wibo and wine.
|
// so the fixture passes under both wibo and wine. TEST_CHECK_U64_EQ(12ULL, trackedOffset);
|
||||||
// TEST_CHECK_U64_EQ(12ULL, trackedOffset);
|
|
||||||
(void)trackedOffset;
|
(void)trackedOffset;
|
||||||
|
|
||||||
TEST_CHECK(CloseHandle(file));
|
TEST_CHECK(CloseHandle(file));
|
||||||
}
|
}
|
||||||
|
|
||||||
static void test_overlapped_read_with_event(void) {
|
static void test_overlapped_read_with_event(void) {
|
||||||
HANDLE file = CreateFileA(kFilename, GENERIC_READ, FILE_SHARE_READ, NULL,
|
HANDLE file = CreateFileA(g_tempFilename, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING,
|
||||||
OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL | FILE_FLAG_OVERLAPPED, NULL);
|
FILE_ATTRIBUTE_NORMAL | FILE_FLAG_OVERLAPPED, NULL);
|
||||||
TEST_CHECK(file != INVALID_HANDLE_VALUE);
|
TEST_CHECK(file != INVALID_HANDLE_VALUE);
|
||||||
|
|
||||||
OVERLAPPED ov = {0};
|
OVERLAPPED ov = {0};
|
||||||
@@ -75,9 +149,40 @@ static void test_overlapped_read_with_event(void) {
|
|||||||
TEST_CHECK(CloseHandle(file));
|
TEST_CHECK(CloseHandle(file));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void test_overlapped_read_without_event(void) {
|
||||||
|
HANDLE file = CreateFileA(g_tempFilename, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING,
|
||||||
|
FILE_ATTRIBUTE_NORMAL | FILE_FLAG_OVERLAPPED, NULL);
|
||||||
|
TEST_CHECK(file != INVALID_HANDLE_VALUE);
|
||||||
|
|
||||||
|
OVERLAPPED ov = {0};
|
||||||
|
ov.Offset = 4;
|
||||||
|
|
||||||
|
char buffer[16] = {0};
|
||||||
|
BOOL issued = ReadFile(file, buffer, 8, NULL, &ov);
|
||||||
|
if (!issued) {
|
||||||
|
TEST_CHECK_EQ(ERROR_IO_PENDING, GetLastError());
|
||||||
|
DWORD transferred = 0xFFFFFFFFU;
|
||||||
|
BOOL ready = GetOverlappedResult(file, &ov, &transferred, FALSE);
|
||||||
|
if (!ready) {
|
||||||
|
TEST_CHECK_EQ(ERROR_IO_INCOMPLETE, GetLastError());
|
||||||
|
TEST_CHECK_EQ(0xFFFFFFFFU, transferred); // untouched while pending
|
||||||
|
} else {
|
||||||
|
TEST_CHECK_EQ(8U, transferred);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
DWORD transferred = 0;
|
||||||
|
TEST_CHECK(GetOverlappedResult(file, &ov, &transferred, TRUE));
|
||||||
|
TEST_CHECK_EQ(8U, transferred);
|
||||||
|
buffer[8] = '\0';
|
||||||
|
TEST_CHECK_STR_EQ("456789AB", buffer);
|
||||||
|
|
||||||
|
TEST_CHECK(CloseHandle(file));
|
||||||
|
}
|
||||||
|
|
||||||
static void test_overlapped_eof(void) {
|
static void test_overlapped_eof(void) {
|
||||||
HANDLE file = CreateFileA(kFilename, GENERIC_READ, FILE_SHARE_READ, NULL,
|
HANDLE file = CreateFileA(g_tempFilename, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING,
|
||||||
OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL | FILE_FLAG_OVERLAPPED, NULL);
|
FILE_ATTRIBUTE_NORMAL | FILE_FLAG_OVERLAPPED, NULL);
|
||||||
TEST_CHECK(file != INVALID_HANDLE_VALUE);
|
TEST_CHECK(file != INVALID_HANDLE_VALUE);
|
||||||
|
|
||||||
OVERLAPPED ov = {0};
|
OVERLAPPED ov = {0};
|
||||||
@@ -102,6 +207,124 @@ static void test_overlapped_eof(void) {
|
|||||||
TEST_CHECK(CloseHandle(file));
|
TEST_CHECK(CloseHandle(file));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void test_getoverlappedresult_manual_event_signal(void) {
|
||||||
|
static const char *pipeName = "\\\\.\\pipe\\wibo_manual_event";
|
||||||
|
HANDLE pipe = CreateNamedPipeA(pipeName, PIPE_ACCESS_INBOUND | FILE_FLAG_OVERLAPPED,
|
||||||
|
PIPE_TYPE_BYTE | PIPE_WAIT, 1, 0, 0, 0, NULL);
|
||||||
|
TEST_CHECK_MSG(pipe != INVALID_HANDLE_VALUE, "CreateNamedPipeA failed: %lu", GetLastError());
|
||||||
|
|
||||||
|
HANDLE serverReady = CreateEventA(NULL, TRUE, FALSE, NULL);
|
||||||
|
HANDLE proceedWrite = CreateEventA(NULL, TRUE, FALSE, NULL);
|
||||||
|
TEST_CHECK(serverReady != NULL && proceedWrite != NULL);
|
||||||
|
|
||||||
|
struct ManualEventWriterArgs writerArgs = {pipeName, serverReady, proceedWrite};
|
||||||
|
HANDLE writerThread = CreateThread(NULL, 0, manual_event_writer_thread, &writerArgs, 0, NULL);
|
||||||
|
TEST_CHECK(writerThread != NULL);
|
||||||
|
|
||||||
|
OVERLAPPED connectOv = {0};
|
||||||
|
connectOv.hEvent = CreateEventA(NULL, TRUE, FALSE, NULL);
|
||||||
|
TEST_CHECK(connectOv.hEvent != NULL);
|
||||||
|
BOOL connectIssued = ConnectNamedPipe(pipe, &connectOv);
|
||||||
|
if (!connectIssued) {
|
||||||
|
DWORD err = GetLastError();
|
||||||
|
TEST_CHECK_MSG(err == ERROR_IO_PENDING || err == ERROR_PIPE_CONNECTED, "ConnectNamedPipe err=%lu", err);
|
||||||
|
}
|
||||||
|
TEST_CHECK(SetEvent(serverReady));
|
||||||
|
if (!connectIssued) {
|
||||||
|
TEST_CHECK(WaitForSingleObject(connectOv.hEvent, 1000) == WAIT_OBJECT_0);
|
||||||
|
}
|
||||||
|
CloseHandle(connectOv.hEvent);
|
||||||
|
|
||||||
|
OVERLAPPED ov = {0};
|
||||||
|
ov.hEvent = CreateEventA(NULL, TRUE, FALSE, NULL);
|
||||||
|
TEST_CHECK(ov.hEvent != NULL);
|
||||||
|
|
||||||
|
char buffer[8] = {0};
|
||||||
|
BOOL issued = ReadFile(pipe, buffer, sizeof(buffer), NULL, &ov);
|
||||||
|
TEST_CHECK(!issued);
|
||||||
|
TEST_CHECK_EQ(ERROR_IO_PENDING, GetLastError());
|
||||||
|
|
||||||
|
DWORD transferred = 0xDEADBEEFu;
|
||||||
|
TEST_CHECK(SetEvent(ov.hEvent));
|
||||||
|
SetLastError(0xDEADBEEFu);
|
||||||
|
DWORD start = GetTickCount();
|
||||||
|
BOOL ready = GetOverlappedResult(pipe, &ov, &transferred, TRUE);
|
||||||
|
DWORD elapsed = GetTickCount() - start;
|
||||||
|
TEST_CHECK_MSG(elapsed < 1000, "GetOverlappedResult waited unexpectedly long (%lu ms)", elapsed);
|
||||||
|
if (!ready) {
|
||||||
|
TEST_CHECK_EQ(ERROR_IO_INCOMPLETE, GetLastError());
|
||||||
|
TEST_CHECK_EQ(0xDEADBEEFu, transferred);
|
||||||
|
} else {
|
||||||
|
TEST_CHECK_EQ(STATUS_PENDING, (DWORD)ov.Internal);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CHECK(ResetEvent(ov.hEvent));
|
||||||
|
TEST_CHECK(SetEvent(proceedWrite));
|
||||||
|
TEST_CHECK(WaitForSingleObject(writerThread, 1000) == WAIT_OBJECT_0);
|
||||||
|
TEST_CHECK(WaitForSingleObject(ov.hEvent, 1000) == WAIT_OBJECT_0);
|
||||||
|
|
||||||
|
DWORD finalTransferred = 0;
|
||||||
|
TEST_CHECK(GetOverlappedResult(pipe, &ov, &finalTransferred, TRUE));
|
||||||
|
TEST_CHECK_EQ(5U, finalTransferred);
|
||||||
|
|
||||||
|
TEST_CHECK(CloseHandle(writerThread));
|
||||||
|
TEST_CHECK(CloseHandle(proceedWrite));
|
||||||
|
TEST_CHECK(CloseHandle(serverReady));
|
||||||
|
TEST_CHECK(CloseHandle(ov.hEvent));
|
||||||
|
TEST_CHECK(CloseHandle(pipe));
|
||||||
|
}
|
||||||
|
|
||||||
|
static void test_getoverlappedresult_non_overlapped_handle(void) {
|
||||||
|
static const char *pipeName = "\\\\.\\pipe\\wibo_sync_pipe";
|
||||||
|
HANDLE pipe = CreateNamedPipeA(pipeName, PIPE_ACCESS_INBOUND, PIPE_TYPE_BYTE | PIPE_WAIT, 1, 0, 0, 0, NULL);
|
||||||
|
TEST_CHECK_MSG(pipe != INVALID_HANDLE_VALUE, "CreateNamedPipeA failed: %lu", GetLastError());
|
||||||
|
|
||||||
|
HANDLE serverReady = CreateEventA(NULL, TRUE, FALSE, NULL);
|
||||||
|
TEST_CHECK(serverReady != NULL);
|
||||||
|
|
||||||
|
struct SyncWriterArgs writerArgs = {pipeName, serverReady};
|
||||||
|
HANDLE writerThread = CreateThread(NULL, 0, sync_writer_thread, &writerArgs, 0, NULL);
|
||||||
|
TEST_CHECK(writerThread != NULL);
|
||||||
|
|
||||||
|
TEST_CHECK(SetEvent(serverReady));
|
||||||
|
BOOL connected = ConnectNamedPipe(pipe, NULL);
|
||||||
|
if (!connected) {
|
||||||
|
DWORD err = GetLastError();
|
||||||
|
TEST_CHECK_EQ(ERROR_PIPE_CONNECTED, err);
|
||||||
|
}
|
||||||
|
|
||||||
|
HANDLE readStarted = CreateEventA(NULL, TRUE, FALSE, NULL);
|
||||||
|
TEST_CHECK(readStarted != NULL);
|
||||||
|
|
||||||
|
OVERLAPPED ov = {0};
|
||||||
|
struct SyncReaderArgs readerArgs = {pipe, &ov, readStarted, 5, 0, FALSE};
|
||||||
|
HANDLE readerThread = CreateThread(NULL, 0, sync_reader_thread, &readerArgs, 0, NULL);
|
||||||
|
TEST_CHECK(readerThread != NULL);
|
||||||
|
|
||||||
|
TEST_CHECK(WaitForSingleObject(readStarted, 1000) == WAIT_OBJECT_0);
|
||||||
|
|
||||||
|
struct GetOverlappedWaitArgs waitArgs = {pipe, &ov, 0, FALSE, 0};
|
||||||
|
HANDLE waitThread = CreateThread(NULL, 0, getoverlapped_wait_thread, &waitArgs, 0, NULL);
|
||||||
|
TEST_CHECK(waitThread != NULL);
|
||||||
|
|
||||||
|
TEST_CHECK(WaitForSingleObject(readerThread, 5000) == WAIT_OBJECT_0);
|
||||||
|
TEST_CHECK(readerArgs.readSucceeded);
|
||||||
|
TEST_CHECK_EQ(5U, readerArgs.bytesRead);
|
||||||
|
|
||||||
|
TEST_CHECK(WaitForSingleObject(waitThread, 5000) == WAIT_OBJECT_0);
|
||||||
|
TEST_CHECK(waitArgs.result);
|
||||||
|
TEST_CHECK_EQ(ERROR_SUCCESS, waitArgs.error);
|
||||||
|
TEST_CHECK_EQ(5U, waitArgs.bytesTransferred);
|
||||||
|
|
||||||
|
TEST_CHECK(WaitForSingleObject(writerThread, 5000) == WAIT_OBJECT_0);
|
||||||
|
TEST_CHECK(CloseHandle(waitThread));
|
||||||
|
TEST_CHECK(CloseHandle(readerThread));
|
||||||
|
TEST_CHECK(CloseHandle(readStarted));
|
||||||
|
TEST_CHECK(CloseHandle(writerThread));
|
||||||
|
TEST_CHECK(CloseHandle(serverReady));
|
||||||
|
TEST_CHECK(CloseHandle(pipe));
|
||||||
|
}
|
||||||
|
|
||||||
static void test_getoverlappedresult_pending(void) {
|
static void test_getoverlappedresult_pending(void) {
|
||||||
OVERLAPPED ov = {0};
|
OVERLAPPED ov = {0};
|
||||||
ov.Internal = STATUS_PENDING;
|
ov.Internal = STATUS_PENDING;
|
||||||
@@ -113,8 +336,8 @@ static void test_getoverlappedresult_pending(void) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
static void test_overlapped_multiple_reads(void) {
|
static void test_overlapped_multiple_reads(void) {
|
||||||
HANDLE file = CreateFileA(kFilename, GENERIC_READ, FILE_SHARE_READ, NULL,
|
HANDLE file = CreateFileA(g_tempFilename, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING,
|
||||||
OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL | FILE_FLAG_OVERLAPPED, NULL);
|
FILE_ATTRIBUTE_NORMAL | FILE_FLAG_OVERLAPPED, NULL);
|
||||||
TEST_CHECK(file != INVALID_HANDLE_VALUE);
|
TEST_CHECK(file != INVALID_HANDLE_VALUE);
|
||||||
|
|
||||||
OVERLAPPED ov1 = {0};
|
OVERLAPPED ov1 = {0};
|
||||||
@@ -161,8 +384,8 @@ static void test_overlapped_multiple_reads(void) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
static void test_getoverlappedresult_wait(void) {
|
static void test_getoverlappedresult_wait(void) {
|
||||||
HANDLE file = CreateFileA(kFilename, GENERIC_READ, FILE_SHARE_READ, NULL,
|
HANDLE file = CreateFileA(g_tempFilename, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING,
|
||||||
OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL | FILE_FLAG_OVERLAPPED, NULL);
|
FILE_ATTRIBUTE_NORMAL | FILE_FLAG_OVERLAPPED, NULL);
|
||||||
TEST_CHECK(file != INVALID_HANDLE_VALUE);
|
TEST_CHECK(file != INVALID_HANDLE_VALUE);
|
||||||
|
|
||||||
OVERLAPPED ov = {0};
|
OVERLAPPED ov = {0};
|
||||||
@@ -187,8 +410,8 @@ static void test_getoverlappedresult_wait(void) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
static void test_overlapped_write(void) {
|
static void test_overlapped_write(void) {
|
||||||
HANDLE file = CreateFileA(kFilename, GENERIC_WRITE, 0, NULL,
|
HANDLE file = CreateFileA(g_tempFilename, GENERIC_WRITE, 0, NULL, OPEN_EXISTING,
|
||||||
OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL | FILE_FLAG_OVERLAPPED, NULL);
|
FILE_ATTRIBUTE_NORMAL | FILE_FLAG_OVERLAPPED, NULL);
|
||||||
TEST_CHECK(file != INVALID_HANDLE_VALUE);
|
TEST_CHECK(file != INVALID_HANDLE_VALUE);
|
||||||
|
|
||||||
OVERLAPPED ov = {0};
|
OVERLAPPED ov = {0};
|
||||||
@@ -210,8 +433,7 @@ static void test_overlapped_write(void) {
|
|||||||
TEST_CHECK(CloseHandle(ov.hEvent));
|
TEST_CHECK(CloseHandle(ov.hEvent));
|
||||||
TEST_CHECK(CloseHandle(file));
|
TEST_CHECK(CloseHandle(file));
|
||||||
|
|
||||||
HANDLE verify = CreateFileA(kFilename, GENERIC_READ, 0, NULL,
|
HANDLE verify = CreateFileA(g_tempFilename, GENERIC_READ, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
|
||||||
OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
|
|
||||||
TEST_CHECK(verify != INVALID_HANDLE_VALUE);
|
TEST_CHECK(verify != INVALID_HANDLE_VALUE);
|
||||||
|
|
||||||
TEST_CHECK(SetFilePointer(verify, 2, NULL, FILE_BEGIN) != INVALID_SET_FILE_POINTER);
|
TEST_CHECK(SetFilePointer(verify, 2, NULL, FILE_BEGIN) != INVALID_SET_FILE_POINTER);
|
||||||
@@ -225,15 +447,23 @@ static void test_overlapped_write(void) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
int main(void) {
|
int main(void) {
|
||||||
DeleteFileA(kFilename);
|
char tempPath[MAX_PATH] = {0};
|
||||||
|
DWORD len = GetTempPathA((DWORD)sizeof(tempPath), tempPath);
|
||||||
|
TEST_CHECK_MSG(len > 0 && len < sizeof(tempPath), "GetTempPathA failed: %lu", GetLastError());
|
||||||
|
TEST_CHECK_MSG(GetTempFileNameA(tempPath, "wbo", 0, g_tempFilename) != 0,
|
||||||
|
"GetTempFileNameA failed: %lu", GetLastError());
|
||||||
|
DeleteFileA(g_tempFilename);
|
||||||
write_fixture_file();
|
write_fixture_file();
|
||||||
test_synchronous_overlapped_read();
|
test_synchronous_overlapped_read();
|
||||||
test_overlapped_read_with_event();
|
test_overlapped_read_with_event();
|
||||||
|
test_overlapped_read_without_event();
|
||||||
test_overlapped_eof();
|
test_overlapped_eof();
|
||||||
test_getoverlappedresult_pending();
|
test_getoverlappedresult_pending();
|
||||||
test_overlapped_multiple_reads();
|
test_overlapped_multiple_reads();
|
||||||
test_getoverlappedresult_wait();
|
test_getoverlappedresult_wait();
|
||||||
test_overlapped_write();
|
test_overlapped_write();
|
||||||
TEST_CHECK(DeleteFileA(kFilename));
|
test_getoverlappedresult_manual_event_signal();
|
||||||
|
test_getoverlappedresult_non_overlapped_handle();
|
||||||
|
TEST_CHECK(DeleteFileA(g_tempFilename));
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user