| // 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 |