• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
commit()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 
rollback()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 
commit()370 BOOST_LOG_API void once_block_sentry::commit() BOOST_NOEXCEPT
371 {
372     get_once_block_impl()->commit(m_flag);
373 }
374 
rollback()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 
commit()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 
rollback()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