PageRenderTime 164ms CodeModel.GetById 81ms app.highlight 12ms RepoModel.GetById 68ms app.codeStats 0ms

/mordor/fibersynchronization.h

http://github.com/mozy/mordor
C Header | 235 lines | 131 code | 33 blank | 71 comment | 5 complexity | 4d62780114c97e06f225fc703f06aab8 MD5 | raw file
  1#ifndef __MORDOR_FIBERSYNCHRONIZATION_H__
  2#define __MORDOR_FIBERSYNCHRONIZATION_H__
  3// Copyright (c) 2009 - Mozy, Inc.
  4
  5#include <list>
  6
  7#include <boost/noncopyable.hpp>
  8#include <boost/shared_ptr.hpp>
  9#include <boost/thread/mutex.hpp>
 10
 11namespace Mordor {
 12
 13class Fiber;
 14class Scheduler;
 15
 16/// Scheduler based Mutex for Fibers
 17
 18/// Type that will lock the mutex on construction, and unlock on
 19/// destruction
 20template<class Mutex> struct ScopedLockImpl
 21{
 22public:
 23    ScopedLockImpl(Mutex &mutex)
 24        : m_mutex(mutex)
 25    {
 26        m_mutex.lock();
 27        m_locked = true;
 28    }
 29    ~ScopedLockImpl()
 30    { unlock(); }
 31
 32    void lock()
 33    {
 34        if (!m_locked) {
 35            m_mutex.lock();
 36            m_locked = true;
 37        }
 38    }
 39
 40    void unlock()
 41    {
 42        if (m_locked) {
 43            m_mutex.unlock();
 44            m_locked = false;
 45        }
 46    }
 47
 48    bool unlockIfNotUnique()
 49    {
 50        if (!m_locked) {
 51            return true;
 52        }
 53        if (m_mutex.unlockIfNotUnique()) {
 54            m_locked = false;
 55            return true;
 56        } else {
 57            return false;
 58        }
 59    }
 60
 61private:
 62    Mutex &m_mutex;
 63    bool m_locked;
 64};
 65
 66/// Mutex for use by Fibers that yields to a Scheduler instead of blocking
 67/// if the mutex cannot be immediately acquired.  It also provides the
 68/// additional guarantee that it is strictly FIFO, instead of random which
 69/// Fiber will acquire the mutex next after it is released.
 70struct FiberMutex : boost::noncopyable
 71{
 72    friend struct FiberCondition;
 73public:
 74    typedef ScopedLockImpl<FiberMutex> ScopedLock;
 75
 76public:
 77    ~FiberMutex();
 78
 79    /// @brief Locks the mutex
 80    /// Note that it is possible for this Fiber to switch threads after this
 81    /// method, though it is guaranteed to still be on the same Scheduler
 82    /// @pre Scheduler::getThis() != NULL
 83    /// @pre Fiber::getThis() does not own this mutex
 84    /// @post Fiber::getThis() owns this mutex
 85    void lock();
 86    /// @brief Unlocks the mutex
 87    /// @pre Fiber::getThis() owns this mutex
 88    void unlock();
 89
 90    /// Unlocks the mutex if there are other Fibers waiting for the mutex.
 91    /// This is useful if there is extra work should be done if there is no one
 92    /// else waiting (such as flushing a buffer).
 93    /// @return If the mutex was unlocked
 94    bool unlockIfNotUnique();
 95
 96private:
 97    void unlockNoLock();
 98
 99private:
100    boost::mutex m_mutex;
101    boost::shared_ptr<Fiber> m_owner;
102    std::list<std::pair<Scheduler *, boost::shared_ptr<Fiber> > > m_waiters;
103};
104
105struct RecursiveFiberMutex : boost::noncopyable
106{
107public:
108    typedef ScopedLockImpl<RecursiveFiberMutex> ScopedLock;
109public:
110    RecursiveFiberMutex() : m_recursion(0) {}
111    ~RecursiveFiberMutex();
112    /// @brief Locks the mutex
113    /// Note that it is possible for this Fiber to switch threads after this
114    /// method, though it is guaranteed to still be on the same Scheduler
115    /// @pre Scheduler::getThis() != NULL
116    /// @post Fiber::getThis() owns this mutex
117    void lock();
118    /// @brief Unlocks the mutex
119    /// @pre Fiber::getThis() owns this mutex
120    void unlock();
121    /// Unlocks the mutex if there are other Fibers waiting for the mutex.
122    /// This is useful if there is extra work should be done if there is no one
123    /// else waiting (such as flushing a buffer).
124    /// @return If the mutex was unlocked
125    /// @note the mutex is not completely released if current fiber is holding
126    ///       it muliple times at the mean time
127    bool unlockIfNotUnique();
128
129private:
130    void unlockNoLock();
131
132private:
133    boost::mutex m_mutex;
134    boost::shared_ptr<Fiber> m_owner;
135    std::list<std::pair<Scheduler *, boost::shared_ptr<Fiber> > > m_waiters;
136    unsigned m_recursion;
137};
138
139/// Scheduler based Semaphore for Fibers
140
141/// Semaphore for use by Fibers that yields to a Scheduler instead of blocking
142/// if the mutex cannot be immediately acquired.  It also provides the
143/// additional guarantee that it is strictly FIFO, instead of random which
144/// Fiber will acquire the semaphore next after it is released.
145struct FiberSemaphore : boost::noncopyable
146{
147public:
148    FiberSemaphore(size_t initialConcurrency = 0);
149    ~FiberSemaphore();
150
151    /// @brief Waits for the semaphore
152    /// Decreases the amount of concurrency.  If concurrency is already at
153    /// zero, it will wait for someone else to notify the semaphore
154    /// @note It is possible for this Fiber to switch threads after this
155    /// method, though it is guaranteed to still be on the same Scheduler
156    /// @pre Scheduler::getThis() != NULL
157    void wait();
158    /// @brief Increases the level of concurrency
159    void notify();
160
161private:
162    boost::mutex m_mutex;
163    std::list<std::pair<Scheduler *, boost::shared_ptr<Fiber> > > m_waiters;
164    size_t m_concurrency;
165};
166
167/// Scheduler based condition variable for Fibers
168
169/// Condition for use by Fibers that yields to a Scheduler instead of blocking.
170/// It also provides the additional guarantee that it is strictly FIFO,
171/// instead of random which waiting Fiber will be released when the condition
172/// is signalled.
173struct FiberCondition : boost::noncopyable
174{
175public:
176    /// @param mutex The mutex to associate with the Condition
177    FiberCondition(FiberMutex &mutex)
178        : m_fiberMutex(mutex)
179    {}
180    ~FiberCondition();
181
182    /// @brief Wait for the Condition to be signalled
183    /// @details
184    /// Atomically unlock mutex, and wait for the Condition to be signalled.
185    /// Once released, the mutex is locked again.
186    /// @pre Scheduler::getThis() != NULL
187    /// @pre Fiber::getThis() owns mutex
188    /// @post Fiber::getThis() owns mutex
189    void wait();
190    /// Release a single Fiber from wait()
191    void signal();
192    /// Release all waiting Fibers
193    void broadcast();
194
195private:
196    boost::mutex m_mutex;
197    FiberMutex &m_fiberMutex;
198    std::list<std::pair<Scheduler *, boost::shared_ptr<Fiber> > > m_waiters;
199};
200
201/// Scheduler based event variable for Fibers
202
203/// Event for use by Fibers that yields to a Scheduler instead of blocking.
204/// It also provides the additional guarantee that it is strictly FIFO,
205/// instead of random which waiting Fiber will be released when the event
206/// is signalled.
207struct FiberEvent : boost::noncopyable
208{
209public:
210    /// @param autoReset If the Event should automatically reset itself
211    /// whenever a Fiber is released
212    FiberEvent(bool autoReset = true)
213        : m_signalled(false),
214          m_autoReset(autoReset)
215    {}
216    ~FiberEvent();
217
218    /// @brief Wait for the Event to become set
219    /// @pre Scheduler::getThis() != NULL
220    void wait();
221    /// Set the Event
222    void set();
223    /// Reset the Event
224    void reset();
225
226private:
227    boost::mutex m_mutex;
228    bool m_signalled;
229    const bool m_autoReset;
230    std::list<std::pair<Scheduler *, boost::shared_ptr<Fiber> > > m_waiters;
231};
232
233}
234
235#endif