PageRenderTime 163ms CodeModel.GetById 60ms app.highlight 9ms RepoModel.GetById 92ms app.codeStats 0ms

/mordor/iomanager_iocp.h

http://github.com/mozy/mordor
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