mirror of
https://github.com/decompals/wibo.git
synced 2025-12-13 15:16:27 +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);
|
||||
if (file->overlapped) {
|
||||
if (nNumberOfBytesToWrite == 0) {
|
||||
lpOverlapped->Internal = STATUS_SUCCESS;
|
||||
lpOverlapped->InternalHigh = 0;
|
||||
detail::signalOverlappedEvent(lpOverlapped);
|
||||
detail::signalOverlappedEvent(file.get(), lpOverlapped, STATUS_SUCCESS, 0);
|
||||
if (lpNumberOfBytesWritten) {
|
||||
*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);
|
||||
DWORD completionStatus = STATUS_SUCCESS;
|
||||
NTSTATUS completionStatus = STATUS_SUCCESS;
|
||||
if (io.unixError != 0) {
|
||||
completionStatus = wibo::statusFromErrno(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);
|
||||
}
|
||||
|
||||
if (lpOverlapped != nullptr) {
|
||||
lpOverlapped->Internal = completionStatus;
|
||||
lpOverlapped->InternalHigh = io.bytesTransferred;
|
||||
detail::signalOverlappedEvent(lpOverlapped);
|
||||
}
|
||||
detail::signalOverlappedEvent(file.get(), lpOverlapped, completionStatus, io.bytesTransferred);
|
||||
|
||||
return io.unixError == 0;
|
||||
}
|
||||
@@ -825,9 +819,7 @@ BOOL WIN_FUNC ReadFile(HANDLE hFile, LPVOID lpBuffer, DWORD nNumberOfBytesToRead
|
||||
detail::resetOverlappedEvent(lpOverlapped);
|
||||
if (file->overlapped) {
|
||||
if (nNumberOfBytesToRead == 0) {
|
||||
lpOverlapped->Internal = STATUS_SUCCESS;
|
||||
lpOverlapped->InternalHigh = 0;
|
||||
detail::signalOverlappedEvent(lpOverlapped);
|
||||
detail::signalOverlappedEvent(file.get(), lpOverlapped, STATUS_SUCCESS, 0);
|
||||
if (lpNumberOfBytesRead) {
|
||||
*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);
|
||||
DWORD completionStatus = STATUS_SUCCESS;
|
||||
NTSTATUS completionStatus = STATUS_SUCCESS;
|
||||
if (io.unixError != 0) {
|
||||
completionStatus = wibo::statusFromErrno(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) {
|
||||
completionStatus = STATUS_PIPE_BROKEN;
|
||||
wibo::lastError = ERROR_BROKEN_PIPE;
|
||||
if (lpOverlapped != nullptr) {
|
||||
lpOverlapped->Internal = completionStatus;
|
||||
lpOverlapped->InternalHigh = 0;
|
||||
detail::signalOverlappedEvent(lpOverlapped);
|
||||
}
|
||||
detail::signalOverlappedEvent(file.get(), lpOverlapped, completionStatus, 0);
|
||||
DEBUG_LOG("-> ERROR_BROKEN_PIPE\n");
|
||||
return FALSE;
|
||||
}
|
||||
@@ -868,11 +856,7 @@ BOOL WIN_FUNC ReadFile(HANDLE hFile, LPVOID lpBuffer, DWORD nNumberOfBytesToRead
|
||||
*lpNumberOfBytesRead = static_cast<DWORD>(io.bytesTransferred);
|
||||
}
|
||||
|
||||
if (lpOverlapped != nullptr) {
|
||||
lpOverlapped->Internal = completionStatus;
|
||||
lpOverlapped->InternalHigh = io.bytesTransferred;
|
||||
detail::signalOverlappedEvent(lpOverlapped);
|
||||
}
|
||||
detail::signalOverlappedEvent(file.get(), lpOverlapped, completionStatus, io.bytesTransferred);
|
||||
|
||||
DEBUG_LOG("-> %u bytes read, error %d\n", io.bytesTransferred, io.unixError == 0 ? 0 : wibo::lastError);
|
||||
return io.unixError == 0;
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
#include "handles.h"
|
||||
#include "mimalloc.h"
|
||||
|
||||
#include <condition_variable>
|
||||
#include <pthread.h>
|
||||
|
||||
namespace kernel32 {
|
||||
@@ -30,6 +31,8 @@ struct FileObject : FsObject {
|
||||
bool overlapped = false;
|
||||
bool appendOnly = false;
|
||||
bool isPipe = false;
|
||||
// Used to notify overlapped operations without an event handle
|
||||
std::condition_variable overlappedCv;
|
||||
|
||||
explicit FileObject(int fd) : FsObject(kType, fd) {
|
||||
if (fd >= 0) {
|
||||
@@ -99,18 +102,6 @@ struct MutexObject final : WaitableObject {
|
||||
bool abandoned = false; // Owner exited without releasing
|
||||
|
||||
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 {
|
||||
|
||||
@@ -2,24 +2,32 @@
|
||||
|
||||
#include "context.h"
|
||||
#include "errors.h"
|
||||
#include "internal.h"
|
||||
#include "overlapped_util.h"
|
||||
#include "synchapi.h"
|
||||
|
||||
#include <mutex>
|
||||
|
||||
namespace kernel32 {
|
||||
|
||||
BOOL WIN_FUNC GetOverlappedResult(HANDLE hFile, LPOVERLAPPED lpOverlapped, LPDWORD lpNumberOfBytesTransferred,
|
||||
BOOL bWait) {
|
||||
HOST_CONTEXT_GUARD();
|
||||
DEBUG_LOG("GetOverlappedResult(%p, %p, %p, %d)\n", hFile, lpOverlapped, lpNumberOfBytesTransferred, bWait);
|
||||
(void)hFile;
|
||||
if (!lpOverlapped) {
|
||||
wibo::lastError = ERROR_INVALID_PARAMETER;
|
||||
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)) {
|
||||
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 {
|
||||
|
||||
class NamedPipeInstance;
|
||||
struct NamedPipeInstance;
|
||||
|
||||
void configureInheritability(int fd, bool inherit) {
|
||||
if (fd < 0) {
|
||||
@@ -112,17 +112,17 @@ struct NamedPipeState : ObjectBase {
|
||||
DWORD defaultTimeout = 0;
|
||||
DWORD maxInstances = PIPE_UNLIMITED_INSTANCES;
|
||||
uint32_t instanceCount = 0;
|
||||
std::vector<class NamedPipeInstance *> instances;
|
||||
std::vector<NamedPipeInstance *> instances;
|
||||
|
||||
explicit NamedPipeState(std::string k) : ObjectBase(kType), key(std::move(k)) {}
|
||||
~NamedPipeState() override { wibo::g_namespace.remove(this); }
|
||||
|
||||
void registerInstance(class NamedPipeInstance *inst) {
|
||||
void registerInstance(NamedPipeInstance *inst) {
|
||||
std::lock_guard lk(mutex);
|
||||
instances.push_back(inst);
|
||||
}
|
||||
|
||||
void unregisterInstance(class NamedPipeInstance *inst) {
|
||||
void unregisterInstance(NamedPipeInstance *inst) {
|
||||
std::lock_guard lk(mutex);
|
||||
auto it = std::find(instances.begin(), instances.end(), inst);
|
||||
if (it != instances.end()) {
|
||||
@@ -194,12 +194,8 @@ struct NamedPipeInstance final : FileObject {
|
||||
std::lock_guard lk(connectMutex);
|
||||
localCompanion = companionFd;
|
||||
companionFd = -1;
|
||||
if (pendingOverlapped) {
|
||||
pendingOverlapped->Internal = STATUS_PIPE_BROKEN;
|
||||
pendingOverlapped->InternalHigh = 0;
|
||||
kernel32::detail::signalOverlappedEvent(pendingOverlapped);
|
||||
pendingOverlapped = nullptr;
|
||||
}
|
||||
kernel32::detail::signalOverlappedEvent(this, pendingOverlapped, STATUS_PIPE_BROKEN, 0);
|
||||
pendingOverlapped = nullptr;
|
||||
connectPending = false;
|
||||
connectCv.notify_all();
|
||||
}
|
||||
@@ -208,8 +204,6 @@ struct NamedPipeInstance final : FileObject {
|
||||
}
|
||||
if (state) {
|
||||
state->unregisterInstance(this);
|
||||
}
|
||||
if (state) {
|
||||
state->releaseInstance();
|
||||
}
|
||||
}
|
||||
@@ -233,24 +227,19 @@ struct NamedPipeInstance final : FileObject {
|
||||
}
|
||||
|
||||
int takeCompanion() {
|
||||
std::unique_lock lk(connectMutex);
|
||||
std::lock_guard lk(connectMutex);
|
||||
if (companionFd < 0 || clientConnected) {
|
||||
return -1;
|
||||
}
|
||||
int fd = companionFd;
|
||||
companionFd = -1;
|
||||
clientConnected = true;
|
||||
if (pendingOverlapped) {
|
||||
pendingOverlapped->Internal = STATUS_SUCCESS;
|
||||
pendingOverlapped->InternalHigh = 0;
|
||||
kernel32::detail::signalOverlappedEvent(pendingOverlapped);
|
||||
pendingOverlapped = nullptr;
|
||||
}
|
||||
kernel32::detail::signalOverlappedEvent(this, pendingOverlapped, STATUS_SUCCESS, 0);
|
||||
pendingOverlapped = nullptr;
|
||||
if (connectPending) {
|
||||
connectPending = false;
|
||||
connectCv.notify_all();
|
||||
}
|
||||
lk.unlock();
|
||||
return fd;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
#pragma once
|
||||
|
||||
#include "context.h"
|
||||
#include "errors.h"
|
||||
#include "handles.h"
|
||||
#include "internal.h"
|
||||
#include "minwinbase.h"
|
||||
@@ -9,41 +9,30 @@
|
||||
|
||||
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) {
|
||||
if (!ov || (reinterpret_cast<uintptr_t>(ov->hEvent) & 1U) != 0) {
|
||||
return nullptr;
|
||||
}
|
||||
auto raw = reinterpret_cast<uintptr_t>(ov->hEvent);
|
||||
raw &= ~static_cast<uintptr_t>(1);
|
||||
return reinterpret_cast<HANDLE>(raw);
|
||||
return ov->hEvent;
|
||||
}
|
||||
|
||||
inline void signalOverlappedEvent(OVERLAPPED *ov) {
|
||||
if (!shouldSignalOverlappedEvent(ov)) {
|
||||
return;
|
||||
inline void signalOverlappedEvent(FileObject *file, OVERLAPPED *ov, NTSTATUS status, size_t bytesTransferred) {
|
||||
if (ov) {
|
||||
ov->Internal = status;
|
||||
ov->InternalHigh = static_cast<ULONG_PTR>(bytesTransferred);
|
||||
}
|
||||
HANDLE handle = normalizedOverlappedEventHandle(ov);
|
||||
if (handle) {
|
||||
if (HANDLE handle = normalizedOverlappedEventHandle(ov)) {
|
||||
if (auto ev = wibo::handles().getAs<EventObject>(handle)) {
|
||||
ev->set();
|
||||
}
|
||||
}
|
||||
if (file) {
|
||||
file->overlappedCv.notify_all();
|
||||
}
|
||||
}
|
||||
|
||||
inline void resetOverlappedEvent(OVERLAPPED *ov) {
|
||||
if (!ov) {
|
||||
return;
|
||||
}
|
||||
HANDLE handle = normalizedOverlappedEventHandle(ov);
|
||||
if (handle) {
|
||||
if (HANDLE handle = normalizedOverlappedEventHandle(ov)) {
|
||||
if (auto ev = wibo::handles().getAs<EventObject>(handle)) {
|
||||
ev->reset();
|
||||
}
|
||||
|
||||
@@ -176,11 +176,7 @@ void ThreadPoolBackend::workerLoop() {
|
||||
|
||||
void ThreadPoolBackend::processRequest(const AsyncRequest &req) {
|
||||
if (!req.file || !req.file->valid()) {
|
||||
if (req.overlapped) {
|
||||
req.overlapped->Internal = STATUS_INVALID_HANDLE;
|
||||
req.overlapped->InternalHigh = 0;
|
||||
kernel32::detail::signalOverlappedEvent(req.overlapped);
|
||||
}
|
||||
kernel32::detail::signalOverlappedEvent(req.file.get(), req.overlapped, STATUS_INVALID_HANDLE, 0);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -206,11 +202,7 @@ void ThreadPoolBackend::processRequest(const AsyncRequest &req) {
|
||||
completionStatus = STATUS_END_OF_FILE;
|
||||
}
|
||||
|
||||
if (req.overlapped) {
|
||||
req.overlapped->Internal = completionStatus;
|
||||
req.overlapped->InternalHigh = bytesTransferred;
|
||||
kernel32::detail::signalOverlappedEvent(req.overlapped);
|
||||
}
|
||||
kernel32::detail::signalOverlappedEvent(req.file.get(), req.overlapped, completionStatus, bytesTransferred);
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
@@ -201,30 +201,25 @@ void IoUringBackend::handleCompletion(struct io_uring_cqe *cqe) {
|
||||
return;
|
||||
}
|
||||
|
||||
OVERLAPPED *ov = req->overlapped;
|
||||
if (ov) {
|
||||
if (cqe->res >= 0) {
|
||||
ov->InternalHigh = static_cast<ULONG_PTR>(cqe->res);
|
||||
if (req->kind == AsyncRequest::Kind::Read && cqe->res == 0) {
|
||||
ov->Internal = req->isPipe ? STATUS_PIPE_BROKEN : STATUS_END_OF_FILE;
|
||||
} else {
|
||||
ov->Internal = STATUS_SUCCESS;
|
||||
}
|
||||
NTSTATUS completionStatus = STATUS_SUCCESS;
|
||||
size_t bytesTransferred = 0;
|
||||
if (cqe->res >= 0) {
|
||||
bytesTransferred = static_cast<size_t>(cqe->res);
|
||||
if (req->kind == AsyncRequest::Kind::Read && cqe->res == 0) {
|
||||
completionStatus = req->isPipe ? STATUS_PIPE_BROKEN : STATUS_END_OF_FILE;
|
||||
}
|
||||
} else {
|
||||
int err = -cqe->res;
|
||||
if (err == EPIPE) {
|
||||
completionStatus = STATUS_PIPE_BROKEN;
|
||||
} else {
|
||||
int err = -cqe->res;
|
||||
ov->InternalHigh = 0;
|
||||
if (err == EPIPE) {
|
||||
ov->Internal = STATUS_PIPE_BROKEN;
|
||||
} else {
|
||||
NTSTATUS status = wibo::statusFromErrno(err);
|
||||
if (status == STATUS_SUCCESS) {
|
||||
status = STATUS_UNEXPECTED_IO_ERROR;
|
||||
}
|
||||
ov->Internal = status;
|
||||
completionStatus = wibo::statusFromErrno(err);
|
||||
if (completionStatus == STATUS_SUCCESS) {
|
||||
completionStatus = STATUS_UNEXPECTED_IO_ERROR;
|
||||
}
|
||||
}
|
||||
kernel32::detail::signalOverlappedEvent(ov);
|
||||
}
|
||||
kernel32::detail::signalOverlappedEvent(req->file.get(), req->overlapped, completionStatus, bytesTransferred);
|
||||
|
||||
delete req;
|
||||
mPending.fetch_sub(1, std::memory_order_acq_rel);
|
||||
|
||||
@@ -5,235 +5,465 @@
|
||||
#define STATUS_PENDING ((DWORD)0x00000103)
|
||||
#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) {
|
||||
HANDLE file = CreateFileA(kFilename, GENERIC_WRITE | GENERIC_READ, 0, NULL,
|
||||
CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
|
||||
TEST_CHECK(file != INVALID_HANDLE_VALUE);
|
||||
HANDLE file = CreateFileA(g_tempFilename, GENERIC_WRITE | GENERIC_READ, 0, NULL, CREATE_ALWAYS,
|
||||
FILE_ATTRIBUTE_NORMAL, NULL);
|
||||
TEST_CHECK(file != INVALID_HANDLE_VALUE);
|
||||
|
||||
const char contents[] = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ";
|
||||
DWORD written = 0;
|
||||
TEST_CHECK(WriteFile(file, contents, (DWORD)(sizeof(contents) - 1), &written, NULL));
|
||||
TEST_CHECK_EQ(sizeof(contents) - 1, written);
|
||||
TEST_CHECK(CloseHandle(file));
|
||||
const char contents[] = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ";
|
||||
DWORD written = 0;
|
||||
TEST_CHECK(WriteFile(file, contents, (DWORD)(sizeof(contents) - 1), &written, NULL));
|
||||
TEST_CHECK_EQ(sizeof(contents) - 1, written);
|
||||
TEST_CHECK(CloseHandle(file));
|
||||
}
|
||||
|
||||
static void test_synchronous_overlapped_read(void) {
|
||||
HANDLE file = CreateFileA(kFilename, GENERIC_READ, 0, NULL,
|
||||
OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
|
||||
TEST_CHECK(file != INVALID_HANDLE_VALUE);
|
||||
HANDLE file = CreateFileA(g_tempFilename, GENERIC_READ, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
|
||||
TEST_CHECK(file != INVALID_HANDLE_VALUE);
|
||||
|
||||
OVERLAPPED ov = {0};
|
||||
ov.Offset = 5;
|
||||
OVERLAPPED ov = {0};
|
||||
ov.Offset = 5;
|
||||
|
||||
char buffer[16] = {0};
|
||||
DWORD bytesRead = 0;
|
||||
TEST_CHECK(ReadFile(file, buffer, 7, &bytesRead, &ov));
|
||||
TEST_CHECK_EQ(7, bytesRead);
|
||||
buffer[7] = '\0';
|
||||
TEST_CHECK_STR_EQ("56789AB", buffer);
|
||||
char buffer[16] = {0};
|
||||
DWORD bytesRead = 0;
|
||||
TEST_CHECK(ReadFile(file, buffer, 7, &bytesRead, &ov));
|
||||
TEST_CHECK_EQ(7, bytesRead);
|
||||
buffer[7] = '\0';
|
||||
TEST_CHECK_STR_EQ("56789AB", buffer);
|
||||
|
||||
DWORD pos = SetFilePointer(file, 0, NULL, FILE_CURRENT);
|
||||
TEST_CHECK_EQ(12, (int)pos);
|
||||
DWORD pos = SetFilePointer(file, 0, NULL, FILE_CURRENT);
|
||||
TEST_CHECK_EQ(12, (int)pos);
|
||||
|
||||
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
|
||||
// file pointer and the OVERLAPPED offsets. We intentionally skip asserting the
|
||||
// offset here so the fixture passes under both wibo and wine.
|
||||
// TEST_CHECK_U64_EQ(12ULL, trackedOffset);
|
||||
(void)trackedOffset;
|
||||
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 file pointer and the OVERLAPPED offsets. We intentionally skip asserting the offset here
|
||||
// so the fixture passes under both wibo and wine. TEST_CHECK_U64_EQ(12ULL, trackedOffset);
|
||||
(void)trackedOffset;
|
||||
|
||||
TEST_CHECK(CloseHandle(file));
|
||||
TEST_CHECK(CloseHandle(file));
|
||||
}
|
||||
|
||||
static void test_overlapped_read_with_event(void) {
|
||||
HANDLE file = CreateFileA(kFilename, GENERIC_READ, FILE_SHARE_READ, NULL,
|
||||
OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL | FILE_FLAG_OVERLAPPED, NULL);
|
||||
TEST_CHECK(file != INVALID_HANDLE_VALUE);
|
||||
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 = 10;
|
||||
ov.hEvent = CreateEventA(NULL, TRUE, FALSE, NULL);
|
||||
TEST_CHECK(ov.hEvent != NULL);
|
||||
OVERLAPPED ov = {0};
|
||||
ov.Offset = 10;
|
||||
ov.hEvent = CreateEventA(NULL, TRUE, FALSE, NULL);
|
||||
TEST_CHECK(ov.hEvent != NULL);
|
||||
|
||||
char buffer[16] = {0};
|
||||
BOOL issued = ReadFile(file, buffer, 6, NULL, &ov);
|
||||
if (!issued) {
|
||||
TEST_CHECK_EQ(ERROR_IO_PENDING, GetLastError());
|
||||
}
|
||||
char buffer[16] = {0};
|
||||
BOOL issued = ReadFile(file, buffer, 6, NULL, &ov);
|
||||
if (!issued) {
|
||||
TEST_CHECK_EQ(ERROR_IO_PENDING, GetLastError());
|
||||
}
|
||||
|
||||
TEST_CHECK(WaitForSingleObject(ov.hEvent, 1000) == WAIT_OBJECT_0);
|
||||
TEST_CHECK(WaitForSingleObject(ov.hEvent, 1000) == WAIT_OBJECT_0);
|
||||
|
||||
DWORD transferred = 0;
|
||||
TEST_CHECK(GetOverlappedResult(file, &ov, &transferred, FALSE));
|
||||
TEST_CHECK_EQ(6U, transferred);
|
||||
buffer[6] = '\0';
|
||||
TEST_CHECK_STR_EQ("ABCDEF", buffer);
|
||||
DWORD transferred = 0;
|
||||
TEST_CHECK(GetOverlappedResult(file, &ov, &transferred, FALSE));
|
||||
TEST_CHECK_EQ(6U, transferred);
|
||||
buffer[6] = '\0';
|
||||
TEST_CHECK_STR_EQ("ABCDEF", buffer);
|
||||
|
||||
TEST_CHECK(CloseHandle(ov.hEvent));
|
||||
TEST_CHECK(CloseHandle(file));
|
||||
TEST_CHECK(CloseHandle(ov.hEvent));
|
||||
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) {
|
||||
HANDLE file = CreateFileA(kFilename, GENERIC_READ, FILE_SHARE_READ, NULL,
|
||||
OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL | FILE_FLAG_OVERLAPPED, NULL);
|
||||
TEST_CHECK(file != INVALID_HANDLE_VALUE);
|
||||
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 = 80; /* beyond end */
|
||||
ov.hEvent = CreateEventA(NULL, TRUE, FALSE, NULL);
|
||||
TEST_CHECK(ov.hEvent != NULL);
|
||||
OVERLAPPED ov = {0};
|
||||
ov.Offset = 80; /* beyond end */
|
||||
ov.hEvent = CreateEventA(NULL, TRUE, FALSE, NULL);
|
||||
TEST_CHECK(ov.hEvent != NULL);
|
||||
|
||||
char buffer[8] = {0};
|
||||
BOOL issued = ReadFile(file, buffer, sizeof(buffer), NULL, &ov);
|
||||
if (!issued) {
|
||||
TEST_CHECK_EQ(ERROR_IO_PENDING, GetLastError());
|
||||
}
|
||||
char buffer[8] = {0};
|
||||
BOOL issued = ReadFile(file, buffer, sizeof(buffer), NULL, &ov);
|
||||
if (!issued) {
|
||||
TEST_CHECK_EQ(ERROR_IO_PENDING, GetLastError());
|
||||
}
|
||||
|
||||
TEST_CHECK(WaitForSingleObject(ov.hEvent, 1000) == WAIT_OBJECT_0);
|
||||
TEST_CHECK(WaitForSingleObject(ov.hEvent, 1000) == WAIT_OBJECT_0);
|
||||
|
||||
DWORD transferred = 1234;
|
||||
TEST_CHECK(!GetOverlappedResult(file, &ov, &transferred, FALSE));
|
||||
TEST_CHECK_EQ(ERROR_HANDLE_EOF, GetLastError());
|
||||
TEST_CHECK_EQ(0U, transferred);
|
||||
DWORD transferred = 1234;
|
||||
TEST_CHECK(!GetOverlappedResult(file, &ov, &transferred, FALSE));
|
||||
TEST_CHECK_EQ(ERROR_HANDLE_EOF, GetLastError());
|
||||
TEST_CHECK_EQ(0U, transferred);
|
||||
|
||||
TEST_CHECK(CloseHandle(ov.hEvent));
|
||||
TEST_CHECK(CloseHandle(file));
|
||||
TEST_CHECK(CloseHandle(ov.hEvent));
|
||||
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) {
|
||||
OVERLAPPED ov = {0};
|
||||
ov.Internal = STATUS_PENDING;
|
||||
ov.InternalHigh = 42;
|
||||
DWORD transferred = 0;
|
||||
TEST_CHECK(!GetOverlappedResult(NULL, &ov, &transferred, FALSE));
|
||||
TEST_CHECK_EQ(ERROR_IO_INCOMPLETE, GetLastError());
|
||||
TEST_CHECK_EQ(0U, transferred); // No update if the operation is still pending
|
||||
OVERLAPPED ov = {0};
|
||||
ov.Internal = STATUS_PENDING;
|
||||
ov.InternalHigh = 42;
|
||||
DWORD transferred = 0;
|
||||
TEST_CHECK(!GetOverlappedResult(NULL, &ov, &transferred, FALSE));
|
||||
TEST_CHECK_EQ(ERROR_IO_INCOMPLETE, GetLastError());
|
||||
TEST_CHECK_EQ(0U, transferred); // No update if the operation is still pending
|
||||
}
|
||||
|
||||
static void test_overlapped_multiple_reads(void) {
|
||||
HANDLE file = CreateFileA(kFilename, GENERIC_READ, FILE_SHARE_READ, NULL,
|
||||
OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL | FILE_FLAG_OVERLAPPED, NULL);
|
||||
TEST_CHECK(file != INVALID_HANDLE_VALUE);
|
||||
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 ov1 = {0};
|
||||
OVERLAPPED ov2 = {0};
|
||||
ov1.Offset = 0;
|
||||
ov2.Offset = 16;
|
||||
ov1.hEvent = CreateEventA(NULL, TRUE, FALSE, NULL);
|
||||
ov2.hEvent = CreateEventA(NULL, TRUE, FALSE, NULL);
|
||||
TEST_CHECK(ov1.hEvent != NULL);
|
||||
TEST_CHECK(ov2.hEvent != NULL);
|
||||
OVERLAPPED ov1 = {0};
|
||||
OVERLAPPED ov2 = {0};
|
||||
ov1.Offset = 0;
|
||||
ov2.Offset = 16;
|
||||
ov1.hEvent = CreateEventA(NULL, TRUE, FALSE, NULL);
|
||||
ov2.hEvent = CreateEventA(NULL, TRUE, FALSE, NULL);
|
||||
TEST_CHECK(ov1.hEvent != NULL);
|
||||
TEST_CHECK(ov2.hEvent != NULL);
|
||||
|
||||
char head[8] = {0};
|
||||
char tail[8] = {0};
|
||||
char head[8] = {0};
|
||||
char tail[8] = {0};
|
||||
|
||||
BOOL issued1 = ReadFile(file, head, 5, NULL, &ov1);
|
||||
if (!issued1) {
|
||||
TEST_CHECK_EQ(ERROR_IO_PENDING, GetLastError());
|
||||
}
|
||||
BOOL issued1 = ReadFile(file, head, 5, NULL, &ov1);
|
||||
if (!issued1) {
|
||||
TEST_CHECK_EQ(ERROR_IO_PENDING, GetLastError());
|
||||
}
|
||||
|
||||
BOOL issued2 = ReadFile(file, tail, 5, NULL, &ov2);
|
||||
if (!issued2) {
|
||||
TEST_CHECK_EQ(ERROR_IO_PENDING, GetLastError());
|
||||
}
|
||||
BOOL issued2 = ReadFile(file, tail, 5, NULL, &ov2);
|
||||
if (!issued2) {
|
||||
TEST_CHECK_EQ(ERROR_IO_PENDING, GetLastError());
|
||||
}
|
||||
|
||||
HANDLE events[2] = {ov1.hEvent, ov2.hEvent};
|
||||
DWORD waitResult = WaitForMultipleObjects(2, events, TRUE, 1000);
|
||||
TEST_CHECK_EQ(WAIT_OBJECT_0, waitResult);
|
||||
HANDLE events[2] = {ov1.hEvent, ov2.hEvent};
|
||||
DWORD waitResult = WaitForMultipleObjects(2, events, TRUE, 1000);
|
||||
TEST_CHECK_EQ(WAIT_OBJECT_0, waitResult);
|
||||
|
||||
DWORD transferred = 0;
|
||||
TEST_CHECK(GetOverlappedResult(file, &ov1, &transferred, FALSE));
|
||||
TEST_CHECK_EQ(5U, transferred);
|
||||
head[5] = '\0';
|
||||
TEST_CHECK_STR_EQ("01234", head);
|
||||
DWORD transferred = 0;
|
||||
TEST_CHECK(GetOverlappedResult(file, &ov1, &transferred, FALSE));
|
||||
TEST_CHECK_EQ(5U, transferred);
|
||||
head[5] = '\0';
|
||||
TEST_CHECK_STR_EQ("01234", head);
|
||||
|
||||
transferred = 0;
|
||||
TEST_CHECK(GetOverlappedResult(file, &ov2, &transferred, FALSE));
|
||||
TEST_CHECK_EQ(5U, transferred);
|
||||
tail[5] = '\0';
|
||||
TEST_CHECK_STR_EQ("GHIJK", tail);
|
||||
transferred = 0;
|
||||
TEST_CHECK(GetOverlappedResult(file, &ov2, &transferred, FALSE));
|
||||
TEST_CHECK_EQ(5U, transferred);
|
||||
tail[5] = '\0';
|
||||
TEST_CHECK_STR_EQ("GHIJK", tail);
|
||||
|
||||
TEST_CHECK(CloseHandle(ov2.hEvent));
|
||||
TEST_CHECK(CloseHandle(ov1.hEvent));
|
||||
TEST_CHECK(CloseHandle(file));
|
||||
TEST_CHECK(CloseHandle(ov2.hEvent));
|
||||
TEST_CHECK(CloseHandle(ov1.hEvent));
|
||||
TEST_CHECK(CloseHandle(file));
|
||||
}
|
||||
|
||||
static void test_getoverlappedresult_wait(void) {
|
||||
HANDLE file = CreateFileA(kFilename, GENERIC_READ, FILE_SHARE_READ, NULL,
|
||||
OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL | FILE_FLAG_OVERLAPPED, NULL);
|
||||
TEST_CHECK(file != INVALID_HANDLE_VALUE);
|
||||
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 = 20;
|
||||
ov.hEvent = CreateEventA(NULL, FALSE, FALSE, NULL);
|
||||
TEST_CHECK(ov.hEvent != NULL);
|
||||
OVERLAPPED ov = {0};
|
||||
ov.Offset = 20;
|
||||
ov.hEvent = CreateEventA(NULL, FALSE, FALSE, NULL);
|
||||
TEST_CHECK(ov.hEvent != NULL);
|
||||
|
||||
char buffer[8] = {0};
|
||||
BOOL issued = ReadFile(file, buffer, 6, NULL, &ov);
|
||||
if (!issued) {
|
||||
TEST_CHECK_EQ(ERROR_IO_PENDING, GetLastError());
|
||||
}
|
||||
char buffer[8] = {0};
|
||||
BOOL issued = ReadFile(file, buffer, 6, NULL, &ov);
|
||||
if (!issued) {
|
||||
TEST_CHECK_EQ(ERROR_IO_PENDING, GetLastError());
|
||||
}
|
||||
|
||||
DWORD transferred = 0;
|
||||
TEST_CHECK(GetOverlappedResult(file, &ov, &transferred, TRUE));
|
||||
TEST_CHECK_EQ(6U, transferred);
|
||||
buffer[6] = '\0';
|
||||
TEST_CHECK_STR_EQ("KLMNOP", buffer);
|
||||
DWORD transferred = 0;
|
||||
TEST_CHECK(GetOverlappedResult(file, &ov, &transferred, TRUE));
|
||||
TEST_CHECK_EQ(6U, transferred);
|
||||
buffer[6] = '\0';
|
||||
TEST_CHECK_STR_EQ("KLMNOP", buffer);
|
||||
|
||||
TEST_CHECK(CloseHandle(ov.hEvent));
|
||||
TEST_CHECK(CloseHandle(file));
|
||||
TEST_CHECK(CloseHandle(ov.hEvent));
|
||||
TEST_CHECK(CloseHandle(file));
|
||||
}
|
||||
|
||||
static void test_overlapped_write(void) {
|
||||
HANDLE file = CreateFileA(kFilename, GENERIC_WRITE, 0, NULL,
|
||||
OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL | FILE_FLAG_OVERLAPPED, NULL);
|
||||
TEST_CHECK(file != INVALID_HANDLE_VALUE);
|
||||
HANDLE file = CreateFileA(g_tempFilename, GENERIC_WRITE, 0, NULL, OPEN_EXISTING,
|
||||
FILE_ATTRIBUTE_NORMAL | FILE_FLAG_OVERLAPPED, NULL);
|
||||
TEST_CHECK(file != INVALID_HANDLE_VALUE);
|
||||
|
||||
OVERLAPPED ov = {0};
|
||||
ov.Offset = 2;
|
||||
ov.hEvent = CreateEventA(NULL, FALSE, FALSE, NULL);
|
||||
TEST_CHECK(ov.hEvent != NULL);
|
||||
OVERLAPPED ov = {0};
|
||||
ov.Offset = 2;
|
||||
ov.hEvent = CreateEventA(NULL, FALSE, FALSE, NULL);
|
||||
TEST_CHECK(ov.hEvent != NULL);
|
||||
|
||||
const char patch[] = "zz";
|
||||
BOOL issued = WriteFile(file, patch, (DWORD)(sizeof(patch) - 1), NULL, &ov);
|
||||
if (!issued) {
|
||||
TEST_CHECK_EQ(ERROR_IO_PENDING, GetLastError());
|
||||
}
|
||||
TEST_CHECK(WaitForSingleObject(ov.hEvent, 1000) == WAIT_OBJECT_0);
|
||||
const char patch[] = "zz";
|
||||
BOOL issued = WriteFile(file, patch, (DWORD)(sizeof(patch) - 1), NULL, &ov);
|
||||
if (!issued) {
|
||||
TEST_CHECK_EQ(ERROR_IO_PENDING, GetLastError());
|
||||
}
|
||||
TEST_CHECK(WaitForSingleObject(ov.hEvent, 1000) == WAIT_OBJECT_0);
|
||||
|
||||
DWORD transferred = 0;
|
||||
TEST_CHECK(GetOverlappedResult(file, &ov, &transferred, FALSE));
|
||||
TEST_CHECK_EQ((DWORD)(sizeof(patch) - 1), transferred);
|
||||
DWORD transferred = 0;
|
||||
TEST_CHECK(GetOverlappedResult(file, &ov, &transferred, FALSE));
|
||||
TEST_CHECK_EQ((DWORD)(sizeof(patch) - 1), transferred);
|
||||
|
||||
TEST_CHECK(CloseHandle(ov.hEvent));
|
||||
TEST_CHECK(CloseHandle(file));
|
||||
TEST_CHECK(CloseHandle(ov.hEvent));
|
||||
TEST_CHECK(CloseHandle(file));
|
||||
|
||||
HANDLE verify = CreateFileA(kFilename, GENERIC_READ, 0, NULL,
|
||||
OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
|
||||
TEST_CHECK(verify != INVALID_HANDLE_VALUE);
|
||||
HANDLE verify = CreateFileA(g_tempFilename, GENERIC_READ, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
|
||||
TEST_CHECK(verify != INVALID_HANDLE_VALUE);
|
||||
|
||||
TEST_CHECK(SetFilePointer(verify, 2, NULL, FILE_BEGIN) != INVALID_SET_FILE_POINTER);
|
||||
char buffer[3] = {0};
|
||||
DWORD bytesRead = 0;
|
||||
TEST_CHECK(ReadFile(verify, buffer, sizeof(patch) - 1, &bytesRead, NULL));
|
||||
TEST_CHECK_EQ((DWORD)(sizeof(patch) - 1), bytesRead);
|
||||
TEST_CHECK(buffer[0] == 'z' && buffer[1] == 'z');
|
||||
TEST_CHECK(SetFilePointer(verify, 2, NULL, FILE_BEGIN) != INVALID_SET_FILE_POINTER);
|
||||
char buffer[3] = {0};
|
||||
DWORD bytesRead = 0;
|
||||
TEST_CHECK(ReadFile(verify, buffer, sizeof(patch) - 1, &bytesRead, NULL));
|
||||
TEST_CHECK_EQ((DWORD)(sizeof(patch) - 1), bytesRead);
|
||||
TEST_CHECK(buffer[0] == 'z' && buffer[1] == 'z');
|
||||
|
||||
TEST_CHECK(CloseHandle(verify));
|
||||
TEST_CHECK(CloseHandle(verify));
|
||||
}
|
||||
|
||||
int main(void) {
|
||||
DeleteFileA(kFilename);
|
||||
write_fixture_file();
|
||||
test_synchronous_overlapped_read();
|
||||
test_overlapped_read_with_event();
|
||||
test_overlapped_eof();
|
||||
test_getoverlappedresult_pending();
|
||||
test_overlapped_multiple_reads();
|
||||
test_getoverlappedresult_wait();
|
||||
test_overlapped_write();
|
||||
TEST_CHECK(DeleteFileA(kFilename));
|
||||
return 0;
|
||||
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();
|
||||
test_synchronous_overlapped_read();
|
||||
test_overlapped_read_with_event();
|
||||
test_overlapped_read_without_event();
|
||||
test_overlapped_eof();
|
||||
test_getoverlappedresult_pending();
|
||||
test_overlapped_multiple_reads();
|
||||
test_getoverlappedresult_wait();
|
||||
test_overlapped_write();
|
||||
test_getoverlappedresult_manual_event_signal();
|
||||
test_getoverlappedresult_non_overlapped_handle();
|
||||
TEST_CHECK(DeleteFileA(g_tempFilename));
|
||||
return 0;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user