1 // 2 // detail/posix_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_POSIX_EVENT_HPP 12 #define BOOST_ASIO_DETAIL_POSIX_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_PTHREADS) 21 22 #include <cstddef> 23 #include <pthread.h> 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 posix_event 34 : private noncopyable 35 { 36 public: 37 // Constructor. 38 BOOST_ASIO_DECL posix_event(); 39 40 // Destructor. ~posix_event()41 ~posix_event() 42 { 43 ::pthread_cond_destroy(&cond_); 44 } 45 46 // Signal the event. (Retained for backward compatibility.) 47 template <typename Lock> signal(Lock & lock)48 void signal(Lock& lock) 49 { 50 this->signal_all(lock); 51 } 52 53 // Signal all waiters. 54 template <typename Lock> signal_all(Lock & lock)55 void signal_all(Lock& lock) 56 { 57 BOOST_ASIO_ASSERT(lock.locked()); 58 (void)lock; 59 state_ |= 1; 60 ::pthread_cond_broadcast(&cond_); // Ignore EINVAL. 61 } 62 63 // Unlock the mutex and signal one waiter. 64 template <typename Lock> unlock_and_signal_one(Lock & lock)65 void unlock_and_signal_one(Lock& lock) 66 { 67 BOOST_ASIO_ASSERT(lock.locked()); 68 state_ |= 1; 69 bool have_waiters = (state_ > 1); 70 lock.unlock(); 71 if (have_waiters) 72 ::pthread_cond_signal(&cond_); // Ignore EINVAL. 73 } 74 75 // Unlock the mutex and signal one waiter who may destroy us. 76 template <typename Lock> unlock_and_signal_one_for_destruction(Lock & lock)77 void unlock_and_signal_one_for_destruction(Lock& lock) 78 { 79 BOOST_ASIO_ASSERT(lock.locked()); 80 state_ |= 1; 81 bool have_waiters = (state_ > 1); 82 if (have_waiters) 83 ::pthread_cond_signal(&cond_); // Ignore EINVAL. 84 lock.unlock(); 85 } 86 87 // If there's a waiter, unlock the mutex and signal it. 88 template <typename Lock> maybe_unlock_and_signal_one(Lock & lock)89 bool maybe_unlock_and_signal_one(Lock& lock) 90 { 91 BOOST_ASIO_ASSERT(lock.locked()); 92 state_ |= 1; 93 if (state_ > 1) 94 { 95 lock.unlock(); 96 ::pthread_cond_signal(&cond_); // Ignore EINVAL. 97 return true; 98 } 99 return false; 100 } 101 102 // Reset the event. 103 template <typename Lock> clear(Lock & lock)104 void clear(Lock& lock) 105 { 106 BOOST_ASIO_ASSERT(lock.locked()); 107 (void)lock; 108 state_ &= ~std::size_t(1); 109 } 110 111 // Wait for the event to become signalled. 112 template <typename Lock> wait(Lock & lock)113 void wait(Lock& lock) 114 { 115 BOOST_ASIO_ASSERT(lock.locked()); 116 while ((state_ & 1) == 0) 117 { 118 state_ += 2; 119 ::pthread_cond_wait(&cond_, &lock.mutex().mutex_); // Ignore EINVAL. 120 state_ -= 2; 121 } 122 } 123 124 // Timed wait for the event to become signalled. 125 template <typename Lock> wait_for_usec(Lock & lock,long usec)126 bool wait_for_usec(Lock& lock, long usec) 127 { 128 BOOST_ASIO_ASSERT(lock.locked()); 129 if ((state_ & 1) == 0) 130 { 131 state_ += 2; 132 timespec ts; 133 #if (defined(__MACH__) && defined(__APPLE__)) \ 134 || (defined(__ANDROID__) && (__ANDROID_API__ < 21) \ 135 && defined(HAVE_PTHREAD_COND_TIMEDWAIT_RELATIVE)) 136 ts.tv_sec = usec / 1000000; 137 ts.tv_nsec = (usec % 1000000) * 1000; 138 ::pthread_cond_timedwait_relative_np( 139 &cond_, &lock.mutex().mutex_, &ts); // Ignore EINVAL. 140 #else // (defined(__MACH__) && defined(__APPLE__)) 141 // || (defined(__ANDROID__) && (__ANDROID_API__ < 21) 142 // && defined(HAVE_PTHREAD_COND_TIMEDWAIT_RELATIVE)) 143 if (::clock_gettime(CLOCK_MONOTONIC, &ts) == 0) 144 { 145 ts.tv_sec += usec / 1000000; 146 ts.tv_nsec += (usec % 1000000) * 1000; 147 ts.tv_sec += ts.tv_nsec / 1000000000; 148 ts.tv_nsec = ts.tv_nsec % 1000000000; 149 ::pthread_cond_timedwait(&cond_, 150 &lock.mutex().mutex_, &ts); // Ignore EINVAL. 151 } 152 #endif // (defined(__MACH__) && defined(__APPLE__)) 153 // || (defined(__ANDROID__) && (__ANDROID_API__ < 21) 154 // && defined(HAVE_PTHREAD_COND_TIMEDWAIT_RELATIVE)) 155 state_ -= 2; 156 } 157 return (state_ & 1) != 0; 158 } 159 160 private: 161 ::pthread_cond_t cond_; 162 std::size_t state_; 163 }; 164 165 } // namespace detail 166 } // namespace asio 167 } // namespace boost 168 169 #include <boost/asio/detail/pop_options.hpp> 170 171 #if defined(BOOST_ASIO_HEADER_ONLY) 172 # include <boost/asio/detail/impl/posix_event.ipp> 173 #endif // defined(BOOST_ASIO_HEADER_ONLY) 174 175 #endif // defined(BOOST_ASIO_HAS_PTHREADS) 176 177 #endif // BOOST_ASIO_DETAIL_POSIX_EVENT_HPP 178