annotate DEPENDENCIES/generic/include/boost/interprocess/sync/spin/condition.hpp @ 16:2665513ce2d3

Add boost headers
author Chris Cannam
date Tue, 05 Aug 2014 11:11:38 +0100
parents
children c530137014c0
rev   line source
Chris@16 1 //////////////////////////////////////////////////////////////////////////////
Chris@16 2 //
Chris@16 3 // (C) Copyright Ion Gaztanaga 2005-2012. Distributed under the Boost
Chris@16 4 // Software License, Version 1.0. (See accompanying file
Chris@16 5 // LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
Chris@16 6 //
Chris@16 7 // See http://www.boost.org/libs/interprocess for documentation.
Chris@16 8 //
Chris@16 9 //////////////////////////////////////////////////////////////////////////////
Chris@16 10
Chris@16 11 #ifndef BOOST_INTERPROCESS_DETAIL_SPIN_CONDITION_HPP
Chris@16 12 #define BOOST_INTERPROCESS_DETAIL_SPIN_CONDITION_HPP
Chris@16 13
Chris@16 14 #include <boost/interprocess/detail/config_begin.hpp>
Chris@16 15 #include <boost/interprocess/detail/workaround.hpp>
Chris@16 16 #include <boost/interprocess/sync/spin/mutex.hpp>
Chris@16 17 #include <boost/interprocess/detail/posix_time_types_wrk.hpp>
Chris@16 18 #include <boost/interprocess/detail/atomic.hpp>
Chris@16 19 #include <boost/interprocess/sync/scoped_lock.hpp>
Chris@16 20 #include <boost/interprocess/exceptions.hpp>
Chris@16 21 #include <boost/interprocess/detail/os_thread_functions.hpp>
Chris@16 22 #include <boost/interprocess/sync/spin/wait.hpp>
Chris@16 23 #include <boost/move/move.hpp>
Chris@16 24 #include <boost/cstdint.hpp>
Chris@16 25
Chris@16 26 namespace boost {
Chris@16 27 namespace interprocess {
Chris@16 28 namespace ipcdetail {
Chris@16 29
Chris@16 30 class spin_condition
Chris@16 31 {
Chris@16 32 spin_condition(const spin_condition &);
Chris@16 33 spin_condition &operator=(const spin_condition &);
Chris@16 34 public:
Chris@16 35 spin_condition();
Chris@16 36 ~spin_condition();
Chris@16 37
Chris@16 38 void notify_one();
Chris@16 39 void notify_all();
Chris@16 40
Chris@16 41 template <typename L>
Chris@16 42 bool timed_wait(L& lock, const boost::posix_time::ptime &abs_time)
Chris@16 43 {
Chris@16 44 if(abs_time == boost::posix_time::pos_infin){
Chris@16 45 this->wait(lock);
Chris@16 46 return true;
Chris@16 47 }
Chris@16 48 if (!lock)
Chris@16 49 throw lock_exception();
Chris@16 50 return this->do_timed_wait(abs_time, *lock.mutex());
Chris@16 51 }
Chris@16 52
Chris@16 53 template <typename L, typename Pr>
Chris@16 54 bool timed_wait(L& lock, const boost::posix_time::ptime &abs_time, Pr pred)
Chris@16 55 {
Chris@16 56 if(abs_time == boost::posix_time::pos_infin){
Chris@16 57 this->wait(lock, pred);
Chris@16 58 return true;
Chris@16 59 }
Chris@16 60 if (!lock)
Chris@16 61 throw lock_exception();
Chris@16 62 while (!pred()){
Chris@16 63 if (!this->do_timed_wait(abs_time, *lock.mutex()))
Chris@16 64 return pred();
Chris@16 65 }
Chris@16 66 return true;
Chris@16 67 }
Chris@16 68
Chris@16 69 template <typename L>
Chris@16 70 void wait(L& lock)
Chris@16 71 {
Chris@16 72 if (!lock)
Chris@16 73 throw lock_exception();
Chris@16 74 do_wait(*lock.mutex());
Chris@16 75 }
Chris@16 76
Chris@16 77 template <typename L, typename Pr>
Chris@16 78 void wait(L& lock, Pr pred)
Chris@16 79 {
Chris@16 80 if (!lock)
Chris@16 81 throw lock_exception();
Chris@16 82
Chris@16 83 while (!pred())
Chris@16 84 do_wait(*lock.mutex());
Chris@16 85 }
Chris@16 86
Chris@16 87 template<class InterprocessMutex>
Chris@16 88 void do_wait(InterprocessMutex &mut);
Chris@16 89
Chris@16 90 template<class InterprocessMutex>
Chris@16 91 bool do_timed_wait(const boost::posix_time::ptime &abs_time, InterprocessMutex &mut);
Chris@16 92
Chris@16 93 private:
Chris@16 94 template<class InterprocessMutex>
Chris@16 95 bool do_timed_wait(bool tout_enabled, const boost::posix_time::ptime &abs_time, InterprocessMutex &mut);
Chris@16 96
Chris@16 97 enum { SLEEP = 0, NOTIFY_ONE, NOTIFY_ALL };
Chris@16 98 spin_mutex m_enter_mut;
Chris@16 99 volatile boost::uint32_t m_command;
Chris@16 100 volatile boost::uint32_t m_num_waiters;
Chris@16 101 void notify(boost::uint32_t command);
Chris@16 102 };
Chris@16 103
Chris@16 104 inline spin_condition::spin_condition()
Chris@16 105 {
Chris@16 106 //Note that this class is initialized to zero.
Chris@16 107 //So zeroed memory can be interpreted as an initialized
Chris@16 108 //condition variable
Chris@16 109 m_command = SLEEP;
Chris@16 110 m_num_waiters = 0;
Chris@16 111 }
Chris@16 112
Chris@16 113 inline spin_condition::~spin_condition()
Chris@16 114 {
Chris@16 115 //Trivial destructor
Chris@16 116 }
Chris@16 117
Chris@16 118 inline void spin_condition::notify_one()
Chris@16 119 {
Chris@16 120 this->notify(NOTIFY_ONE);
Chris@16 121 }
Chris@16 122
Chris@16 123 inline void spin_condition::notify_all()
Chris@16 124 {
Chris@16 125 this->notify(NOTIFY_ALL);
Chris@16 126 }
Chris@16 127
Chris@16 128 inline void spin_condition::notify(boost::uint32_t command)
Chris@16 129 {
Chris@16 130 //This mutex guarantees that no other thread can enter to the
Chris@16 131 //do_timed_wait method logic, so that thread count will be
Chris@16 132 //constant until the function writes a NOTIFY_ALL command.
Chris@16 133 //It also guarantees that no other notification can be signaled
Chris@16 134 //on this spin_condition before this one ends
Chris@16 135 m_enter_mut.lock();
Chris@16 136
Chris@16 137 //Return if there are no waiters
Chris@16 138 if(!atomic_read32(&m_num_waiters)) {
Chris@16 139 m_enter_mut.unlock();
Chris@16 140 return;
Chris@16 141 }
Chris@16 142
Chris@16 143 //Notify that all threads should execute wait logic
Chris@16 144 spin_wait swait;
Chris@16 145 while(SLEEP != atomic_cas32(const_cast<boost::uint32_t*>(&m_command), command, SLEEP)){
Chris@16 146 swait.yield();
Chris@16 147 }
Chris@16 148 //The enter mutex will rest locked until the last waiting thread unlocks it
Chris@16 149 }
Chris@16 150
Chris@16 151 template<class InterprocessMutex>
Chris@16 152 inline void spin_condition::do_wait(InterprocessMutex &mut)
Chris@16 153 {
Chris@16 154 this->do_timed_wait(false, boost::posix_time::ptime(), mut);
Chris@16 155 }
Chris@16 156
Chris@16 157 template<class InterprocessMutex>
Chris@16 158 inline bool spin_condition::do_timed_wait
Chris@16 159 (const boost::posix_time::ptime &abs_time, InterprocessMutex &mut)
Chris@16 160 {
Chris@16 161 return this->do_timed_wait(true, abs_time, mut);
Chris@16 162 }
Chris@16 163
Chris@16 164 template<class InterprocessMutex>
Chris@16 165 inline bool spin_condition::do_timed_wait(bool tout_enabled,
Chris@16 166 const boost::posix_time::ptime &abs_time,
Chris@16 167 InterprocessMutex &mut)
Chris@16 168 {
Chris@16 169 boost::posix_time::ptime now = microsec_clock::universal_time();
Chris@16 170
Chris@16 171 if(tout_enabled){
Chris@16 172 if(now >= abs_time) return false;
Chris@16 173 }
Chris@16 174
Chris@16 175 typedef boost::interprocess::scoped_lock<spin_mutex> InternalLock;
Chris@16 176 //The enter mutex guarantees that while executing a notification,
Chris@16 177 //no other thread can execute the do_timed_wait method.
Chris@16 178 {
Chris@16 179 //---------------------------------------------------------------
Chris@16 180 InternalLock lock;
Chris@16 181 if(tout_enabled){
Chris@16 182 InternalLock dummy(m_enter_mut, abs_time);
Chris@16 183 lock = boost::move(dummy);
Chris@16 184 }
Chris@16 185 else{
Chris@16 186 InternalLock dummy(m_enter_mut);
Chris@16 187 lock = boost::move(dummy);
Chris@16 188 }
Chris@16 189
Chris@16 190 if(!lock)
Chris@16 191 return false;
Chris@16 192 //---------------------------------------------------------------
Chris@16 193 //We increment the waiting thread count protected so that it will be
Chris@16 194 //always constant when another thread enters the notification logic.
Chris@16 195 //The increment marks this thread as "waiting on spin_condition"
Chris@16 196 atomic_inc32(const_cast<boost::uint32_t*>(&m_num_waiters));
Chris@16 197
Chris@16 198 //We unlock the external mutex atomically with the increment
Chris@16 199 mut.unlock();
Chris@16 200 }
Chris@16 201
Chris@16 202 //By default, we suppose that no timeout has happened
Chris@16 203 bool timed_out = false, unlock_enter_mut= false;
Chris@16 204
Chris@16 205 //Loop until a notification indicates that the thread should
Chris@16 206 //exit or timeout occurs
Chris@16 207 while(1){
Chris@16 208 //The thread sleeps/spins until a spin_condition commands a notification
Chris@16 209 //Notification occurred, we will lock the checking mutex so that
Chris@16 210 spin_wait swait;
Chris@16 211 while(atomic_read32(&m_command) == SLEEP){
Chris@16 212 swait.yield();
Chris@16 213
Chris@16 214 //Check for timeout
Chris@16 215 if(tout_enabled){
Chris@16 216 now = microsec_clock::universal_time();
Chris@16 217
Chris@16 218 if(now >= abs_time){
Chris@16 219 //If we can lock the mutex it means that no notification
Chris@16 220 //is being executed in this spin_condition variable
Chris@16 221 timed_out = m_enter_mut.try_lock();
Chris@16 222
Chris@16 223 //If locking fails, indicates that another thread is executing
Chris@16 224 //notification, so we play the notification game
Chris@16 225 if(!timed_out){
Chris@16 226 //There is an ongoing notification, we will try again later
Chris@16 227 continue;
Chris@16 228 }
Chris@16 229 //No notification in execution, since enter mutex is locked.
Chris@16 230 //We will execute time-out logic, so we will decrement count,
Chris@16 231 //release the enter mutex and return false.
Chris@16 232 break;
Chris@16 233 }
Chris@16 234 }
Chris@16 235 }
Chris@16 236
Chris@16 237 //If a timeout occurred, the mutex will not execute checking logic
Chris@16 238 if(tout_enabled && timed_out){
Chris@16 239 //Decrement wait count
Chris@16 240 atomic_dec32(const_cast<boost::uint32_t*>(&m_num_waiters));
Chris@16 241 unlock_enter_mut = true;
Chris@16 242 break;
Chris@16 243 }
Chris@16 244 else{
Chris@16 245 boost::uint32_t result = atomic_cas32
Chris@16 246 (const_cast<boost::uint32_t*>(&m_command), SLEEP, NOTIFY_ONE);
Chris@16 247 if(result == SLEEP){
Chris@16 248 //Other thread has been notified and since it was a NOTIFY one
Chris@16 249 //command, this thread must sleep again
Chris@16 250 continue;
Chris@16 251 }
Chris@16 252 else if(result == NOTIFY_ONE){
Chris@16 253 //If it was a NOTIFY_ONE command, only this thread should
Chris@16 254 //exit. This thread has atomically marked command as sleep before
Chris@16 255 //so no other thread will exit.
Chris@16 256 //Decrement wait count.
Chris@16 257 unlock_enter_mut = true;
Chris@16 258 atomic_dec32(const_cast<boost::uint32_t*>(&m_num_waiters));
Chris@16 259 break;
Chris@16 260 }
Chris@16 261 else{
Chris@16 262 //If it is a NOTIFY_ALL command, all threads should return
Chris@16 263 //from do_timed_wait function. Decrement wait count.
Chris@16 264 unlock_enter_mut = 1 == atomic_dec32(const_cast<boost::uint32_t*>(&m_num_waiters));
Chris@16 265 //Check if this is the last thread of notify_all waiters
Chris@16 266 //Only the last thread will release the mutex
Chris@16 267 if(unlock_enter_mut){
Chris@16 268 atomic_cas32(const_cast<boost::uint32_t*>(&m_command), SLEEP, NOTIFY_ALL);
Chris@16 269 }
Chris@16 270 break;
Chris@16 271 }
Chris@16 272 }
Chris@16 273 }
Chris@16 274
Chris@16 275 //Unlock the enter mutex if it is a single notification, if this is
Chris@16 276 //the last notified thread in a notify_all or a timeout has occurred
Chris@16 277 if(unlock_enter_mut){
Chris@16 278 m_enter_mut.unlock();
Chris@16 279 }
Chris@16 280
Chris@16 281 //Lock external again before returning from the method
Chris@16 282 mut.lock();
Chris@16 283 return !timed_out;
Chris@16 284 }
Chris@16 285
Chris@16 286 } //namespace ipcdetail
Chris@16 287 } //namespace interprocess
Chris@16 288 } //namespace boost
Chris@16 289
Chris@16 290 #include <boost/interprocess/detail/config_end.hpp>
Chris@16 291
Chris@16 292 #endif //BOOST_INTERPROCESS_DETAIL_SPIN_CONDITION_HPP