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