/mordor/fibersynchronization.h
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