blob: 3dd6e5bc981fc7a95db14398a3a343bcc6b2671e [file] [log] [blame]
// Copyright (c) 2013-2014 Sandstorm Development Group, Inc. and contributors
// Licensed under the MIT License:
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
#if _WIN32
#include "async-win32.h"
#include "thread.h"
#include "test.h"
#include "mutex.h"
namespace kj {
namespace {
KJ_TEST("Win32IocpEventPort I/O operations") {
Win32IocpEventPort port;
EventLoop loop(port);
WaitScope waitScope(loop);
auto pipeName = kj::str("\\\\.\\Pipe\\kj-async-win32-test.", GetCurrentProcessId());
HANDLE readEnd_, writeEnd_;
KJ_WIN32(readEnd_ = CreateNamedPipeA(pipeName.cStr(),
PIPE_ACCESS_INBOUND | FILE_FLAG_OVERLAPPED,
PIPE_TYPE_BYTE | PIPE_WAIT,
1, 0, 0, 0, NULL));
AutoCloseHandle readEnd(readEnd_);
KJ_WIN32(writeEnd_ = CreateFileA(pipeName.cStr(), GENERIC_WRITE, 0, NULL, OPEN_EXISTING,
FILE_ATTRIBUTE_NORMAL, NULL));
AutoCloseHandle writeEnd(writeEnd_);
auto observer = port.observeIo(readEnd);
auto op = observer->newOperation(0);
byte buffer[256];
KJ_ASSERT(!ReadFile(readEnd, buffer, sizeof(buffer), NULL, op->getOverlapped()));
DWORD error = GetLastError();
if (error != ERROR_IO_PENDING) {
KJ_FAIL_WIN32("ReadFile()", error);
}
bool done = false;
auto promise = op->onComplete().then([&](Win32EventPort::IoResult result) {
done = true;
return result;
}).eagerlyEvaluate(nullptr);
KJ_EXPECT(!done);
evalLater([]() {}).wait(waitScope);
evalLater([]() {}).wait(waitScope);
evalLater([]() {}).wait(waitScope);
evalLater([]() {}).wait(waitScope);
evalLater([]() {}).wait(waitScope);
KJ_EXPECT(!done);
DWORD bytesWritten;
KJ_WIN32(WriteFile(writeEnd, "foo", 3, &bytesWritten, NULL));
KJ_EXPECT(bytesWritten == 3);
auto result = promise.wait(waitScope);
KJ_EXPECT(result.errorCode == ERROR_SUCCESS);
KJ_EXPECT(result.bytesTransferred == 3);
KJ_EXPECT(kj::str(kj::arrayPtr(buffer, 3).asChars()) == "foo");
}
KJ_TEST("Win32IocpEventPort::wake()") {
Win32IocpEventPort port;
Thread thread([&]() {
Sleep(10);
port.wake();
});
KJ_EXPECT(port.wait());
}
KJ_TEST("Win32IocpEventPort::wake() on poll()") {
Win32IocpEventPort port;
volatile bool woken = false;
Thread thread([&]() {
Sleep(10);
port.wake();
woken = true;
});
KJ_EXPECT(!port.poll());
while (!woken) Sleep(10);
KJ_EXPECT(port.poll());
}
KJ_TEST("Win32IocpEventPort timer") {
Win32IocpEventPort port;
EventLoop loop(port);
WaitScope waitScope(loop);
auto start = port.getTimer().now();
bool done = false;
auto promise = port.getTimer().afterDelay(10 * MILLISECONDS).then([&]() {
done = true;
}).eagerlyEvaluate(nullptr);
KJ_EXPECT(!done);
evalLater([]() {}).wait(waitScope);
evalLater([]() {}).wait(waitScope);
evalLater([]() {}).wait(waitScope);
evalLater([]() {}).wait(waitScope);
evalLater([]() {}).wait(waitScope);
KJ_EXPECT(!done);
promise.wait(waitScope);
KJ_EXPECT(done);
KJ_EXPECT(port.getTimer().now() - start >= 10 * MILLISECONDS);
}
VOID CALLBACK testApcProc(ULONG_PTR dwParam) {
reinterpret_cast<kj::PromiseFulfiller<void>*>(dwParam)->fulfill();
}
KJ_TEST("Win32IocpEventPort APC") {
if (GetProcAddress(GetModuleHandle("ntdll.dll"), "wine_get_version") != nullptr) {
// TODO(cleanup): Periodically check if Wine supports this yet.
KJ_LOG(WARNING, "detected that we're running under wine and this test won't work; skipping");
return;
}
Win32IocpEventPort port;
EventLoop loop(port);
WaitScope waitScope(loop);
port.allowApc();
auto paf = kj::newPromiseAndFulfiller<void>();
KJ_WIN32(QueueUserAPC(&testApcProc, GetCurrentThread(),
reinterpret_cast<ULONG_PTR>(paf.fulfiller.get())));
paf.promise.wait(waitScope);
}
} // namespace
} // namespace kj
#endif // _WIN32