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 once_block.cpp |
9 | * \author Andrey Semashev |
10 | * \date 23.06.2010 |
11 | * |
12 | * \brief This file 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 | * The code in this file is based on the \c call_once function implementation in Boost.Thread. |
16 | */ |
17 | |
18 | #include <boost/log/detail/config.hpp> |
19 | #include <boost/log/utility/once_block.hpp> |
20 | #ifndef BOOST_LOG_NO_THREADS |
21 | |
22 | #include <cstdlib> |
23 | #include <boost/assert.hpp> |
24 | |
25 | #if defined(BOOST_THREAD_PLATFORM_WIN32) |
26 | |
27 | #include <boost/winapi/wait.hpp> // INFINITE |
28 | |
29 | #if BOOST_USE_WINAPI_VERSION >= BOOST_WINAPI_VERSION_WIN6 |
30 | |
31 | #include <boost/winapi/srw_lock.hpp> |
32 | #include <boost/winapi/condition_variable.hpp> |
33 | #include <boost/log/detail/header.hpp> |
34 | |
35 | namespace boost { |
36 | |
37 | BOOST_LOG_OPEN_NAMESPACE |
38 | |
39 | namespace aux { |
40 | |
41 | BOOST_LOG_ANONYMOUS_NAMESPACE { |
42 | |
43 | boost::winapi::SRWLOCK_ g_OnceBlockMutex = BOOST_WINAPI_SRWLOCK_INIT; |
44 | boost::winapi::CONDITION_VARIABLE_ g_OnceBlockCond = BOOST_WINAPI_CONDITION_VARIABLE_INIT; |
45 | |
46 | } // namespace |
47 | |
48 | BOOST_LOG_API bool once_block_sentry::enter_once_block() const BOOST_NOEXCEPT |
49 | { |
50 | boost::winapi::AcquireSRWLockExclusive(&g_OnceBlockMutex); |
51 | |
52 | once_block_flag volatile& flag = m_flag; |
53 | while (flag.status != once_block_flag::initialized) |
54 | { |
55 | if (flag.status == once_block_flag::uninitialized) |
56 | { |
57 | flag.status = once_block_flag::being_initialized; |
58 | boost::winapi::ReleaseSRWLockExclusive(&g_OnceBlockMutex); |
59 | |
60 | // Invoke the initializer block |
61 | return false; |
62 | } |
63 | else |
64 | { |
65 | while (flag.status == once_block_flag::being_initialized) |
66 | { |
67 | BOOST_VERIFY(boost::winapi::SleepConditionVariableSRW( |
68 | &g_OnceBlockCond, &g_OnceBlockMutex, boost::winapi::INFINITE_, 0)); |
69 | } |
70 | } |
71 | } |
72 | |
73 | boost::winapi::ReleaseSRWLockExclusive(&g_OnceBlockMutex); |
74 | |
75 | return true; |
76 | } |
77 | |
78 | BOOST_LOG_API void once_block_sentry::commit() BOOST_NOEXCEPT |
79 | { |
80 | boost::winapi::AcquireSRWLockExclusive(&g_OnceBlockMutex); |
81 | |
82 | // The initializer executed successfully |
83 | m_flag.status = once_block_flag::initialized; |
84 | |
85 | boost::winapi::ReleaseSRWLockExclusive(&g_OnceBlockMutex); |
86 | boost::winapi::WakeAllConditionVariable(&g_OnceBlockCond); |
87 | } |
88 | |
89 | BOOST_LOG_API void once_block_sentry::rollback() BOOST_NOEXCEPT |
90 | { |
91 | boost::winapi::AcquireSRWLockExclusive(&g_OnceBlockMutex); |
92 | |
93 | // The initializer failed, marking the flag as if it hasn't run at all |
94 | m_flag.status = once_block_flag::uninitialized; |
95 | |
96 | boost::winapi::ReleaseSRWLockExclusive(&g_OnceBlockMutex); |
97 | boost::winapi::WakeAllConditionVariable(&g_OnceBlockCond); |
98 | } |
99 | |
100 | } // namespace aux |
101 | |
102 | BOOST_LOG_CLOSE_NAMESPACE // namespace log |
103 | |
104 | } // namespace boost |
105 | |
106 | #include <boost/log/detail/footer.hpp> |
107 | |
108 | #else // BOOST_USE_WINAPI_VERSION >= BOOST_WINAPI_VERSION_WIN6 |
109 | |
110 | #include <cstdlib> // atexit |
111 | #include <boost/detail/interlocked.hpp> |
112 | #include <boost/winapi/basic_types.hpp> |
113 | #include <boost/winapi/dll.hpp> |
114 | #include <boost/thread/mutex.hpp> |
115 | #include <boost/thread/locks.hpp> |
116 | #include <boost/thread/condition_variable.hpp> |
117 | #include <boost/log/detail/header.hpp> |
118 | |
119 | namespace boost { |
120 | |
121 | BOOST_LOG_OPEN_NAMESPACE |
122 | |
123 | namespace aux { |
124 | |
125 | BOOST_LOG_ANONYMOUS_NAMESPACE { |
126 | |
127 | struct BOOST_LOG_NO_VTABLE once_block_impl_base |
128 | { |
129 | virtual ~once_block_impl_base() {} |
130 | virtual bool enter_once_block(once_block_flag volatile& flag) = 0; |
131 | virtual void commit(once_block_flag& flag) = 0; |
132 | virtual void rollback(once_block_flag& flag) = 0; |
133 | }; |
134 | |
135 | class once_block_impl_nt6 : |
136 | public once_block_impl_base |
137 | { |
138 | public: |
139 | struct winapi_srwlock { void* p; }; |
140 | struct winapi_condition_variable { void* p; }; |
141 | |
142 | typedef void (BOOST_WINAPI_WINAPI_CC *InitializeSRWLock_t)(winapi_srwlock*); |
143 | typedef void (BOOST_WINAPI_WINAPI_CC *AcquireSRWLockExclusive_t)(winapi_srwlock*); |
144 | typedef void (BOOST_WINAPI_WINAPI_CC *ReleaseSRWLockExclusive_t)(winapi_srwlock*); |
145 | typedef void (BOOST_WINAPI_WINAPI_CC *InitializeConditionVariable_t)(winapi_condition_variable*); |
146 | typedef boost::winapi::BOOL_ (BOOST_WINAPI_WINAPI_CC *SleepConditionVariableSRW_t)(winapi_condition_variable*, winapi_srwlock*, boost::winapi::DWORD_, boost::winapi::ULONG_); |
147 | typedef void (BOOST_WINAPI_WINAPI_CC *WakeAllConditionVariable_t)(winapi_condition_variable*); |
148 | |
149 | private: |
150 | winapi_srwlock m_Mutex; |
151 | winapi_condition_variable m_Cond; |
152 | |
153 | AcquireSRWLockExclusive_t m_pAcquireSRWLockExclusive; |
154 | ReleaseSRWLockExclusive_t m_pReleaseSRWLockExclusive; |
155 | SleepConditionVariableSRW_t m_pSleepConditionVariableSRW; |
156 | WakeAllConditionVariable_t m_pWakeAllConditionVariable; |
157 | |
158 | public: |
159 | once_block_impl_nt6( |
160 | InitializeSRWLock_t pInitializeSRWLock, |
161 | AcquireSRWLockExclusive_t pAcquireSRWLockExclusive, |
162 | ReleaseSRWLockExclusive_t pReleaseSRWLockExclusive, |
163 | InitializeConditionVariable_t pInitializeConditionVariable, |
164 | SleepConditionVariableSRW_t pSleepConditionVariableSRW, |
165 | WakeAllConditionVariable_t pWakeAllConditionVariable |
166 | ) : |
167 | m_pAcquireSRWLockExclusive(pAcquireSRWLockExclusive), |
168 | m_pReleaseSRWLockExclusive(pReleaseSRWLockExclusive), |
169 | m_pSleepConditionVariableSRW(pSleepConditionVariableSRW), |
170 | m_pWakeAllConditionVariable(pWakeAllConditionVariable) |
171 | { |
172 | pInitializeSRWLock(&m_Mutex); |
173 | pInitializeConditionVariable(&m_Cond); |
174 | } |
175 | |
176 | bool enter_once_block(once_block_flag volatile& flag) |
177 | { |
178 | m_pAcquireSRWLockExclusive(&m_Mutex); |
179 | |
180 | while (flag.status != once_block_flag::initialized) |
181 | { |
182 | if (flag.status == once_block_flag::uninitialized) |
183 | { |
184 | flag.status = once_block_flag::being_initialized; |
185 | m_pReleaseSRWLockExclusive(&m_Mutex); |
186 | |
187 | // Invoke the initializer block |
188 | return false; |
189 | } |
190 | else |
191 | { |
192 | while (flag.status == once_block_flag::being_initialized) |
193 | { |
194 | BOOST_VERIFY(m_pSleepConditionVariableSRW( |
195 | &m_Cond, &m_Mutex, boost::winapi::INFINITE_, 0)); |
196 | } |
197 | } |
198 | } |
199 | |
200 | m_pReleaseSRWLockExclusive(&m_Mutex); |
201 | |
202 | return true; |
203 | } |
204 | |
205 | void commit(once_block_flag& flag) |
206 | { |
207 | m_pAcquireSRWLockExclusive(&m_Mutex); |
208 | |
209 | // The initializer executed successfully |
210 | flag.status = once_block_flag::initialized; |
211 | |
212 | m_pReleaseSRWLockExclusive(&m_Mutex); |
213 | m_pWakeAllConditionVariable(&m_Cond); |
214 | } |
215 | |
216 | void rollback(once_block_flag& flag) |
217 | { |
218 | m_pAcquireSRWLockExclusive(&m_Mutex); |
219 | |
220 | // The initializer failed, marking the flag as if it hasn't run at all |
221 | flag.status = once_block_flag::uninitialized; |
222 | |
223 | m_pReleaseSRWLockExclusive(&m_Mutex); |
224 | m_pWakeAllConditionVariable(&m_Cond); |
225 | } |
226 | }; |
227 | |
228 | class once_block_impl_nt5 : |
229 | public once_block_impl_base |
230 | { |
231 | private: |
232 | mutex m_Mutex; |
233 | condition_variable m_Cond; |
234 | |
235 | public: |
236 | bool enter_once_block(once_block_flag volatile& flag) |
237 | { |
238 | unique_lock< mutex > lock(m_Mutex); |
239 | |
240 | while (flag.status != once_block_flag::initialized) |
241 | { |
242 | if (flag.status == once_block_flag::uninitialized) |
243 | { |
244 | flag.status = once_block_flag::being_initialized; |
245 | |
246 | // Invoke the initializer block |
247 | return false; |
248 | } |
249 | else |
250 | { |
251 | while (flag.status == once_block_flag::being_initialized) |
252 | { |
253 | m_Cond.wait(lock); |
254 | } |
255 | } |
256 | } |
257 | |
258 | return true; |
259 | } |
260 | |
261 | void commit(once_block_flag& flag) |
262 | { |
263 | { |
264 | lock_guard< mutex > lock(m_Mutex); |
265 | flag.status = once_block_flag::initialized; |
266 | } |
267 | m_Cond.notify_all(); |
268 | } |
269 | |
270 | void rollback(once_block_flag& flag) |
271 | { |
272 | { |
273 | lock_guard< mutex > lock(m_Mutex); |
274 | flag.status = once_block_flag::uninitialized; |
275 | } |
276 | m_Cond.notify_all(); |
277 | } |
278 | }; |
279 | |
280 | once_block_impl_base* create_once_block_impl() |
281 | { |
282 | boost::winapi::HMODULE_ hKernel32 = boost::winapi::GetModuleHandleW(L"kernel32.dll" ); |
283 | if (hKernel32) |
284 | { |
285 | once_block_impl_nt6::InitializeSRWLock_t pInitializeSRWLock = |
286 | (once_block_impl_nt6::InitializeSRWLock_t)boost::winapi::get_proc_address(hKernel32, "InitializeSRWLock" ); |
287 | if (pInitializeSRWLock) |
288 | { |
289 | once_block_impl_nt6::AcquireSRWLockExclusive_t pAcquireSRWLockExclusive = |
290 | (once_block_impl_nt6::AcquireSRWLockExclusive_t)boost::winapi::get_proc_address(hKernel32, "AcquireSRWLockExclusive" ); |
291 | if (pAcquireSRWLockExclusive) |
292 | { |
293 | once_block_impl_nt6::ReleaseSRWLockExclusive_t pReleaseSRWLockExclusive = |
294 | (once_block_impl_nt6::ReleaseSRWLockExclusive_t)boost::winapi::get_proc_address(hKernel32, "ReleaseSRWLockExclusive" ); |
295 | if (pReleaseSRWLockExclusive) |
296 | { |
297 | once_block_impl_nt6::InitializeConditionVariable_t pInitializeConditionVariable = |
298 | (once_block_impl_nt6::InitializeConditionVariable_t)boost::winapi::get_proc_address(hKernel32, "InitializeConditionVariable" ); |
299 | if (pInitializeConditionVariable) |
300 | { |
301 | once_block_impl_nt6::SleepConditionVariableSRW_t pSleepConditionVariableSRW = |
302 | (once_block_impl_nt6::SleepConditionVariableSRW_t)boost::winapi::get_proc_address(hKernel32, "SleepConditionVariableSRW" ); |
303 | if (pSleepConditionVariableSRW) |
304 | { |
305 | once_block_impl_nt6::WakeAllConditionVariable_t pWakeAllConditionVariable = |
306 | (once_block_impl_nt6::WakeAllConditionVariable_t)boost::winapi::get_proc_address(hKernel32, "WakeAllConditionVariable" ); |
307 | if (pWakeAllConditionVariable) |
308 | { |
309 | return new once_block_impl_nt6( |
310 | pInitializeSRWLock, |
311 | pAcquireSRWLockExclusive, |
312 | pReleaseSRWLockExclusive, |
313 | pInitializeConditionVariable, |
314 | pSleepConditionVariableSRW, |
315 | pWakeAllConditionVariable); |
316 | } |
317 | } |
318 | } |
319 | } |
320 | } |
321 | } |
322 | } |
323 | |
324 | return new once_block_impl_nt5(); |
325 | } |
326 | |
327 | once_block_impl_base* g_pOnceBlockImpl = NULL; |
328 | |
329 | void destroy_once_block_impl() |
330 | { |
331 | once_block_impl_base* impl = (once_block_impl_base*) |
332 | BOOST_INTERLOCKED_EXCHANGE_POINTER((void**)&g_pOnceBlockImpl, NULL); |
333 | delete impl; |
334 | } |
335 | |
336 | once_block_impl_base* get_once_block_impl() BOOST_NOEXCEPT |
337 | { |
338 | once_block_impl_base* impl = g_pOnceBlockImpl; |
339 | if (!impl) try |
340 | { |
341 | once_block_impl_base* new_impl = create_once_block_impl(); |
342 | impl = (once_block_impl_base*) |
343 | BOOST_INTERLOCKED_COMPARE_EXCHANGE_POINTER((void**)&g_pOnceBlockImpl, (void*)new_impl, NULL); |
344 | if (impl) |
345 | { |
346 | delete new_impl; |
347 | } |
348 | else |
349 | { |
350 | std::atexit(&destroy_once_block_impl); |
351 | return new_impl; |
352 | } |
353 | } |
354 | catch (...) |
355 | { |
356 | BOOST_ASSERT_MSG(false, "Boost.Log: Failed to initialize the once block thread synchronization structures" ); |
357 | std::abort(); |
358 | } |
359 | |
360 | return impl; |
361 | } |
362 | |
363 | } // namespace |
364 | |
365 | BOOST_LOG_API bool once_block_sentry::enter_once_block() const BOOST_NOEXCEPT |
366 | { |
367 | return get_once_block_impl()->enter_once_block(m_flag); |
368 | } |
369 | |
370 | BOOST_LOG_API void once_block_sentry::commit() BOOST_NOEXCEPT |
371 | { |
372 | get_once_block_impl()->commit(m_flag); |
373 | } |
374 | |
375 | BOOST_LOG_API void once_block_sentry::rollback() BOOST_NOEXCEPT |
376 | { |
377 | get_once_block_impl()->rollback(m_flag); |
378 | } |
379 | |
380 | } // namespace aux |
381 | |
382 | BOOST_LOG_CLOSE_NAMESPACE // namespace log |
383 | |
384 | } // namespace boost |
385 | |
386 | #include <boost/log/detail/footer.hpp> |
387 | |
388 | #endif // BOOST_USE_WINAPI_VERSION >= BOOST_WINAPI_VERSION_WIN6 |
389 | |
390 | #elif defined(BOOST_THREAD_PLATFORM_PTHREAD) |
391 | |
392 | #include <pthread.h> |
393 | #include <boost/log/detail/header.hpp> |
394 | |
395 | namespace boost { |
396 | |
397 | BOOST_LOG_OPEN_NAMESPACE |
398 | |
399 | namespace aux { |
400 | |
401 | BOOST_LOG_ANONYMOUS_NAMESPACE { |
402 | |
403 | static pthread_mutex_t g_OnceBlockMutex = PTHREAD_MUTEX_INITIALIZER; |
404 | static pthread_cond_t g_OnceBlockCond = PTHREAD_COND_INITIALIZER; |
405 | |
406 | } // namespace |
407 | |
408 | BOOST_LOG_API bool once_block_sentry::enter_once_block() const BOOST_NOEXCEPT |
409 | { |
410 | BOOST_VERIFY(!pthread_mutex_lock(&g_OnceBlockMutex)); |
411 | |
412 | once_block_flag volatile& flag = m_flag; |
413 | while (flag.status != once_block_flag::initialized) |
414 | { |
415 | if (flag.status == once_block_flag::uninitialized) |
416 | { |
417 | flag.status = once_block_flag::being_initialized; |
418 | BOOST_VERIFY(!pthread_mutex_unlock(&g_OnceBlockMutex)); |
419 | |
420 | // Invoke the initializer block |
421 | return false; |
422 | } |
423 | else |
424 | { |
425 | while (flag.status == once_block_flag::being_initialized) |
426 | { |
427 | BOOST_VERIFY(!pthread_cond_wait(&g_OnceBlockCond, &g_OnceBlockMutex)); |
428 | } |
429 | } |
430 | } |
431 | |
432 | BOOST_VERIFY(!pthread_mutex_unlock(&g_OnceBlockMutex)); |
433 | |
434 | return true; |
435 | } |
436 | |
437 | BOOST_LOG_API void once_block_sentry::commit() BOOST_NOEXCEPT |
438 | { |
439 | BOOST_VERIFY(!pthread_mutex_lock(&g_OnceBlockMutex)); |
440 | |
441 | // The initializer executed successfully |
442 | m_flag.status = once_block_flag::initialized; |
443 | |
444 | BOOST_VERIFY(!pthread_mutex_unlock(&g_OnceBlockMutex)); |
445 | BOOST_VERIFY(!pthread_cond_broadcast(&g_OnceBlockCond)); |
446 | } |
447 | |
448 | BOOST_LOG_API void once_block_sentry::rollback() BOOST_NOEXCEPT |
449 | { |
450 | BOOST_VERIFY(!pthread_mutex_lock(&g_OnceBlockMutex)); |
451 | |
452 | // The initializer failed, marking the flag as if it hasn't run at all |
453 | m_flag.status = once_block_flag::uninitialized; |
454 | |
455 | BOOST_VERIFY(!pthread_mutex_unlock(&g_OnceBlockMutex)); |
456 | BOOST_VERIFY(!pthread_cond_broadcast(&g_OnceBlockCond)); |
457 | } |
458 | |
459 | } // namespace aux |
460 | |
461 | BOOST_LOG_CLOSE_NAMESPACE // namespace log |
462 | |
463 | } // namespace boost |
464 | |
465 | #include <boost/log/detail/footer.hpp> |
466 | |
467 | #else |
468 | #error Boost.Log: unsupported threading API |
469 | #endif |
470 | |
471 | #endif // BOOST_LOG_NO_THREADS |
472 | |