Back to home page

EIC code displayed by LXR

 
 

    


File indexing completed on 2025-12-16 10:10:03

0001 #ifndef BOOST_BASIC_TIMED_MUTEX_WIN32_HPP
0002 #define BOOST_BASIC_TIMED_MUTEX_WIN32_HPP
0003 
0004 //  basic_timed_mutex_win32.hpp
0005 //
0006 //  (C) Copyright 2006-8 Anthony Williams
0007 //  (C) Copyright 2011-2012 Vicente J. Botet Escriba
0008 //
0009 //  Distributed under the Boost Software License, Version 1.0. (See
0010 //  accompanying file LICENSE_1_0.txt or copy at
0011 //  http://www.boost.org/LICENSE_1_0.txt)
0012 
0013 #include <boost/assert.hpp>
0014 #include <boost/thread/win32/thread_primitives.hpp>
0015 #include <boost/thread/win32/interlocked_read.hpp>
0016 #include <boost/thread/thread_time.hpp>
0017 #if defined BOOST_THREAD_USES_DATETIME
0018 #include <boost/thread/xtime.hpp>
0019 #endif
0020 #include <boost/detail/interlocked.hpp>
0021 #ifdef BOOST_THREAD_USES_CHRONO
0022 #include <boost/chrono/system_clocks.hpp>
0023 #include <boost/chrono/ceil.hpp>
0024 #endif
0025 #include <boost/thread/detail/platform_time.hpp>
0026 
0027 #include <boost/config/abi_prefix.hpp>
0028 
0029 namespace boost
0030 {
0031     namespace detail
0032     {
0033         struct BOOST_THREAD_CAPABILITY("mutex") basic_timed_mutex
0034         {
0035             BOOST_STATIC_CONSTANT(unsigned char,lock_flag_bit=31);
0036             BOOST_STATIC_CONSTANT(unsigned char,event_set_flag_bit=30);
0037             BOOST_STATIC_CONSTANT(long,lock_flag_value=1<<lock_flag_bit);
0038             BOOST_STATIC_CONSTANT(long,event_set_flag_value=1<<event_set_flag_bit);
0039             long active_count;
0040             void* event;
0041 
0042             void initialize()
0043             {
0044                 active_count=0;
0045                 event=0;
0046             }
0047 
0048             void destroy()
0049             {
0050 #ifdef BOOST_MSVC
0051 #pragma warning(push)
0052 #pragma warning(disable:4312)
0053 #endif
0054                 void* const old_event=BOOST_INTERLOCKED_EXCHANGE_POINTER(&event,0);
0055 #ifdef BOOST_MSVC
0056 #pragma warning(pop)
0057 #endif
0058                 if(old_event)
0059                 {
0060                     winapi::CloseHandle(old_event);
0061                 }
0062             }
0063 
0064             // Take the lock flag if it's available
0065             bool try_lock() BOOST_NOEXCEPT BOOST_THREAD_TRY_ACQUIRE(true)
0066             {
0067                 return !win32::interlocked_bit_test_and_set(&active_count,lock_flag_bit);
0068             }
0069 
0070             void lock() BOOST_THREAD_ACQUIRE()
0071             {
0072                 if(try_lock())
0073                 {
0074                     return;
0075                 }
0076                 long old_count=active_count;
0077                 mark_waiting_and_try_lock(old_count);
0078 
0079                 if(old_count&lock_flag_value)
0080                 {
0081                     void* const sem=get_event();
0082 
0083                     do
0084                     {
0085                         if(winapi::WaitForSingleObjectEx(sem,::boost::detail::win32::infinite,0)==0)
0086                         {
0087                             clear_waiting_and_try_lock(old_count);
0088                         }
0089                     }
0090                     while(old_count&lock_flag_value);
0091                 }
0092             }
0093 
0094             // Loop until the number of waiters has been incremented or we've taken the lock flag
0095             // The loop is necessary since this function may be called by multiple threads simultaneously
0096             void mark_waiting_and_try_lock(long& old_count) BOOST_THREAD_TRY_ACQUIRE(true)
0097             {
0098                 for(;;)
0099                 {
0100                     bool const was_locked=(old_count&lock_flag_value) ? true : false;
0101                     long const new_count=was_locked?(old_count+1):(old_count|lock_flag_value);
0102                     long const current=BOOST_INTERLOCKED_COMPARE_EXCHANGE(&active_count,new_count,old_count);
0103                     if(current==old_count)
0104                     {
0105                         if(was_locked)
0106                             old_count=new_count;
0107                         // else we've taken the lock flag
0108                             // don't update old_count so that the calling function can see that
0109                             // the old lock flag was 0 and know that we've taken the lock flag
0110                         break;
0111                     }
0112                     old_count=current;
0113                 }
0114             }
0115 
0116             // Loop until someone else has taken the lock flag and cleared the event set flag or
0117             // until we've taken the lock flag and cleared the event set flag and decremented the
0118             // number of waiters
0119             // The loop is necessary since this function may be called by multiple threads simultaneously
0120             void clear_waiting_and_try_lock(long& old_count) BOOST_THREAD_TRY_ACQUIRE(true)
0121             {
0122                 old_count&=~lock_flag_value;
0123                 old_count|=event_set_flag_value;
0124                 for(;;)
0125                 {
0126                     long const new_count=((old_count&lock_flag_value)?old_count:((old_count-1)|lock_flag_value))&~event_set_flag_value;
0127                     long const current=BOOST_INTERLOCKED_COMPARE_EXCHANGE(&active_count,new_count,old_count);
0128                     if(current==old_count)
0129                     {
0130                         // if someone else has taken the lock flag
0131                             // no need to update old_count since old_count == new_count (ignoring
0132                             // event_set_flag_value which the calling function doesn't care about)
0133                         // else we've taken the lock flag
0134                             // don't update old_count so that the calling function can see that
0135                             // the old lock flag was 0 and know that we've taken the lock flag
0136                         break;
0137                     }
0138                     old_count=current;
0139                 }
0140             }
0141 
0142         private:
0143             unsigned long getMs(detail::platform_duration const& d)
0144             {
0145                 return static_cast<unsigned long>(d.getMs());
0146             }
0147 
0148             template <typename Duration>
0149             unsigned long getMs(Duration const& d)
0150             {
0151                 return static_cast<unsigned long>(chrono::ceil<chrono::milliseconds>(d).count());
0152             }
0153 
0154             template <typename Clock, typename Timepoint, typename Duration>
0155             bool do_lock_until(Timepoint const& t, Duration const& max) BOOST_THREAD_TRY_ACQUIRE(true)
0156             {
0157                 if(try_lock())
0158                 {
0159                     return true;
0160                 }
0161 
0162                 long old_count=active_count;
0163                 mark_waiting_and_try_lock(old_count);
0164 
0165                 if(old_count&lock_flag_value)
0166                 {
0167                     void* const sem=get_event();
0168 
0169                     // If the clock is the system clock, it may jump while this function
0170                     // is waiting. To compensate for this and time out near the correct
0171                     // time, we call WaitForSingleObjectEx() in a loop with a short
0172                     // timeout and recheck the time remaining each time through the loop.
0173                     do
0174                     {
0175                         Duration d(t - Clock::now());
0176                         if(d <= Duration::zero()) // timeout occurred
0177                         {
0178                             BOOST_INTERLOCKED_DECREMENT(&active_count);
0179                             return false;
0180                         }
0181                         if(max != Duration::zero())
0182                         {
0183                             d = (std::min)(d, max);
0184                         }
0185                         if(winapi::WaitForSingleObjectEx(sem,getMs(d),0)==0)
0186                         {
0187                             clear_waiting_and_try_lock(old_count);
0188                         }
0189                     }
0190                     while(old_count&lock_flag_value);
0191                 }
0192                 return true;
0193             }
0194         public:
0195 
0196 #if defined BOOST_THREAD_USES_DATETIME
0197             bool timed_lock(::boost::system_time const& wait_until)
0198             {
0199                 const detail::real_platform_timepoint t(wait_until);
0200                 return do_lock_until<detail::real_platform_clock>(t, detail::platform_milliseconds(BOOST_THREAD_POLL_INTERVAL_MILLISECONDS));
0201             }
0202 
0203             template<typename Duration>
0204             bool timed_lock(Duration const& timeout)
0205             {
0206                 const detail::mono_platform_timepoint t(detail::mono_platform_clock::now() + detail::platform_duration(timeout));
0207                 // The reference clock is steady and so no need to poll periodically, thus 0 ms max (i.e. no max)
0208                 return do_lock_until<detail::mono_platform_clock>(t, detail::platform_duration::zero());
0209             }
0210 
0211             bool timed_lock(boost::xtime const& timeout)
0212             {
0213                 return timed_lock(boost::system_time(timeout));
0214             }
0215 #endif
0216 #ifdef BOOST_THREAD_USES_CHRONO
0217             template <class Rep, class Period>
0218             bool try_lock_for(const chrono::duration<Rep, Period>& rel_time)
0219             {
0220                 const chrono::steady_clock::time_point t(chrono::steady_clock::now() + rel_time);
0221                 typedef typename chrono::duration<Rep, Period> Duration;
0222                 typedef typename common_type<Duration, typename chrono::steady_clock::duration>::type common_duration;
0223                 // The reference clock is steady and so no need to poll periodically, thus 0 ms max (i.e. no max)
0224                 return do_lock_until<chrono::steady_clock>(t, common_duration::zero());
0225             }
0226             template <class Duration>
0227             bool try_lock_until(const chrono::time_point<chrono::steady_clock, Duration>& t)
0228             {
0229                 typedef typename common_type<Duration, typename chrono::steady_clock::duration>::type common_duration;
0230                 // The reference clock is steady and so no need to poll periodically, thus 0 ms max (i.e. no max)
0231                 return do_lock_until<chrono::steady_clock>(t, common_duration::zero());
0232             }
0233             template <class Clock, class Duration>
0234             bool try_lock_until(const chrono::time_point<Clock, Duration>& t)
0235             {
0236                 typedef typename common_type<Duration, typename Clock::duration>::type common_duration;
0237                 return do_lock_until<Clock>(t, common_duration(chrono::milliseconds(BOOST_THREAD_POLL_INTERVAL_MILLISECONDS)));
0238             }
0239 #endif
0240 
0241             void unlock() BOOST_THREAD_RELEASE()
0242             {
0243                 // Clear the lock flag using atomic addition (works since long is always 32 bits on Windows)
0244                 long const old_count=BOOST_INTERLOCKED_EXCHANGE_ADD(&active_count,lock_flag_value);
0245                 // If someone is waiting to take the lock, set the event set flag and, if
0246                 // the event set flag hadn't already been set, send an event.
0247                 if(!(old_count&event_set_flag_value) && (old_count>lock_flag_value))
0248                 {
0249                     if(!win32::interlocked_bit_test_and_set(&active_count,event_set_flag_bit))
0250                     {
0251                         winapi::SetEvent(get_event());
0252                     }
0253                 }
0254             }
0255 
0256         private:
0257             // Create an event in a thread-safe way
0258             // The first thread to create the event wins and all other thread will use that event
0259             void* get_event()
0260             {
0261                 void* current_event=::boost::detail::interlocked_read_acquire(&event);
0262 
0263                 if(!current_event)
0264                 {
0265                     void* const new_event=win32::create_anonymous_event(win32::auto_reset_event,win32::event_initially_reset);
0266 #ifdef BOOST_MSVC
0267 #pragma warning(push)
0268 #pragma warning(disable:4311)
0269 #pragma warning(disable:4312)
0270 #endif
0271                     void* const old_event=BOOST_INTERLOCKED_COMPARE_EXCHANGE_POINTER(&event,new_event,0);
0272 #ifdef BOOST_MSVC
0273 #pragma warning(pop)
0274 #endif
0275                     if(old_event!=0)
0276                     {
0277                         winapi::CloseHandle(new_event);
0278                         return old_event;
0279                     }
0280                     else
0281                     {
0282                         return new_event;
0283                     }
0284                 }
0285                 return current_event;
0286             }
0287 
0288         };
0289 
0290     }
0291 }
0292 
0293 #define BOOST_BASIC_TIMED_MUTEX_INITIALIZER {0}
0294 
0295 #include <boost/config/abi_suffix.hpp>
0296 
0297 #endif