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/system/error_code.hpp>
24 #include <boost/system/system_error.hpp>
25 #include <boost/log/detail/event.hpp>
26
27 #if defined(BOOST_LOG_EVENT_USE_FUTEX)
28
29 #include <stddef.h>
30 #include <errno.h>
31 #include <sys/syscall.h>
32 #include <linux/futex.h>
33 #include <boost/memory_order.hpp>
34
35 #if defined(FUTEX_WAIT_PRIVATE)
36 #define BOOST_LOG_FUTEX_WAIT FUTEX_WAIT_PRIVATE
37 #else
38 #define BOOST_LOG_FUTEX_WAIT FUTEX_WAIT
39 #endif
40
41 #if defined(FUTEX_WAKE_PRIVATE)
42 #define BOOST_LOG_FUTEX_WAKE FUTEX_WAKE_PRIVATE
43 #else
44 #define BOOST_LOG_FUTEX_WAKE FUTEX_WAKE
45 #endif
46
47 #elif defined(BOOST_LOG_EVENT_USE_POSIX_SEMAPHORE)
48
49 #include <errno.h>
50 #include <semaphore.h>
51 #include <boost/memory_order.hpp>
52 #include <boost/atomic/fences.hpp>
53
54 #elif defined(BOOST_LOG_EVENT_USE_WINAPI)
55
56 #include "windows_version.hpp"
57 #include <windows.h>
58 #include <boost/detail/interlocked.hpp>
59
60 #else
61
62 #include <boost/thread/locks.hpp>
63
64 #endif
65
66 #include <boost/log/detail/header.hpp>
67
68 namespace boost {
69
70 BOOST_LOG_OPEN_NAMESPACE
71
72 namespace aux {
73
74 #if defined(BOOST_LOG_EVENT_USE_FUTEX)
75
76 //! Default constructor
futex_based_event()77 BOOST_LOG_API futex_based_event::futex_based_event() : m_state(0)
78 {
79 }
80
81 //! Destructor
~futex_based_event()82 BOOST_LOG_API futex_based_event::~futex_based_event()
83 {
84 }
85
86 //! Waits for the object to become signalled
wait()87 BOOST_LOG_API void futex_based_event::wait()
88 {
89 if (m_state.exchange(0, boost::memory_order_acq_rel) == 0)
90 {
91 while (true)
92 {
93 if (::syscall(SYS_futex, &m_state.storage(), BOOST_LOG_FUTEX_WAIT, 0, NULL, NULL, 0) == 0)
94 {
95 // Another thread has set the event while sleeping
96 break;
97 }
98
99 const int err = errno;
100 if (err == EWOULDBLOCK)
101 {
102 // Another thread has set the event before sleeping
103 break;
104 }
105 else if (err != EINTR)
106 {
107 BOOST_THROW_EXCEPTION(system::system_error(
108 err, system::system_category(), "Failed to block on the futex"));
109 }
110 }
111
112 m_state.store(0, boost::memory_order_relaxed);
113 }
114 }
115
116 //! Sets the object to a signalled state
set_signalled()117 BOOST_LOG_API void futex_based_event::set_signalled()
118 {
119 if (m_state.exchange(1, boost::memory_order_release) == 0)
120 {
121 if (BOOST_UNLIKELY(::syscall(SYS_futex, &m_state.storage(), BOOST_LOG_FUTEX_WAKE, 1, NULL, NULL, 0) < 0))
122 {
123 const int err = errno;
124 BOOST_THROW_EXCEPTION(system::system_error(
125 err, system::system_category(), "Failed to wake threads blocked on the futex"));
126 }
127 }
128 }
129
130 #elif defined(BOOST_LOG_EVENT_USE_POSIX_SEMAPHORE)
131
132 //! Default constructor
133 BOOST_LOG_API sem_based_event::sem_based_event() : m_state()
134 {
135 if (sem_init(&m_semaphore, 0, 0) != 0)
136 {
137 const int err = errno;
138 BOOST_THROW_EXCEPTION(system::system_error(
139 err, system::system_category(), "Failed to initialize semaphore"));
140 }
141 }
142
143 //! Destructor
144 BOOST_LOG_API sem_based_event::~sem_based_event()
145 {
146 BOOST_VERIFY(sem_destroy(&m_semaphore) == 0);
147 }
148
149 //! Waits for the object to become signalled
150 BOOST_LOG_API void sem_based_event::wait()
151 {
152 boost::atomic_thread_fence(boost::memory_order_acq_rel);
153 while (true)
154 {
155 if (sem_wait(&m_semaphore) != 0)
156 {
157 const int err = errno;
158 if (err != EINTR)
159 {
160 BOOST_THROW_EXCEPTION(system::system_error(
161 err, system::system_category(), "Failed to block on the semaphore"));
162 }
163 }
164 else
165 break;
166 }
167 m_state.clear(boost::memory_order_relaxed);
168 }
169
170 //! Sets the object to a signalled state
171 BOOST_LOG_API void sem_based_event::set_signalled()
172 {
173 if (!m_state.test_and_set(boost::memory_order_release))
174 {
175 if (sem_post(&m_semaphore) != 0)
176 {
177 const int err = errno;
178 BOOST_THROW_EXCEPTION(system::system_error(
179 err, system::system_category(), "Failed to wake the blocked thread"));
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 (!m_event)
192 {
193 const DWORD err = GetLastError();
194 BOOST_THROW_EXCEPTION(system::system_error(
195 err, system::system_category(), "Failed to create Windows event"));
196 }
197 }
198
199 //! Destructor
200 BOOST_LOG_API winapi_based_event::~winapi_based_event()
201 {
202 BOOST_VERIFY(CloseHandle(m_event) != 0);
203 }
204
205 //! Waits for the object to become signalled
206 BOOST_LOG_API void winapi_based_event::wait()
207 {
208 // On Windows we assume that memory view is always actual (Intel x86 and x86_64 arch)
209 if (const_cast< volatile boost::uint32_t& >(m_state) == 0)
210 {
211 if (WaitForSingleObject(m_event, INFINITE) != 0)
212 {
213 const DWORD err = GetLastError();
214 BOOST_THROW_EXCEPTION(system::system_error(
215 err, system::system_category(), "Failed to block on Windows event"));
216 }
217 }
218 const_cast< volatile boost::uint32_t& >(m_state) = 0;
219 }
220
221 //! Sets the object to a signalled state
222 BOOST_LOG_API void winapi_based_event::set_signalled()
223 {
224 if (BOOST_INTERLOCKED_COMPARE_EXCHANGE(reinterpret_cast< long* >(&m_state), 1, 0) == 0)
225 {
226 if (SetEvent(m_event) == 0)
227 {
228 const DWORD err = GetLastError();
229 const_cast< volatile boost::uint32_t& >(m_state) = 0;
230 BOOST_THROW_EXCEPTION(system::system_error(
231 err, system::system_category(), "Failed to wake the blocked thread"));
232 }
233 }
234 }
235
236 #else
237
238 //! Default constructor
239 BOOST_LOG_API generic_event::generic_event() : m_state(false)
240 {
241 }
242
243 //! Destructor
244 BOOST_LOG_API generic_event::~generic_event()
245 {
246 }
247
248 //! Waits for the object to become signalled
249 BOOST_LOG_API void generic_event::wait()
250 {
251 boost::unique_lock< boost::mutex > lock(m_mutex);
252 while (!m_state)
253 {
254 m_cond.wait(lock);
255 }
256 m_state = false;
257 }
258
259 //! Sets the object to a signalled state
260 BOOST_LOG_API void generic_event::set_signalled()
261 {
262 boost::lock_guard< boost::mutex > lock(m_mutex);
263 if (!m_state)
264 {
265 m_state = true;
266 m_cond.notify_one();
267 }
268 }
269
270 #endif
271
272 } // namespace aux
273
274 BOOST_LOG_CLOSE_NAMESPACE // namespace log
275
276 } // namespace boost
277
278 #include <boost/log/detail/footer.hpp>
279
280 #endif // BOOST_LOG_NO_THREADS
281