annotate DEPENDENCIES/generic/include/boost/interprocess/sync/spin/condition.hpp @ 133:4acb5d8d80b6 tip

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