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
35namespace boost {
36
37BOOST_LOG_OPEN_NAMESPACE
38
39namespace aux {
40
41BOOST_LOG_ANONYMOUS_NAMESPACE {
42
43boost::winapi::SRWLOCK_ g_OnceBlockMutex = BOOST_WINAPI_SRWLOCK_INIT;
44boost::winapi::CONDITION_VARIABLE_ g_OnceBlockCond = BOOST_WINAPI_CONDITION_VARIABLE_INIT;
45
46} // namespace
47
48BOOST_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
78BOOST_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
89BOOST_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
102BOOST_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
119namespace boost {
120
121BOOST_LOG_OPEN_NAMESPACE
122
123namespace aux {
124
125BOOST_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
365BOOST_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
370BOOST_LOG_API void once_block_sentry::commit() BOOST_NOEXCEPT
371{
372 get_once_block_impl()->commit(m_flag);
373}
374
375BOOST_LOG_API void once_block_sentry::rollback() BOOST_NOEXCEPT
376{
377 get_once_block_impl()->rollback(m_flag);
378}
379
380} // namespace aux
381
382BOOST_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
395namespace boost {
396
397BOOST_LOG_OPEN_NAMESPACE
398
399namespace aux {
400
401BOOST_LOG_ANONYMOUS_NAMESPACE {
402
403static pthread_mutex_t g_OnceBlockMutex = PTHREAD_MUTEX_INITIALIZER;
404static pthread_cond_t g_OnceBlockCond = PTHREAD_COND_INITIALIZER;
405
406} // namespace
407
408BOOST_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
437BOOST_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
448BOOST_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
461BOOST_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

source code of boost/libs/log/src/once_block.cpp