]>
Commit | Line | Data |
---|---|---|
7c673cae FG |
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 | ||
b32b8144 | 27 | #include <boost/winapi/wait.hpp> // INFINITE |
7c673cae FG |
28 | |
29 | #if BOOST_USE_WINAPI_VERSION >= BOOST_WINAPI_VERSION_WIN6 | |
30 | ||
b32b8144 FG |
31 | #include <boost/winapi/srw_lock.hpp> |
32 | #include <boost/winapi/condition_variable.hpp> | |
7c673cae FG |
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 | ||
b32b8144 FG |
43 | boost::winapi::SRWLOCK_ g_OnceBlockMutex = BOOST_WINAPI_SRWLOCK_INIT; |
44 | boost::winapi::CONDITION_VARIABLE_ g_OnceBlockCond = BOOST_WINAPI_CONDITION_VARIABLE_INIT; | |
7c673cae FG |
45 | |
46 | } // namespace | |
47 | ||
48 | BOOST_LOG_API bool once_block_sentry::enter_once_block() const BOOST_NOEXCEPT | |
49 | { | |
b32b8144 | 50 | boost::winapi::AcquireSRWLockExclusive(&g_OnceBlockMutex); |
7c673cae FG |
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; | |
b32b8144 | 58 | boost::winapi::ReleaseSRWLockExclusive(&g_OnceBlockMutex); |
7c673cae FG |
59 | |
60 | // Invoke the initializer block | |
61 | return false; | |
62 | } | |
63 | else | |
64 | { | |
65 | while (flag.status == once_block_flag::being_initialized) | |
66 | { | |
b32b8144 FG |
67 | BOOST_VERIFY(boost::winapi::SleepConditionVariableSRW( |
68 | &g_OnceBlockCond, &g_OnceBlockMutex, boost::winapi::INFINITE_, 0)); | |
7c673cae FG |
69 | } |
70 | } | |
71 | } | |
72 | ||
b32b8144 | 73 | boost::winapi::ReleaseSRWLockExclusive(&g_OnceBlockMutex); |
7c673cae FG |
74 | |
75 | return true; | |
76 | } | |
77 | ||
78 | BOOST_LOG_API void once_block_sentry::commit() BOOST_NOEXCEPT | |
79 | { | |
b32b8144 | 80 | boost::winapi::AcquireSRWLockExclusive(&g_OnceBlockMutex); |
7c673cae FG |
81 | |
82 | // The initializer executed successfully | |
83 | m_flag.status = once_block_flag::initialized; | |
84 | ||
b32b8144 FG |
85 | boost::winapi::ReleaseSRWLockExclusive(&g_OnceBlockMutex); |
86 | boost::winapi::WakeAllConditionVariable(&g_OnceBlockCond); | |
7c673cae FG |
87 | } |
88 | ||
89 | BOOST_LOG_API void once_block_sentry::rollback() BOOST_NOEXCEPT | |
90 | { | |
b32b8144 | 91 | boost::winapi::AcquireSRWLockExclusive(&g_OnceBlockMutex); |
7c673cae FG |
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 | ||
b32b8144 FG |
96 | boost::winapi::ReleaseSRWLockExclusive(&g_OnceBlockMutex); |
97 | boost::winapi::WakeAllConditionVariable(&g_OnceBlockCond); | |
7c673cae FG |
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> | |
b32b8144 FG |
112 | #include <boost/winapi/basic_types.hpp> |
113 | #include <boost/winapi/dll.hpp> | |
7c673cae FG |
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 (WINAPI *InitializeSRWLock_t)(winapi_srwlock*); | |
143 | typedef void (WINAPI *AcquireSRWLockExclusive_t)(winapi_srwlock*); | |
144 | typedef void (WINAPI *ReleaseSRWLockExclusive_t)(winapi_srwlock*); | |
145 | typedef void (WINAPI *InitializeConditionVariable_t)(winapi_condition_variable*); | |
b32b8144 | 146 | typedef boost::winapi::BOOL_ (WINAPI *SleepConditionVariableSRW_t)(winapi_condition_variable*, winapi_srwlock*, boost::winapi::DWORD_, boost::winapi::ULONG_); |
7c673cae FG |
147 | typedef void (WINAPI *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( | |
b32b8144 | 195 | &m_Cond, &m_Mutex, boost::winapi::INFINITE_, 0)); |
7c673cae FG |
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 | { | |
b32b8144 | 282 | boost::winapi::HMODULE_ hKernel32 = boost::winapi::GetModuleHandleW(L"kernel32.dll"); |
7c673cae FG |
283 | if (hKernel32) |
284 | { | |
285 | once_block_impl_nt6::InitializeSRWLock_t pInitializeSRWLock = | |
b32b8144 | 286 | (once_block_impl_nt6::InitializeSRWLock_t)boost::winapi::get_proc_address(hKernel32, "InitializeSRWLock"); |
7c673cae FG |
287 | if (pInitializeSRWLock) |
288 | { | |
289 | once_block_impl_nt6::AcquireSRWLockExclusive_t pAcquireSRWLockExclusive = | |
b32b8144 | 290 | (once_block_impl_nt6::AcquireSRWLockExclusive_t)boost::winapi::get_proc_address(hKernel32, "AcquireSRWLockExclusive"); |
7c673cae FG |
291 | if (pAcquireSRWLockExclusive) |
292 | { | |
293 | once_block_impl_nt6::ReleaseSRWLockExclusive_t pReleaseSRWLockExclusive = | |
b32b8144 | 294 | (once_block_impl_nt6::ReleaseSRWLockExclusive_t)boost::winapi::get_proc_address(hKernel32, "ReleaseSRWLockExclusive"); |
7c673cae FG |
295 | if (pReleaseSRWLockExclusive) |
296 | { | |
297 | once_block_impl_nt6::InitializeConditionVariable_t pInitializeConditionVariable = | |
b32b8144 | 298 | (once_block_impl_nt6::InitializeConditionVariable_t)boost::winapi::get_proc_address(hKernel32, "InitializeConditionVariable"); |
7c673cae FG |
299 | if (pInitializeConditionVariable) |
300 | { | |
301 | once_block_impl_nt6::SleepConditionVariableSRW_t pSleepConditionVariableSRW = | |
b32b8144 | 302 | (once_block_impl_nt6::SleepConditionVariableSRW_t)boost::winapi::get_proc_address(hKernel32, "SleepConditionVariableSRW"); |
7c673cae FG |
303 | if (pSleepConditionVariableSRW) |
304 | { | |
305 | once_block_impl_nt6::WakeAllConditionVariable_t pWakeAllConditionVariable = | |
b32b8144 | 306 | (once_block_impl_nt6::WakeAllConditionVariable_t)boost::winapi::get_proc_address(hKernel32, "WakeAllConditionVariable"); |
7c673cae FG |
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 |