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