Back to home page

EIC code displayed by LXR

 
 

    


File indexing completed on 2025-01-18 09:38:32

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/detail/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>(0, *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>(0, *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                TimePoint now = get_now<TimePoint>(bool_<TimeoutEnabled>());
0172 
0173                if(now >= abs_time){
0174                   //If we can lock the mutex it means that no notification
0175                   //is being executed in this spin_condition variable
0176                   timed_out = m_enter_mut.try_lock();
0177 
0178                   //If locking fails, indicates that another thread is executing
0179                   //notification, so we play the notification game
0180                   if(!timed_out){
0181                      //There is an ongoing notification, we will try again later
0182                      continue;
0183                   }
0184                   //No notification in execution, since enter mutex is locked.
0185                   //We will execute time-out logic, so we will decrement count,
0186                   //release the enter mutex and return false.
0187                   break;
0188                }
0189             }
0190          }
0191 
0192          //If a timeout occurred, the mutex will not execute checking logic
0193          if(TimeoutEnabled && timed_out){
0194             //Decrement wait count
0195             atomic_dec32(const_cast<boost::uint32_t*>(&m_num_waiters));
0196             unlock_enter_mut = true;
0197             break;
0198          }
0199          else{
0200             boost::uint32_t result = atomic_cas32
0201                            (const_cast<boost::uint32_t*>(&m_command), SLEEP, NOTIFY_ONE);
0202             if(result == SLEEP){
0203                //Other thread has been notified and since it was a NOTIFY one
0204                //command, this thread must sleep again
0205                continue;
0206             }
0207             else if(result == NOTIFY_ONE){
0208                //If it was a NOTIFY_ONE command, only this thread should
0209                //exit. This thread has atomically marked command as sleep before
0210                //so no other thread will exit.
0211                //Decrement wait count.
0212                unlock_enter_mut = true;
0213                atomic_dec32(const_cast<boost::uint32_t*>(&m_num_waiters));
0214                break;
0215             }
0216             else{
0217                //If it is a NOTIFY_ALL command, all threads should return
0218                //from do_timed_wait function. Decrement wait count.
0219                unlock_enter_mut = 1 == atomic_dec32(const_cast<boost::uint32_t*>(&m_num_waiters));
0220                //Check if this is the last thread of notify_all waiters
0221                //Only the last thread will release the mutex
0222                if(unlock_enter_mut){
0223                   atomic_cas32(const_cast<boost::uint32_t*>(&m_command), SLEEP, NOTIFY_ALL);
0224                }
0225                break;
0226             }
0227          }
0228       }
0229 
0230       //Unlock the enter mutex if it is a single notification, if this is
0231       //the last notified thread in a notify_all or a timeout has occurred
0232       if(unlock_enter_mut){
0233          m_enter_mut.unlock();
0234       }
0235 
0236       //Lock external again before returning from the method
0237       mut.lock();
0238       return !timed_out;
0239    }
0240 
0241    template <class TimePoint>
0242    static TimePoint get_now(bool_<true>)
0243    {  return microsec_clock<TimePoint>::universal_time();  }
0244 
0245    template <class TimePoint>
0246    static TimePoint get_now(bool_<false>)
0247    {  return TimePoint();  }
0248 
0249    template <class Mutex, class Lock, class TimePoint>
0250    static void  get_lock(bool_<true>, Mutex &m, Lock &lck, const TimePoint &abs_time)
0251    { 
0252       Lock dummy(m, abs_time);
0253       lck = boost::move(dummy);
0254    }
0255 
0256    template <class Mutex, class Lock, class TimePoint>
0257    static void get_lock(bool_<false>, Mutex &m, Lock &lck, const TimePoint &)
0258    { 
0259       Lock dummy(m);
0260       lck = boost::move(dummy);
0261    }
0262 
0263    void notify(boost::uint32_t command)
0264    {
0265       //This mutex guarantees that no other thread can enter to the
0266       //do_timed_wait method logic, so that thread count will be
0267       //constant until the function writes a NOTIFY_ALL command.
0268       //It also guarantees that no other notification can be signaled
0269       //on this spin_condition before this one ends
0270       m_enter_mut.lock();
0271 
0272       //Return if there are no waiters
0273       if(!atomic_read32(&m_num_waiters)) {
0274          m_enter_mut.unlock();
0275          return;
0276       }
0277 
0278       //Notify that all threads should execute wait logic
0279       spin_wait swait;
0280       while(SLEEP != atomic_cas32(const_cast<boost::uint32_t*>(&m_command), command, SLEEP)){
0281          swait.yield();
0282       }
0283       //The enter mutex will rest locked until the last waiting thread unlocks it
0284    }
0285 
0286    enum { SLEEP = 0, NOTIFY_ONE, NOTIFY_ALL };
0287    spin_mutex  m_enter_mut;
0288    volatile boost::uint32_t    m_command;
0289    volatile boost::uint32_t    m_num_waiters;
0290 };
0291 
0292 }  //namespace ipcdetail
0293 }  //namespace interprocess
0294 }  //namespace boost
0295 
0296 #include <boost/interprocess/detail/config_end.hpp>
0297 
0298 #endif   //BOOST_INTERPROCESS_DETAIL_SPIN_CONDITION_HPP