1 //
2 // detail/std_event.hpp
3 // ~~~~~~~~~~~~~~~~~~~~
4 //
5 // Copyright (c) 2003-2021 Christopher M. Kohlhoff (chris at kohlhoff dot com)
6 //
7 // Distributed under the Boost Software License, Version 1.0. (See accompanying
8 // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
9 //
10 
11 #ifndef BOOST_ASIO_DETAIL_STD_EVENT_HPP
12 #define BOOST_ASIO_DETAIL_STD_EVENT_HPP
13 
14 #if defined(_MSC_VER) && (_MSC_VER >= 1200)
15 # pragma once
16 #endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
17 
18 #include <boost/asio/detail/config.hpp>
19 
20 #if defined(BOOST_ASIO_HAS_STD_MUTEX_AND_CONDVAR)
21 
22 #include <chrono>
23 #include <condition_variable>
24 #include <boost/asio/detail/assert.hpp>
25 #include <boost/asio/detail/noncopyable.hpp>
26 
27 #include <boost/asio/detail/push_options.hpp>
28 
29 namespace boost {
30 namespace asio {
31 namespace detail {
32 
33 class std_event
34   : private noncopyable
35 {
36 public:
37   // Constructor.
std_event()38   std_event()
39     : state_(0)
40   {
41   }
42 
43   // Destructor.
~std_event()44   ~std_event()
45   {
46   }
47 
48   // Signal the event. (Retained for backward compatibility.)
49   template <typename Lock>
signal(Lock & lock)50   void signal(Lock& lock)
51   {
52     this->signal_all(lock);
53   }
54 
55   // Signal all waiters.
56   template <typename Lock>
signal_all(Lock & lock)57   void signal_all(Lock& lock)
58   {
59     BOOST_ASIO_ASSERT(lock.locked());
60     (void)lock;
61     state_ |= 1;
62     cond_.notify_all();
63   }
64 
65   // Unlock the mutex and signal one waiter.
66   template <typename Lock>
unlock_and_signal_one(Lock & lock)67   void unlock_and_signal_one(Lock& lock)
68   {
69     BOOST_ASIO_ASSERT(lock.locked());
70     state_ |= 1;
71     bool have_waiters = (state_ > 1);
72     lock.unlock();
73     if (have_waiters)
74       cond_.notify_one();
75   }
76 
77   // Unlock the mutex and signal one waiter who may destroy us.
78   template <typename Lock>
unlock_and_signal_one_for_destruction(Lock & lock)79   void unlock_and_signal_one_for_destruction(Lock& lock)
80   {
81     BOOST_ASIO_ASSERT(lock.locked());
82     state_ |= 1;
83     bool have_waiters = (state_ > 1);
84     if (have_waiters)
85       cond_.notify_one();
86     lock.unlock();
87   }
88 
89   // If there's a waiter, unlock the mutex and signal it.
90   template <typename Lock>
maybe_unlock_and_signal_one(Lock & lock)91   bool maybe_unlock_and_signal_one(Lock& lock)
92   {
93     BOOST_ASIO_ASSERT(lock.locked());
94     state_ |= 1;
95     if (state_ > 1)
96     {
97       lock.unlock();
98       cond_.notify_one();
99       return true;
100     }
101     return false;
102   }
103 
104   // Reset the event.
105   template <typename Lock>
clear(Lock & lock)106   void clear(Lock& lock)
107   {
108     BOOST_ASIO_ASSERT(lock.locked());
109     (void)lock;
110     state_ &= ~std::size_t(1);
111   }
112 
113   // Wait for the event to become signalled.
114   template <typename Lock>
wait(Lock & lock)115   void wait(Lock& lock)
116   {
117     BOOST_ASIO_ASSERT(lock.locked());
118     unique_lock_adapter u_lock(lock);
119     while ((state_ & 1) == 0)
120     {
121       waiter w(state_);
122       cond_.wait(u_lock.unique_lock_);
123     }
124   }
125 
126   // Timed wait for the event to become signalled.
127   template <typename Lock>
wait_for_usec(Lock & lock,long usec)128   bool wait_for_usec(Lock& lock, long usec)
129   {
130     BOOST_ASIO_ASSERT(lock.locked());
131     unique_lock_adapter u_lock(lock);
132     if ((state_ & 1) == 0)
133     {
134       waiter w(state_);
135       cond_.wait_for(u_lock.unique_lock_, std::chrono::microseconds(usec));
136     }
137     return (state_ & 1) != 0;
138   }
139 
140 private:
141   // Helper class to temporarily adapt a scoped_lock into a unique_lock so that
142   // it can be passed to std::condition_variable::wait().
143   struct unique_lock_adapter
144   {
145     template <typename Lock>
unique_lock_adapterboost::asio::detail::std_event::unique_lock_adapter146     explicit unique_lock_adapter(Lock& lock)
147       : unique_lock_(lock.mutex().mutex_, std::adopt_lock)
148     {
149     }
150 
~unique_lock_adapterboost::asio::detail::std_event::unique_lock_adapter151     ~unique_lock_adapter()
152     {
153       unique_lock_.release();
154     }
155 
156     std::unique_lock<std::mutex> unique_lock_;
157   };
158 
159   // Helper to increment and decrement the state to track outstanding waiters.
160   class waiter
161   {
162   public:
waiter(std::size_t & state)163     explicit waiter(std::size_t& state)
164       : state_(state)
165     {
166       state_ += 2;
167     }
168 
~waiter()169     ~waiter()
170     {
171       state_ -= 2;
172     }
173 
174   private:
175     std::size_t& state_;
176   };
177 
178   std::condition_variable cond_;
179   std::size_t state_;
180 };
181 
182 } // namespace detail
183 } // namespace asio
184 } // namespace boost
185 
186 #include <boost/asio/detail/pop_options.hpp>
187 
188 #endif // defined(BOOST_ASIO_HAS_STD_MUTEX_AND_CONDVAR)
189 
190 #endif // BOOST_ASIO_DETAIL_STD_EVENT_HPP
191