cannam@149: // Copyright (c) 2016 Sandstorm Development Group, Inc. and contributors cannam@149: // Licensed under the MIT License: cannam@149: // cannam@149: // Permission is hereby granted, free of charge, to any person obtaining a copy cannam@149: // of this software and associated documentation files (the "Software"), to deal cannam@149: // in the Software without restriction, including without limitation the rights cannam@149: // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell cannam@149: // copies of the Software, and to permit persons to whom the Software is cannam@149: // furnished to do so, subject to the following conditions: cannam@149: // cannam@149: // The above copyright notice and this permission notice shall be included in cannam@149: // all copies or substantial portions of the Software. cannam@149: // cannam@149: // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR cannam@149: // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, cannam@149: // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE cannam@149: // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER cannam@149: // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, cannam@149: // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN cannam@149: // THE SOFTWARE. cannam@149: cannam@149: #ifndef KJ_ASYNC_WIN32_H_ cannam@149: #define KJ_ASYNC_WIN32_H_ cannam@149: cannam@149: #if !_WIN32 cannam@149: #error "This file is Windows-specific. On Unix, include async-unix.h instead." cannam@149: #endif cannam@149: cannam@149: #include "async.h" cannam@149: #include "time.h" cannam@149: #include "io.h" cannam@149: #include cannam@149: #include cannam@149: cannam@149: // Include windows.h as lean as possible. (If you need more of the Windows API for your app, cannam@149: // #include windows.h yourself before including this header.) cannam@149: #define WIN32_LEAN_AND_MEAN 1 cannam@149: #define NOSERVICE 1 cannam@149: #define NOMCX 1 cannam@149: #define NOIME 1 cannam@149: #include cannam@149: #include "windows-sanity.h" cannam@149: cannam@149: namespace kj { cannam@149: cannam@149: class Win32EventPort: public EventPort { cannam@149: // Abstract base interface for EventPorts that can listen on Win32 event types. Due to the cannam@149: // absurd complexity of the Win32 API, it's not possible to standardize on a single cannam@149: // implementation of EventPort. In particular, there is no way for a single thread to use I/O cannam@149: // completion ports (the most efficient way of handling I/O) while at the same time waiting for cannam@149: // signalable handles or UI messages. cannam@149: // cannam@149: // Note that UI messages are not supported at all by this interface because the message queue cannam@149: // is implemented by user32.dll and we want libkj to depend only on kernel32.dll. A separate cannam@149: // compat library could provide a Win32EventPort implementation that works with the UI message cannam@149: // queue. cannam@149: cannam@149: public: cannam@149: // --------------------------------------------------------------------------- cannam@149: // overlapped I/O cannam@149: cannam@149: struct IoResult { cannam@149: DWORD errorCode; cannam@149: DWORD bytesTransferred; cannam@149: }; cannam@149: cannam@149: class IoOperation { cannam@149: public: cannam@149: virtual LPOVERLAPPED getOverlapped() = 0; cannam@149: // Gets the OVERLAPPED structure to pass to the Win32 I/O call. Do NOT modify it; just pass it cannam@149: // on. cannam@149: cannam@149: virtual Promise onComplete() = 0; cannam@149: // After making the Win32 call, if the return value indicates that the operation was cannam@149: // successfully queued (i.e. the completion event will definitely occur), call this to wait cannam@149: // for completion. cannam@149: // cannam@149: // You MUST call this if the operation was successfully queued, and you MUST NOT call this cannam@149: // otherwise. If the Win32 call failed (without queuing any operation or event) then you should cannam@149: // simply drop the IoOperation object. cannam@149: // cannam@149: // Dropping the returned Promise cancels the operation via Win32's CancelIoEx(). The destructor cannam@149: // will wait for the cancellation to complete, such that after dropping the proimse it is safe cannam@149: // to free the buffer that the operation was reading from / writing to. cannam@149: // cannam@149: // You may safely drop the `IoOperation` while still waiting for this promise. You may not, cannam@149: // however, drop the `IoObserver`. cannam@149: }; cannam@149: cannam@149: class IoObserver { cannam@149: public: cannam@149: virtual Own newOperation(uint64_t offset) = 0; cannam@149: // Begin an I/O operation. For file operations, `offset` is the offset within the file at cannam@149: // which the operation will start. For stream operations, `offset` is ignored. cannam@149: }; cannam@149: cannam@149: virtual Own observeIo(HANDLE handle) = 0; cannam@149: // Given a handle which supports overlapped I/O, arrange to receive I/O completion events via cannam@149: // this EventPort. cannam@149: // cannam@149: // Different Win32EventPort implementations may handle this in different ways, such as by using cannam@149: // completion routines (APCs) or by using I/O completion ports. The caller should not assume cannam@149: // any particular technique. cannam@149: // cannam@149: // WARNING: It is only safe to call observeIo() on a particular handle once during its lifetime. cannam@149: // You cannot observe the same handle from multiple Win32EventPorts, even if not at the same cannam@149: // time. This is because the Win32 API provides no way to disassociate a handle from an I/O cannam@149: // completion port once it is associated. cannam@149: cannam@149: // --------------------------------------------------------------------------- cannam@149: // signalable handles cannam@149: // cannam@149: // Warning: Due to limitations in the Win32 API, implementations of EventPort may be forced to cannam@149: // spawn additional threads to wait for signaled objects. This is necessary if the EventPort cannam@149: // implementation is based on I/O completion ports, or if you need to wait on more than 64 cannam@149: // handles at once. cannam@149: cannam@149: class SignalObserver { cannam@149: public: cannam@149: virtual Promise onSignaled() = 0; cannam@149: // Returns a promise that completes the next time the handle enters the signaled state. cannam@149: // cannam@149: // Depending on the type of handle, the handle may automatically be reset to a non-signaled cannam@149: // state before the promise resolves. The underlying implementaiton uses WaitForSingleObject() cannam@149: // or an equivalent wait call, so check the documentation for that to understand the semantics. cannam@149: // cannam@149: // If the handle is a mutex and it is abandoned without being unlocked, the promise breaks with cannam@149: // an exception. cannam@149: cannam@149: virtual Promise onSignaledOrAbandoned() = 0; cannam@149: // Like onSingaled(), but instead of throwing when a mutex is abandoned, resolves to `true`. cannam@149: // Resolves to `false` for non-abandoned signals. cannam@149: }; cannam@149: cannam@149: virtual Own observeSignalState(HANDLE handle) = 0; cannam@149: // Given a handle that supports waiting for it to become "signaled" via WaitForSingleObject(), cannam@149: // return an object that can wait for this state using the EventPort. cannam@149: cannam@149: // --------------------------------------------------------------------------- cannam@149: // APCs cannam@149: cannam@149: virtual void allowApc() = 0; cannam@149: // If this is ever called, the Win32EventPort will switch modes so that APCs can be scheduled cannam@149: // on the thread, e.g. through the Win32 QueueUserAPC() call. In the future, this may be enabled cannam@149: // by default. However, as of this writing, Wine does not support the necessary cannam@149: // GetQueuedCompletionStatusEx() call, thus allowApc() breaks Wine support. (Tested on Wine cannam@149: // 1.8.7.) cannam@149: // cannam@149: // If the event port implementation can't support APCs for some reason, this throws. cannam@149: cannam@149: // --------------------------------------------------------------------------- cannam@149: // time cannam@149: cannam@149: virtual Timer& getTimer() = 0; cannam@149: }; cannam@149: cannam@149: class Win32WaitObjectThreadPool { cannam@149: // Helper class that implements Win32EventPort::observeSignalState() by spawning additional cannam@149: // threads as needed to perform the actual waiting. cannam@149: // cannam@149: // This class is intended to be used to assist in building Win32EventPort implementations. cannam@149: cannam@149: public: cannam@149: Win32WaitObjectThreadPool(uint mainThreadCount = 0); cannam@149: // `mainThreadCount` indicates the number of objects the main thread is able to listen on cannam@149: // directly. Typically this would be zero (e.g. if the main thread watches an I/O completion cannam@149: // port) or MAXIMUM_WAIT_OBJECTS (e.g. if the main thread is a UI thread but can use cannam@149: // MsgWaitForMultipleObjectsEx() to wait on some handles at the same time as messages). cannam@149: cannam@149: Own observeSignalState(HANDLE handle); cannam@149: // Implemetns Win32EventPort::observeSignalState(). cannam@149: cannam@149: uint prepareMainThreadWait(HANDLE* handles[]); cannam@149: // Call immediately before invoking WaitForMultipleObjects() or similar in the main thread. cannam@149: // Fills in `handles` with the handle pointers to wait on, and returns the number of handles cannam@149: // in this array. (The array should be allocated to be at least the size passed to the cannam@149: // constructor). cannam@149: // cannam@149: // There's no need to call this if `mainThreadCount` as passed to the constructor was zero. cannam@149: cannam@149: bool finishedMainThreadWait(DWORD returnCode); cannam@149: // Call immediately after invoking WaitForMultipleObjects() or similar in the main thread, cannam@149: // passing the value returend by that call. Returns true if the event indicated by `returnCode` cannam@149: // has been handled (i.e. it was WAIT_OBJECT_n or WAIT_ABANDONED_n where n is in-range for the cannam@149: // last call to prepareMainThreadWait()). cannam@149: }; cannam@149: cannam@149: class Win32IocpEventPort final: public Win32EventPort { cannam@149: // An EventPort implementation which uses Windows I/O completion ports to listen for events. cannam@149: // cannam@149: // With this implementation, observeSignalState() requires spawning a separate thread. cannam@149: cannam@149: public: cannam@149: Win32IocpEventPort(); cannam@149: ~Win32IocpEventPort() noexcept(false); cannam@149: cannam@149: // implements EventPort ------------------------------------------------------ cannam@149: bool wait() override; cannam@149: bool poll() override; cannam@149: void wake() const override; cannam@149: cannam@149: // implements Win32IocpEventPort --------------------------------------------- cannam@149: Own observeIo(HANDLE handle) override; cannam@149: Own observeSignalState(HANDLE handle) override; cannam@149: Timer& getTimer() override { return timerImpl; } cannam@149: void allowApc() override { isAllowApc = true; } cannam@149: cannam@149: private: cannam@149: class IoPromiseAdapter; cannam@149: class IoOperationImpl; cannam@149: class IoObserverImpl; cannam@149: cannam@149: AutoCloseHandle iocp; cannam@149: AutoCloseHandle thread; cannam@149: Win32WaitObjectThreadPool waitThreads; cannam@149: TimerImpl timerImpl; cannam@149: mutable std::atomic sentWake {false}; cannam@149: bool isAllowApc = false; cannam@149: cannam@149: static TimePoint readClock(); cannam@149: cannam@149: void waitIocp(DWORD timeoutMs); cannam@149: // Wait on the I/O completion port for up to timeoutMs and pump events. Does not advance the cannam@149: // timer; caller must do that. cannam@149: cannam@149: bool receivedWake(); cannam@149: cannam@149: static AutoCloseHandle newIocpHandle(); cannam@149: static AutoCloseHandle openCurrentThread(); cannam@149: }; cannam@149: cannam@149: } // namespace kj cannam@149: cannam@149: #endif // KJ_ASYNC_WIN32_H_