1 // autoreset event for generic semaphores
2 //
3 // Copyright (C) 2013 Tim Blechmann
4 // Copyright (C) 2013 Andrey Semashev
5 //
6 // Distributed under the Boost Software License, Version 1.0. (See
7 // accompanying file LICENSE_1_0.txt or copy at
8 // http://www.boost.org/LICENSE_1_0.txt)
9 
10 #ifndef BOOST_SYNC_DETAIL_EVENTS_AUTO_RESET_EVENT_SEMAPHORE_HPP_INCLUDED_
11 #define BOOST_SYNC_DETAIL_EVENTS_AUTO_RESET_EVENT_SEMAPHORE_HPP_INCLUDED_
12 
13 #include <cstddef>
14 #include <boost/cstdint.hpp>
15 #include <boost/utility/enable_if.hpp>
16 #include <boost/sync/detail/config.hpp>
17 #include <boost/sync/detail/atomic.hpp>
18 #include <boost/sync/detail/pause.hpp>
19 #include <boost/sync/detail/time_traits.hpp>
20 #include <boost/sync/semaphore.hpp>
21 #include <boost/sync/detail/header.hpp>
22 
23 #ifdef BOOST_HAS_PRAGMA_ONCE
24 #pragma once
25 #endif
26 
27 namespace boost {
28 
29 namespace sync {
30 
31 BOOST_SYNC_DETAIL_OPEN_ABI_NAMESPACE {
32 
33 class auto_reset_event
34 {
35     BOOST_DELETED_FUNCTION(auto_reset_event(auto_reset_event const&))
36     BOOST_DELETED_FUNCTION(auto_reset_event& operator= (auto_reset_event const&));
37 
38 public:
39     auto_reset_event() : m_state(0)
40     {
41     }
42 
43     void post() BOOST_NOEXCEPT
44     {
45         int32_t old_state = m_state.load(detail::atomic_ns::memory_order_acquire);
46         if (old_state >= 0)
47         {
48             for (;;)
49             {
50                 if (m_state.compare_exchange_weak(old_state, old_state - 1, detail::atomic_ns::memory_order_release, detail::atomic_ns::memory_order_acquire))
51                 {
52                     m_sem.post();
53                     return; // avoid unnecessary fence
54                 }
55 
56                 if (old_state < 0)
57                     break; // someone else has set the auto_reset_event with no waiters
58 
59                 detail::pause();
60             }
61         }
62 
63         detail::atomic_ns::atomic_thread_fence(detail::atomic_ns::memory_order_release);
64     }
65 
66     void wait()
67     {
68         m_state.fetch_add(1, detail::atomic_ns::memory_order_acquire);
69         m_sem.wait();
70     }
71 
72     bool try_wait()
73     {
74         m_state.fetch_add(1, detail::atomic_ns::memory_order_acquire);
75 
76         const bool wait_successful = m_sem.try_wait();
77         if (wait_successful)
78             return true;
79 
80         m_state.fetch_add(-1, detail::atomic_ns::memory_order_relaxed);
81         return false;
82     }
83 
84     template< typename Time >
85     typename enable_if_c< sync::detail::time_traits< Time >::is_specialized, bool >::type timed_wait(Time const& timeout)
86     {
87         m_state.fetch_add(1, detail::atomic_ns::memory_order_acquire);
88 
89         const bool wait_successful = m_sem.timed_wait(timeout);
90         if (wait_successful)
91             return true;
92 
93         m_state.fetch_add(-1, detail::atomic_ns::memory_order_relaxed);
94         return false;
95     }
96 
97     template< typename Duration >
98     typename enable_if< detail::is_time_tag_of< Duration, detail::time_duration_tag >, bool >::type wait_for(Duration const& duration)
99     {
100         return timed_wait(duration);
101     }
102 
103     template< typename TimePoint >
104     typename enable_if< detail::is_time_tag_of< TimePoint, detail::time_point_tag >, bool >::type wait_until(TimePoint const& abs_time)
105     {
106         return timed_wait(abs_time);
107     }
108 
109 private:
110     semaphore m_sem;
111     detail::atomic_ns::atomic< int32_t > m_state;
112 };
113 
114 } // namespace abi
115 
116 } // namespace sync
117 
118 } // namespace boost
119 
120 #include <boost/sync/detail/footer.hpp>
121 
122 #endif // BOOST_SYNC_DETAIL_EVENTS_AUTO_RESET_EVENT_SEMAPHORE_HPP_INCLUDED_
123