/mordor/iomanager_iocp.h
C Header | 167 lines | 85 code | 29 blank | 53 comment | 0 complexity | 3c9e3e4ef7656d600e08e6dad7ab55d1 MD5 | raw file
1#ifndef __MORDOR_IOMANAGER_IOCP_H__ 2#define __MORDOR_IOMANAGER_IOCP_H__ 3 4#include <map> 5 6#include <boost/enable_shared_from_this.hpp> 7#include <boost/shared_ptr.hpp> 8#include <boost/thread/mutex.hpp> 9 10#include "scheduler.h" 11#include "timer.h" 12#include "version.h" 13 14#ifndef WINDOWS 15#error IOManagerIOCP is Windows only 16#endif 17 18namespace Mordor { 19 20class Fiber; 21 22struct AsyncEvent 23{ 24 AsyncEvent(); 25 26 OVERLAPPED overlapped; 27 28 Scheduler *m_scheduler; 29 tid_t m_thread; 30 boost::shared_ptr<Fiber> m_fiber; 31}; 32 33class IOManager : public Scheduler, public TimerManager 34{ 35 friend class WaitBlock; 36private: 37 class WaitBlock : public boost::enable_shared_from_this<WaitBlock> 38 { 39 public: 40 typedef boost::shared_ptr<WaitBlock> ptr; 41 public: 42 WaitBlock(IOManager &outer); 43 ~WaitBlock(); 44 45 bool registerEvent(HANDLE handle, boost::function<void ()> dg, 46 bool recurring); 47 size_t unregisterEvent(HANDLE handle); 48 49 private: 50 void run(); 51 void removeEntry(int index); 52 53 private: 54 boost::mutex m_mutex; 55 IOManager &m_outer; 56 HANDLE m_reconfigured; 57 HANDLE m_handles[MAXIMUM_WAIT_OBJECTS]; 58 Scheduler *m_schedulers[MAXIMUM_WAIT_OBJECTS]; 59 boost::shared_ptr<Fiber> m_fibers[MAXIMUM_WAIT_OBJECTS]; 60 boost::function<void ()> m_dgs[MAXIMUM_WAIT_OBJECTS]; 61 bool m_recurring[MAXIMUM_WAIT_OBJECTS]; 62 int m_inUseCount; 63 }; 64 65public: 66 IOManager(size_t threads = 1, bool useCaller = true, bool autoStart = true); 67 ~IOManager(); 68 69 bool stopping(); 70 71 // Associate the handle with the IOManagers completion port 72 // This must be called one per handle before making Windows 73 // system calls that use asynchronous IO 74 void registerFile(HANDLE handle); 75 76 // Callers who have registered a handle with registerFile() must 77 // call this method to prepare the IOManager before performing a 78 // Windows system call on that handle that expects a OVERLAPPED structure 79 // (e.g. ConnectEx, WSASend, ReadDirectoryChanges etc) 80 // The IOManager will add context information to the AsyncEvent structure. 81 // The caller must then pass the AsyncEvent::overlapped member 82 // as the lpOverlapped argument for the async IO call. 83 // After making the async call the caller will normally call yieldTo() to stop 84 // execution. The IOManager will resume the caller fiber when the IO call completes. 85 // At that point the caller can use the AsyncEvent::overlapped member to learn 86 // the result of the IO call. 87 void registerEvent(AsyncEvent *e); 88 89 // If a caller has called registerEvent to prepare for an Async IO call 90 // but then the Async IO call fails this must be called so that the IOManager 91 // does not wait for the Async IO call to complete. 92 // This does not work to cancel a successfully launched Async IO call. 93 void unregisterEvent(AsyncEvent *e); 94 95 // Register a handle to an Windows Event. 96 // The callback "dg" will be scheduled once the event 97 // is signalled. 98 void registerEvent(HANDLE handle, boost::function<void ()> dg, 99 bool recurring = false); 100 101 // Register a handle to an Windows Event. 102 // Use this method when a fiber wants to sleep until an event is signalled. 103 // The caller will typically yield its fiber immediately after 104 // calling this method and the fiber will be rescheduled as 105 // soon as the event is signalled. 106 // (see CreateEventW, WaitForMultipleObjects, WSAEventSelect) 107 // Note: See FiberEvent for a cross platform event primitive. 108 void registerEvent(HANDLE handle, bool recurring = false) 109 { registerEvent(handle, NULL, recurring); } 110 111 // Cancel the registration of event handle that was previously 112 // registered with the IOManager 113 size_t unregisterEvent(HANDLE handle); 114 115 // Cancel an Async IO call that has already been successfully launched. 116 // If successfully the fiber that is waiting for the result 117 // will be resumed and the AsyncEvent::overlapped will have the 118 // ERROR_OPERATION_ABORTED result. 119 void cancelEvent(HANDLE hFile, AsyncEvent *e); 120 121 // #111932 122 // HACK (hopefully temporary). 123 // This method allows the caller to specify the number of errors to ignore when 124 // calling PostQueuedCompletionStatus in the tickle() method. The default value is 0. 125 // The Sync product has experienced some "Insufficient system resources" errors 126 // returned by PostQueuedCompletionStatus, possibly indicating the the IOCP queue is full. 127 // Be careful when using this method as it is possible for the corresponding 128 // GetQueuedCompletionStatusEx call in idle() to wait infinitely, which could cause deadlock if 129 // the PostQueuedCompletionStatus failure is ignored. (Sync makes use of many Timers which 130 // should ensure that GetQueuedCompletionStatusEx will not ever wait infinitely.) 131 // Parameters 132 // count: The number of errors to allow 133 // seconds: The time span within which the number of errors must exceed the maximum specified in count. 134 static void setIOCPErrorTolerance(size_t count, size_t seconds); 135 136protected: 137 bool stopping(unsigned long long &nextTimeout); 138 void idle(); 139 void tickle(); 140 141 // Call when a new timer is added that will be the next timer 142 // to expire. We have to tickle() the IOManager so that it can 143 // adjust the timeout value in its blocking call to GetQueuedCompletionStatusEx 144 // so that it doesn't miss the timer 145 void onTimerInsertedAtFront() { tickle(); } 146 147private: 148 HANDLE m_hCompletionPort; 149#ifndef NDEBUG 150 std::map<OVERLAPPED *, AsyncEvent*> m_pendingEvents; 151#endif 152 size_t m_pendingEventCount; 153 boost::mutex m_mutex; 154 std::list<WaitBlock::ptr> m_waitBlocks; 155 156 // These variables are part of the hack for #111932. 157 // See the comment for setIOCPErrorTolerance(). 158 static boost::mutex m_errorMutex; 159 static size_t m_iocpAllowedErrorCount; 160 static size_t m_iocpErrorCountWindowInSeconds; 161 static size_t m_errorCount; 162 static unsigned long long m_firstErrorTime; 163}; 164 165} 166 167#endif