async-win32.h (9992B)
1 // Copyright (c) 2016 Sandstorm Development Group, Inc. and contributors 2 // Licensed under the MIT License: 3 // 4 // Permission is hereby granted, free of charge, to any person obtaining a copy 5 // of this software and associated documentation files (the "Software"), to deal 6 // in the Software without restriction, including without limitation the rights 7 // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 // copies of the Software, and to permit persons to whom the Software is 9 // furnished to do so, subject to the following conditions: 10 // 11 // The above copyright notice and this permission notice shall be included in 12 // all copies or substantial portions of the Software. 13 // 14 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 20 // THE SOFTWARE. 21 22 #pragma once 23 24 #if !_WIN32 25 #error "This file is Windows-specific. On Unix, include async-unix.h instead." 26 #endif 27 28 // Include windows.h as lean as possible. (If you need more of the Windows API for your app, 29 // #include windows.h yourself before including this header.) 30 #include "win32-api-version.h" 31 32 #include "async.h" 33 #include "timer.h" 34 #include "io.h" 35 #include <atomic> 36 #include <inttypes.h> 37 38 #include <windows.h> 39 #include "windows-sanity.h" 40 41 namespace kj { 42 43 class Win32EventPort: public EventPort { 44 // Abstract base interface for EventPorts that can listen on Win32 event types. Due to the 45 // absurd complexity of the Win32 API, it's not possible to standardize on a single 46 // implementation of EventPort. In particular, there is no way for a single thread to use I/O 47 // completion ports (the most efficient way of handling I/O) while at the same time waiting for 48 // signalable handles or UI messages. 49 // 50 // Note that UI messages are not supported at all by this interface because the message queue 51 // is implemented by user32.dll and we want libkj to depend only on kernel32.dll. A separate 52 // compat library could provide a Win32EventPort implementation that works with the UI message 53 // queue. 54 55 public: 56 // --------------------------------------------------------------------------- 57 // overlapped I/O 58 59 struct IoResult { 60 DWORD errorCode; 61 DWORD bytesTransferred; 62 }; 63 64 class IoOperation { 65 public: 66 virtual LPOVERLAPPED getOverlapped() = 0; 67 // Gets the OVERLAPPED structure to pass to the Win32 I/O call. Do NOT modify it; just pass it 68 // on. 69 70 virtual Promise<IoResult> onComplete() = 0; 71 // After making the Win32 call, if the return value indicates that the operation was 72 // successfully queued (i.e. the completion event will definitely occur), call this to wait 73 // for completion. 74 // 75 // You MUST call this if the operation was successfully queued, and you MUST NOT call this 76 // otherwise. If the Win32 call failed (without queuing any operation or event) then you should 77 // simply drop the IoOperation object. 78 // 79 // Dropping the returned Promise cancels the operation via Win32's CancelIoEx(). The destructor 80 // will wait for the cancellation to complete, such that after dropping the proimse it is safe 81 // to free the buffer that the operation was reading from / writing to. 82 // 83 // You may safely drop the `IoOperation` while still waiting for this promise. You may not, 84 // however, drop the `IoObserver`. 85 }; 86 87 class IoObserver { 88 public: 89 virtual Own<IoOperation> newOperation(uint64_t offset) = 0; 90 // Begin an I/O operation. For file operations, `offset` is the offset within the file at 91 // which the operation will start. For stream operations, `offset` is ignored. 92 }; 93 94 virtual Own<IoObserver> observeIo(HANDLE handle) = 0; 95 // Given a handle which supports overlapped I/O, arrange to receive I/O completion events via 96 // this EventPort. 97 // 98 // Different Win32EventPort implementations may handle this in different ways, such as by using 99 // completion routines (APCs) or by using I/O completion ports. The caller should not assume 100 // any particular technique. 101 // 102 // WARNING: It is only safe to call observeIo() on a particular handle once during its lifetime. 103 // You cannot observe the same handle from multiple Win32EventPorts, even if not at the same 104 // time. This is because the Win32 API provides no way to disassociate a handle from an I/O 105 // completion port once it is associated. 106 107 // --------------------------------------------------------------------------- 108 // signalable handles 109 // 110 // Warning: Due to limitations in the Win32 API, implementations of EventPort may be forced to 111 // spawn additional threads to wait for signaled objects. This is necessary if the EventPort 112 // implementation is based on I/O completion ports, or if you need to wait on more than 64 113 // handles at once. 114 115 class SignalObserver { 116 public: 117 virtual Promise<void> onSignaled() = 0; 118 // Returns a promise that completes the next time the handle enters the signaled state. 119 // 120 // Depending on the type of handle, the handle may automatically be reset to a non-signaled 121 // state before the promise resolves. The underlying implementaiton uses WaitForSingleObject() 122 // or an equivalent wait call, so check the documentation for that to understand the semantics. 123 // 124 // If the handle is a mutex and it is abandoned without being unlocked, the promise breaks with 125 // an exception. 126 127 virtual Promise<bool> onSignaledOrAbandoned() = 0; 128 // Like onSignaled(), but instead of throwing when a mutex is abandoned, resolves to `true`. 129 // Resolves to `false` for non-abandoned signals. 130 }; 131 132 virtual Own<SignalObserver> observeSignalState(HANDLE handle) = 0; 133 // Given a handle that supports waiting for it to become "signaled" via WaitForSingleObject(), 134 // return an object that can wait for this state using the EventPort. 135 136 // --------------------------------------------------------------------------- 137 // APCs 138 139 virtual void allowApc() = 0; 140 // If this is ever called, the Win32EventPort will switch modes so that APCs can be scheduled 141 // on the thread, e.g. through the Win32 QueueUserAPC() call. In the future, this may be enabled 142 // by default. However, as of this writing, Wine does not support the necessary 143 // GetQueuedCompletionStatusEx() call, thus allowApc() breaks Wine support. (Tested on Wine 144 // 1.8.7.) 145 // 146 // If the event port implementation can't support APCs for some reason, this throws. 147 148 // --------------------------------------------------------------------------- 149 // time 150 151 virtual Timer& getTimer() = 0; 152 }; 153 154 class Win32WaitObjectThreadPool { 155 // Helper class that implements Win32EventPort::observeSignalState() by spawning additional 156 // threads as needed to perform the actual waiting. 157 // 158 // This class is intended to be used to assist in building Win32EventPort implementations. 159 160 public: 161 Win32WaitObjectThreadPool(uint mainThreadCount = 0); 162 // `mainThreadCount` indicates the number of objects the main thread is able to listen on 163 // directly. Typically this would be zero (e.g. if the main thread watches an I/O completion 164 // port) or MAXIMUM_WAIT_OBJECTS (e.g. if the main thread is a UI thread but can use 165 // MsgWaitForMultipleObjectsEx() to wait on some handles at the same time as messages). 166 167 Own<Win32EventPort::SignalObserver> observeSignalState(HANDLE handle); 168 // Implemetns Win32EventPort::observeSignalState(). 169 170 uint prepareMainThreadWait(HANDLE* handles[]); 171 // Call immediately before invoking WaitForMultipleObjects() or similar in the main thread. 172 // Fills in `handles` with the handle pointers to wait on, and returns the number of handles 173 // in this array. (The array should be allocated to be at least the size passed to the 174 // constructor). 175 // 176 // There's no need to call this if `mainThreadCount` as passed to the constructor was zero. 177 178 bool finishedMainThreadWait(DWORD returnCode); 179 // Call immediately after invoking WaitForMultipleObjects() or similar in the main thread, 180 // passing the value returend by that call. Returns true if the event indicated by `returnCode` 181 // has been handled (i.e. it was WAIT_OBJECT_n or WAIT_ABANDONED_n where n is in-range for the 182 // last call to prepareMainThreadWait()). 183 }; 184 185 class Win32IocpEventPort final: public Win32EventPort { 186 // An EventPort implementation which uses Windows I/O completion ports to listen for events. 187 // 188 // With this implementation, observeSignalState() requires spawning a separate thread. 189 190 public: 191 Win32IocpEventPort(); 192 ~Win32IocpEventPort() noexcept(false); 193 194 // implements EventPort ------------------------------------------------------ 195 bool wait() override; 196 bool poll() override; 197 void wake() const override; 198 199 // implements Win32IocpEventPort --------------------------------------------- 200 Own<IoObserver> observeIo(HANDLE handle) override; 201 Own<SignalObserver> observeSignalState(HANDLE handle) override; 202 Timer& getTimer() override { return timerImpl; } 203 void allowApc() override { isAllowApc = true; } 204 205 private: 206 class IoPromiseAdapter; 207 class IoOperationImpl; 208 class IoObserverImpl; 209 210 const MonotonicClock& clock; 211 212 AutoCloseHandle iocp; 213 AutoCloseHandle thread; 214 Win32WaitObjectThreadPool waitThreads; 215 TimerImpl timerImpl; 216 mutable std::atomic<bool> sentWake {false}; 217 bool isAllowApc = false; 218 219 void waitIocp(DWORD timeoutMs); 220 // Wait on the I/O completion port for up to timeoutMs and pump events. Does not advance the 221 // timer; caller must do that. 222 223 bool receivedWake(); 224 225 static AutoCloseHandle newIocpHandle(); 226 static AutoCloseHandle openCurrentThread(); 227 }; 228 229 } // namespace kj