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