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