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