1 /*
2  *          Copyright Andrey Semashev 2007 - 2015.
3  * Distributed under the Boost Software License, Version 1.0.
4  *    (See accompanying file LICENSE_1_0.txt or copy at
5  *          http://www.boost.org/LICENSE_1_0.txt)
6  */
7 /*!
8  * \file   event.cpp
9  * \author Andrey Semashev
10  * \date   24.07.2011
11  *
12  * \brief  This header is the Boost.Log library implementation, see the library documentation
13  *         at http://www.boost.org/doc/libs/release/libs/log/doc/html/index.html.
14  */
15 
16 #include <boost/log/detail/config.hpp>
17 
18 #ifndef BOOST_LOG_NO_THREADS
19 
20 #include <boost/assert.hpp>
21 #include <boost/cstdint.hpp>
22 #include <boost/throw_exception.hpp>
23 #include <boost/log/detail/event.hpp>
24 #include <boost/log/exceptions.hpp>
25 
26 #if defined(BOOST_LOG_EVENT_USE_FUTEX)
27 
28 #include <stddef.h>
29 #include <errno.h>
30 #include <sys/syscall.h>
31 #include <linux/futex.h>
32 #include <boost/memory_order.hpp>
33 
34 // Some Android NDKs (Google NDK and older Crystax.NET NDK versions) don't define SYS_futex
35 #if defined(SYS_futex)
36 #define BOOST_LOG_SYS_FUTEX SYS_futex
37 #else
38 #define BOOST_LOG_SYS_FUTEX __NR_futex
39 #endif
40 
41 #if defined(FUTEX_WAIT_PRIVATE)
42 #define BOOST_LOG_FUTEX_WAIT FUTEX_WAIT_PRIVATE
43 #else
44 #define BOOST_LOG_FUTEX_WAIT FUTEX_WAIT
45 #endif
46 
47 #if defined(FUTEX_WAKE_PRIVATE)
48 #define BOOST_LOG_FUTEX_WAKE FUTEX_WAKE_PRIVATE
49 #else
50 #define BOOST_LOG_FUTEX_WAKE FUTEX_WAKE
51 #endif
52 
53 #elif defined(BOOST_LOG_EVENT_USE_POSIX_SEMAPHORE)
54 
55 #include <errno.h>
56 #include <semaphore.h>
57 #include <boost/memory_order.hpp>
58 #include <boost/atomic/fences.hpp>
59 
60 #elif defined(BOOST_LOG_EVENT_USE_WINAPI)
61 
62 #include <windows.h>
63 #include <boost/detail/interlocked.hpp>
64 
65 #else
66 
67 #include <boost/thread/locks.hpp>
68 
69 #endif
70 
71 #include <boost/log/detail/header.hpp>
72 
73 namespace boost {
74 
75 BOOST_LOG_OPEN_NAMESPACE
76 
77 namespace aux {
78 
79 #if defined(BOOST_LOG_EVENT_USE_FUTEX)
80 
81 //! Default constructor
futex_based_event()82 BOOST_LOG_API futex_based_event::futex_based_event() : m_state(0)
83 {
84 }
85 
86 //! Destructor
~futex_based_event()87 BOOST_LOG_API futex_based_event::~futex_based_event()
88 {
89 }
90 
91 //! Waits for the object to become signalled
wait()92 BOOST_LOG_API void futex_based_event::wait()
93 {
94     if (m_state.exchange(0, boost::memory_order_acq_rel) == 0)
95     {
96         while (true)
97         {
98             if (::syscall(BOOST_LOG_SYS_FUTEX, &m_state.storage(), BOOST_LOG_FUTEX_WAIT, 0, NULL, NULL, 0) == 0)
99             {
100                 // Another thread has set the event while sleeping
101                 break;
102             }
103 
104             const int err = errno;
105             if (err == EWOULDBLOCK)
106             {
107                 // Another thread has set the event before sleeping
108                 break;
109             }
110             else if (BOOST_UNLIKELY(err != EINTR))
111             {
112                 BOOST_LOG_THROW_DESCR_PARAMS(system_error, "Failed to block on the futex", (err));
113             }
114         }
115 
116         m_state.store(0, boost::memory_order_relaxed);
117     }
118 }
119 
120 //! Sets the object to a signalled state
set_signalled()121 BOOST_LOG_API void futex_based_event::set_signalled()
122 {
123     if (m_state.exchange(1, boost::memory_order_release) == 0)
124     {
125         if (BOOST_UNLIKELY(::syscall(BOOST_LOG_SYS_FUTEX, &m_state.storage(), BOOST_LOG_FUTEX_WAKE, 1, NULL, NULL, 0) < 0))
126         {
127             const int err = errno;
128             BOOST_LOG_THROW_DESCR_PARAMS(system_error, "Failed to wake threads blocked on the futex", (err));
129         }
130     }
131 }
132 
133 #elif defined(BOOST_LOG_EVENT_USE_POSIX_SEMAPHORE)
134 
135 //! Default constructor
136 BOOST_LOG_API sem_based_event::sem_based_event() : m_state()
137 {
138     if (BOOST_UNLIKELY(sem_init(&m_semaphore, 0, 0) != 0))
139     {
140         const int err = errno;
141         BOOST_LOG_THROW_DESCR_PARAMS(system_error, "Failed to initialize semaphore", (err));
142     }
143 }
144 
145 //! Destructor
146 BOOST_LOG_API sem_based_event::~sem_based_event()
147 {
148     BOOST_VERIFY(sem_destroy(&m_semaphore) == 0);
149 }
150 
151 //! Waits for the object to become signalled
152 BOOST_LOG_API void sem_based_event::wait()
153 {
154     boost::atomic_thread_fence(boost::memory_order_acq_rel);
155     while (true)
156     {
157         if (sem_wait(&m_semaphore) != 0)
158         {
159             const int err = errno;
160             if (BOOST_UNLIKELY(err != EINTR))
161             {
162                 BOOST_LOG_THROW_DESCR_PARAMS(system_error, "Failed to block on the semaphore", (err));
163             }
164         }
165         else
166             break;
167     }
168     m_state.clear(boost::memory_order_relaxed);
169 }
170 
171 //! Sets the object to a signalled state
172 BOOST_LOG_API void sem_based_event::set_signalled()
173 {
174     if (!m_state.test_and_set(boost::memory_order_release))
175     {
176         if (BOOST_UNLIKELY(sem_post(&m_semaphore) != 0))
177         {
178             const int err = errno;
179             BOOST_LOG_THROW_DESCR_PARAMS(system_error, "Failed to wake the blocked thread", (err));
180         }
181     }
182 }
183 
184 #elif defined(BOOST_LOG_EVENT_USE_WINAPI)
185 
186 //! Default constructor
187 BOOST_LOG_API winapi_based_event::winapi_based_event() :
188     m_state(0),
189     m_event(CreateEventA(NULL, false, false, NULL))
190 {
191     if (BOOST_UNLIKELY(!m_event))
192     {
193         const DWORD err = GetLastError();
194         BOOST_LOG_THROW_DESCR_PARAMS(system_error, "Failed to create Windows event", (err));
195     }
196 }
197 
198 //! Destructor
199 BOOST_LOG_API winapi_based_event::~winapi_based_event()
200 {
201     BOOST_VERIFY(CloseHandle(m_event) != 0);
202 }
203 
204 //! Waits for the object to become signalled
205 BOOST_LOG_API void winapi_based_event::wait()
206 {
207     // On Windows we assume that memory view is always actual (Intel x86 and x86_64 arch)
208     if (const_cast< volatile boost::uint32_t& >(m_state) == 0)
209     {
210         if (BOOST_UNLIKELY(WaitForSingleObject(m_event, INFINITE) != 0))
211         {
212             const DWORD err = GetLastError();
213             BOOST_LOG_THROW_DESCR_PARAMS(system_error, "Failed to block on Windows event", (err));
214         }
215     }
216     const_cast< volatile boost::uint32_t& >(m_state) = 0;
217 }
218 
219 //! Sets the object to a signalled state
220 BOOST_LOG_API void winapi_based_event::set_signalled()
221 {
222     if (BOOST_INTERLOCKED_COMPARE_EXCHANGE(reinterpret_cast< long* >(&m_state), 1, 0) == 0)
223     {
224         if (BOOST_UNLIKELY(SetEvent(m_event) == 0))
225         {
226             const DWORD err = GetLastError();
227             const_cast< volatile boost::uint32_t& >(m_state) = 0;
228             BOOST_LOG_THROW_DESCR_PARAMS(system_error, "Failed to wake the blocked thread", (err));
229         }
230     }
231 }
232 
233 #else
234 
235 //! Default constructor
236 BOOST_LOG_API generic_event::generic_event() : m_state(false)
237 {
238 }
239 
240 //! Destructor
241 BOOST_LOG_API generic_event::~generic_event()
242 {
243 }
244 
245 //! Waits for the object to become signalled
246 BOOST_LOG_API void generic_event::wait()
247 {
248     boost::unique_lock< boost::mutex > lock(m_mutex);
249     while (!m_state)
250     {
251         m_cond.wait(lock);
252     }
253     m_state = false;
254 }
255 
256 //! Sets the object to a signalled state
257 BOOST_LOG_API void generic_event::set_signalled()
258 {
259     boost::lock_guard< boost::mutex > lock(m_mutex);
260     if (!m_state)
261     {
262         m_state = true;
263         m_cond.notify_one();
264     }
265 }
266 
267 #endif
268 
269 } // namespace aux
270 
271 BOOST_LOG_CLOSE_NAMESPACE // namespace log
272 
273 } // namespace boost
274 
275 #include <boost/log/detail/footer.hpp>
276 
277 #endif // BOOST_LOG_NO_THREADS
278