Back to home page

EIC code displayed by LXR

 
 

    


File indexing completed on 2025-07-05 08:35:56

0001 //////////////////////////////////////////////////////////////////////////////
0002 //
0003 // (C) Copyright Ion Gaztanaga 2005-2012. Distributed under the Boost
0004 // Software License, Version 1.0. (See accompanying file
0005 // LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
0006 //
0007 // See http://www.boost.org/libs/interprocess for documentation.
0008 //
0009 //////////////////////////////////////////////////////////////////////////////
0010 
0011 #ifndef BOOST_INTERPROCESS_DETAIL_SPIN_CONDITION_HPP
0012 #define BOOST_INTERPROCESS_DETAIL_SPIN_CONDITION_HPP
0013 
0014 #ifndef BOOST_CONFIG_HPP
0015 #  include <boost/config.hpp>
0016 #endif
0017 #
0018 #if defined(BOOST_HAS_PRAGMA_ONCE)
0019 #  pragma once
0020 #endif
0021 
0022 #include <boost/interprocess/detail/config_begin.hpp>
0023 #include <boost/interprocess/detail/workaround.hpp>
0024 
0025 #include <boost/interprocess/sync/cv_status.hpp>
0026 #include <boost/interprocess/sync/spin/mutex.hpp>
0027 #include <boost/interprocess/detail/atomic.hpp>
0028 #include <boost/interprocess/sync/scoped_lock.hpp>
0029 #include <boost/interprocess/exceptions.hpp>
0030 #include <boost/interprocess/detail/os_thread_functions.hpp>
0031 #include <boost/interprocess/timed_utils.hpp>
0032 #include <boost/interprocess/sync/spin/wait.hpp>
0033 #include <boost/move/utility_core.hpp>
0034 #include <boost/cstdint.hpp>
0035 
0036 namespace boost {
0037 namespace interprocess {
0038 namespace ipcdetail {
0039 
0040 class spin_condition
0041 {
0042    spin_condition(const spin_condition &);
0043    spin_condition &operator=(const spin_condition &);
0044 
0045    public:
0046    spin_condition()
0047    {
0048       //Note that this class is initialized to zero.
0049       //So zeroed memory can be interpreted as an initialized
0050       //condition variable
0051       m_command      = SLEEP;
0052       m_num_waiters  = 0;
0053    }
0054 
0055    ~spin_condition()
0056    {
0057       //Notify all waiting threads
0058       //to allow POSIX semantics on condition destruction
0059       this->notify_all();
0060    }
0061 
0062    void notify_one()
0063    {  this->notify(NOTIFY_ONE);  }
0064 
0065    void notify_all()
0066    {  this->notify(NOTIFY_ALL);  }
0067 
0068    template <typename L>
0069    void wait(L& lock)
0070    {
0071       if (!lock)
0072          throw lock_exception();
0073       this->do_timed_wait_impl<false>(ustime(0u), *lock.mutex());
0074    }
0075 
0076    template <typename L, typename Pr>
0077    void wait(L& lock, Pr pred)
0078    {
0079       if (!lock)
0080          throw lock_exception();
0081 
0082       while (!pred())
0083          this->do_timed_wait_impl<false>(ustime(0u), *lock.mutex());
0084    }
0085 
0086    template <typename L, typename TimePoint>
0087    bool timed_wait(L& lock, const TimePoint &abs_time)
0088    {
0089       if (!lock)
0090          throw lock_exception();
0091       //Handle infinity absolute time here to avoid complications in do_timed_wait
0092       if(is_pos_infinity(abs_time)){
0093          this->wait(lock);
0094          return true;
0095       }
0096       return this->do_timed_wait_impl<true>(abs_time, *lock.mutex());
0097    }
0098 
0099    template <typename L, typename TimePoint, typename Pr>
0100    bool timed_wait(L& lock, const TimePoint &abs_time, Pr pred)
0101    {
0102       if (!lock)
0103          throw lock_exception();
0104       //Handle infinity absolute time here to avoid complications in do_timed_wait
0105       if(is_pos_infinity(abs_time)){
0106          this->wait(lock, pred);
0107          return true;
0108       }
0109       while (!pred()){
0110          if (!this->do_timed_wait_impl<true>(abs_time, *lock.mutex()))
0111             return pred();
0112       }
0113       return true;
0114    }
0115 
0116    template <typename L, class TimePoint>
0117    cv_status wait_until(L& lock, const TimePoint &abs_time)
0118    {  return this->timed_wait(lock, abs_time) ? cv_status::no_timeout : cv_status::timeout; }
0119 
0120    template <typename L, class TimePoint, typename Pr>
0121    bool wait_until(L& lock, const TimePoint &abs_time, Pr pred)
0122    {  return this->timed_wait(lock, abs_time, pred); }
0123 
0124    template <typename L, class Duration>
0125    cv_status wait_for(L& lock, const Duration &dur)
0126    {  return this->wait_until(lock, duration_to_ustime(dur)); }
0127 
0128    template <typename L, class Duration, typename Pr>
0129    bool wait_for(L& lock, const Duration &dur, Pr pred)
0130    {  return this->wait_until(lock, duration_to_ustime(dur), pred); }
0131 
0132    private:
0133 
0134    template<bool TimeoutEnabled, class InterprocessMutex, class TimePoint>
0135    bool do_timed_wait_impl(const TimePoint &abs_time, InterprocessMutex &mut)
0136    {
0137       typedef boost::interprocess::scoped_lock<spin_mutex> InternalLock;
0138       //The enter mutex guarantees that while executing a notification,
0139       //no other thread can execute the do_timed_wait method.
0140       {
0141          //---------------------------------------------------------------
0142          InternalLock lock;
0143          get_lock(bool_<TimeoutEnabled>(), m_enter_mut, lock, abs_time);
0144 
0145          if(!lock)
0146             return false;
0147          //---------------------------------------------------------------
0148          //We increment the waiting thread count protected so that it will be
0149          //always constant when another thread enters the notification logic.
0150          //The increment marks this thread as "waiting on spin_condition"
0151          atomic_inc32(const_cast<boost::uint32_t*>(&m_num_waiters));
0152 
0153          //We unlock the external mutex atomically with the increment
0154          mut.unlock();
0155       }
0156 
0157       //By default, we suppose that no timeout has happened
0158       bool timed_out  = false, unlock_enter_mut= false;
0159 
0160       //Loop until a notification indicates that the thread should
0161       //exit or timeout occurs
0162       while(1){
0163          //The thread sleeps/spins until a spin_condition commands a notification
0164          //Notification occurred, we will lock the checking mutex so that
0165          spin_wait swait;
0166          while(atomic_read32(&m_command) == SLEEP){
0167             swait.yield();
0168 
0169             //Check for timeout
0170             if(TimeoutEnabled){
0171                typedef typename microsec_clock<TimePoint>::time_point time_point;
0172                time_point now = get_now<TimePoint>(bool_<TimeoutEnabled>());
0173 
0174                if(now >= abs_time){
0175                   //If we can lock the mutex it means that no notification
0176                   //is being executed in this spin_condition variable
0177                   timed_out = m_enter_mut.try_lock();
0178 
0179                   //If locking fails, indicates that another thread is executing
0180                   //notification, so we play the notification game
0181                   if(!timed_out){
0182                      //There is an ongoing notification, we will try again later
0183                      continue;
0184                   }
0185                   //No notification in execution, since enter mutex is locked.
0186                   //We will execute time-out logic, so we will decrement count,
0187                   //release the enter mutex and return false.
0188                   break;
0189                }
0190             }
0191          }
0192 
0193          //If a timeout occurred, the mutex will not execute checking logic
0194          if(TimeoutEnabled && timed_out){
0195             //Decrement wait count
0196             atomic_dec32(const_cast<boost::uint32_t*>(&m_num_waiters));
0197             unlock_enter_mut = true;
0198             break;
0199          }
0200          else{
0201             boost::uint32_t result = atomic_cas32
0202                            (const_cast<boost::uint32_t*>(&m_command), SLEEP, NOTIFY_ONE);
0203             if(result == SLEEP){
0204                //Other thread has been notified and since it was a NOTIFY one
0205                //command, this thread must sleep again
0206                continue;
0207             }
0208             else if(result == NOTIFY_ONE){
0209                //If it was a NOTIFY_ONE command, only this thread should
0210                //exit. This thread has atomically marked command as sleep before
0211                //so no other thread will exit.
0212                //Decrement wait count.
0213                unlock_enter_mut = true;
0214                atomic_dec32(const_cast<boost::uint32_t*>(&m_num_waiters));
0215                break;
0216             }
0217             else{
0218                //If it is a NOTIFY_ALL command, all threads should return
0219                //from do_timed_wait function. Decrement wait count.
0220                unlock_enter_mut = 1 == atomic_dec32(const_cast<boost::uint32_t*>(&m_num_waiters));
0221                //Check if this is the last thread of notify_all waiters
0222                //Only the last thread will release the mutex
0223                if(unlock_enter_mut){
0224                   atomic_cas32(const_cast<boost::uint32_t*>(&m_command), SLEEP, NOTIFY_ALL);
0225                }
0226                break;
0227             }
0228          }
0229       }
0230 
0231       //Unlock the enter mutex if it is a single notification, if this is
0232       //the last notified thread in a notify_all or a timeout has occurred
0233       if(unlock_enter_mut){
0234          m_enter_mut.unlock();
0235       }
0236 
0237       //Lock external again before returning from the method
0238       mut.lock();
0239       return !timed_out;
0240    }
0241 
0242    template <class TimePoint>
0243    static typename microsec_clock<TimePoint>::time_point get_now(bool_<true>)
0244    {  return microsec_clock<TimePoint>::universal_time();  }
0245 
0246    template <class TimePoint>
0247    static typename microsec_clock<TimePoint>::time_point get_now(bool_<false>)
0248    {  return typename microsec_clock<TimePoint>::time_point();  }
0249 
0250    template <class Mutex, class Lock, class TimePoint>
0251    static void  get_lock(bool_<true>, Mutex &m, Lock &lck, const TimePoint &abs_time)
0252    { 
0253       Lock dummy(m, abs_time);
0254       lck = boost::move(dummy);
0255    }
0256 
0257    template <class Mutex, class Lock, class TimePoint>
0258    static void get_lock(bool_<false>, Mutex &m, Lock &lck, const TimePoint &)
0259    { 
0260       Lock dummy(m);
0261       lck = boost::move(dummy);
0262    }
0263 
0264    void notify(boost::uint32_t command)
0265    {
0266       //This mutex guarantees that no other thread can enter to the
0267       //do_timed_wait method logic, so that thread count will be
0268       //constant until the function writes a NOTIFY_ALL command.
0269       //It also guarantees that no other notification can be signaled
0270       //on this spin_condition before this one ends
0271       m_enter_mut.lock();
0272 
0273       //Return if there are no waiters
0274       if(!atomic_read32(&m_num_waiters)) {
0275          m_enter_mut.unlock();
0276          return;
0277       }
0278 
0279       //Notify that all threads should execute wait logic
0280       spin_wait swait;
0281       while(SLEEP != atomic_cas32(const_cast<boost::uint32_t*>(&m_command), command, SLEEP)){
0282          swait.yield();
0283       }
0284       //The enter mutex will rest locked until the last waiting thread unlocks it
0285    }
0286 
0287    enum { SLEEP = 0, NOTIFY_ONE, NOTIFY_ALL };
0288    spin_mutex  m_enter_mut;
0289    volatile boost::uint32_t    m_command;
0290    volatile boost::uint32_t    m_num_waiters;
0291 };
0292 
0293 }  //namespace ipcdetail
0294 }  //namespace interprocess
0295 }  //namespace boost
0296 
0297 #include <boost/interprocess/detail/config_end.hpp>
0298 
0299 #endif   //BOOST_INTERPROCESS_DETAIL_SPIN_CONDITION_HPP