]>
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 async_frontend.hpp | |
9 | * \author Andrey Semashev | |
10 | * \date 14.07.2009 | |
11 | * | |
12 | * The header contains implementation of asynchronous sink frontend. | |
13 | */ | |
14 | ||
15 | #ifndef BOOST_LOG_SINKS_ASYNC_FRONTEND_HPP_INCLUDED_ | |
16 | #define BOOST_LOG_SINKS_ASYNC_FRONTEND_HPP_INCLUDED_ | |
17 | ||
18 | #include <exception> // std::terminate | |
19 | #include <boost/log/detail/config.hpp> | |
20 | ||
21 | #ifdef BOOST_HAS_PRAGMA_ONCE | |
22 | #pragma once | |
23 | #endif | |
24 | ||
25 | #if defined(BOOST_LOG_NO_THREADS) | |
26 | #error Boost.Log: Asynchronous sink frontend is only supported in multithreaded environment | |
27 | #endif | |
28 | ||
7c673cae FG |
29 | #include <boost/static_assert.hpp> |
30 | #include <boost/memory_order.hpp> | |
31 | #include <boost/atomic/atomic.hpp> | |
32 | #include <boost/smart_ptr/shared_ptr.hpp> | |
33 | #include <boost/smart_ptr/make_shared_object.hpp> | |
34 | #include <boost/preprocessor/control/if.hpp> | |
35 | #include <boost/preprocessor/comparison/equal.hpp> | |
36 | #include <boost/thread/locks.hpp> | |
37 | #include <boost/thread/recursive_mutex.hpp> | |
38 | #include <boost/thread/thread.hpp> | |
39 | #include <boost/thread/condition_variable.hpp> | |
40 | #include <boost/log/exceptions.hpp> | |
41 | #include <boost/log/detail/locking_ptr.hpp> | |
42 | #include <boost/log/detail/parameter_tools.hpp> | |
43 | #include <boost/log/core/record_view.hpp> | |
44 | #include <boost/log/sinks/basic_sink_frontend.hpp> | |
45 | #include <boost/log/sinks/frontend_requirements.hpp> | |
46 | #include <boost/log/sinks/unbounded_fifo_queue.hpp> | |
47 | #include <boost/log/keywords/start_thread.hpp> | |
48 | #include <boost/log/detail/header.hpp> | |
49 | ||
50 | namespace boost { | |
51 | ||
52 | BOOST_LOG_OPEN_NAMESPACE | |
53 | ||
54 | namespace sinks { | |
55 | ||
56 | #ifndef BOOST_LOG_DOXYGEN_PASS | |
57 | ||
20effc67 | 58 | #define BOOST_LOG_SINK_CTOR_FORWARD_INTERNAL_1(z, n, data)\ |
7c673cae FG |
59 | template< typename T0 >\ |
60 | explicit asynchronous_sink(T0 const& arg0, typename boost::log::aux::enable_if_named_parameters< T0, boost::log::aux::sfinae_dummy >::type = boost::log::aux::sfinae_dummy()) :\ | |
61 | base_type(true),\ | |
62 | queue_base_type(arg0),\ | |
63 | m_pBackend(boost::make_shared< sink_backend_type >(arg0)),\ | |
20effc67 | 64 | m_ActiveOperation(idle),\ |
7c673cae FG |
65 | m_StopRequested(false),\ |
66 | m_FlushRequested(false)\ | |
67 | {\ | |
68 | if (arg0[keywords::start_thread | true])\ | |
69 | start_feeding_thread();\ | |
70 | }\ | |
71 | template< typename T0 >\ | |
72 | explicit asynchronous_sink(shared_ptr< sink_backend_type > const& backend, T0 const& arg0) :\ | |
73 | base_type(true),\ | |
74 | queue_base_type(arg0),\ | |
75 | m_pBackend(backend),\ | |
20effc67 | 76 | m_ActiveOperation(idle),\ |
7c673cae FG |
77 | m_StopRequested(false),\ |
78 | m_FlushRequested(false)\ | |
79 | {\ | |
80 | if (arg0[keywords::start_thread | true])\ | |
81 | start_feeding_thread();\ | |
82 | } | |
83 | ||
20effc67 TL |
84 | #define BOOST_LOG_SINK_CTOR_FORWARD_INTERNAL_N(z, n, data)\ |
85 | template< BOOST_PP_ENUM_PARAMS_Z(z, n, typename T) >\ | |
86 | explicit asynchronous_sink(BOOST_PP_ENUM_BINARY_PARAMS_Z(z, n, T, const& arg)) :\ | |
7c673cae | 87 | base_type(true),\ |
20effc67 TL |
88 | queue_base_type((BOOST_PP_ENUM_PARAMS_Z(z, n, arg))),\ |
89 | m_pBackend(boost::make_shared< sink_backend_type >(BOOST_PP_ENUM_PARAMS_Z(z, n, arg))),\ | |
90 | m_ActiveOperation(idle),\ | |
7c673cae FG |
91 | m_StopRequested(false),\ |
92 | m_FlushRequested(false)\ | |
93 | {\ | |
20effc67 | 94 | if ((BOOST_PP_ENUM_PARAMS_Z(z, n, arg))[keywords::start_thread | true])\ |
7c673cae FG |
95 | start_feeding_thread();\ |
96 | }\ | |
20effc67 TL |
97 | template< BOOST_PP_ENUM_PARAMS_Z(z, n, typename T) >\ |
98 | explicit asynchronous_sink(shared_ptr< sink_backend_type > const& backend, BOOST_PP_ENUM_BINARY_PARAMS_Z(z, n, T, const& arg)) :\ | |
7c673cae | 99 | base_type(true),\ |
20effc67 | 100 | queue_base_type((BOOST_PP_ENUM_PARAMS_Z(z, n, arg))),\ |
7c673cae | 101 | m_pBackend(backend),\ |
20effc67 | 102 | m_ActiveOperation(idle),\ |
7c673cae FG |
103 | m_StopRequested(false),\ |
104 | m_FlushRequested(false)\ | |
105 | {\ | |
20effc67 | 106 | if ((BOOST_PP_ENUM_PARAMS_Z(z, n, arg))[keywords::start_thread | true])\ |
7c673cae FG |
107 | start_feeding_thread();\ |
108 | } | |
109 | ||
110 | #define BOOST_LOG_SINK_CTOR_FORWARD_INTERNAL(z, n, data)\ | |
20effc67 | 111 | BOOST_PP_IF(BOOST_PP_EQUAL(n, 1), BOOST_LOG_SINK_CTOR_FORWARD_INTERNAL_1, BOOST_LOG_SINK_CTOR_FORWARD_INTERNAL_N)(z, n, data) |
7c673cae FG |
112 | |
113 | #endif // BOOST_LOG_DOXYGEN_PASS | |
114 | ||
115 | /*! | |
116 | * \brief Asynchronous logging sink frontend | |
117 | * | |
118 | * The frontend starts a separate thread on construction. All logging records are passed | |
20effc67 TL |
119 | * to the backend in this dedicated thread. |
120 | * | |
121 | * The user can prevent spawning the internal thread by specifying \c start_thread parameter | |
122 | * with the value of \c false on construction. In this case log records will be buffered | |
123 | * in the internal queue until the user calls \c run, \c feed_records or \c flush in his own | |
124 | * thread. Log record queueing strategy is specified in the \c QueueingStrategyT template | |
125 | * parameter. | |
7c673cae FG |
126 | */ |
127 | template< typename SinkBackendT, typename QueueingStrategyT = unbounded_fifo_queue > | |
128 | class asynchronous_sink : | |
129 | public aux::make_sink_frontend_base< SinkBackendT >::type, | |
130 | public QueueingStrategyT | |
131 | { | |
132 | typedef typename aux::make_sink_frontend_base< SinkBackendT >::type base_type; | |
133 | typedef QueueingStrategyT queue_base_type; | |
134 | ||
135 | private: | |
136 | //! Backend synchronization mutex type | |
137 | typedef boost::recursive_mutex backend_mutex_type; | |
138 | //! Frontend synchronization mutex type | |
139 | typedef typename base_type::mutex_type frontend_mutex_type; | |
140 | ||
20effc67 TL |
141 | //! Operation bit mask |
142 | enum operation | |
143 | { | |
144 | idle = 0u, | |
145 | feeding_records = 1u, | |
146 | flushing = 3u | |
147 | }; | |
148 | ||
149 | //! Function object to run the log record feeding thread | |
150 | class run_func | |
7c673cae | 151 | { |
20effc67 TL |
152 | public: |
153 | typedef void result_type; | |
154 | ||
7c673cae | 155 | private: |
20effc67 | 156 | asynchronous_sink* m_self; |
7c673cae FG |
157 | |
158 | public: | |
20effc67 | 159 | explicit run_func(asynchronous_sink* self) BOOST_NOEXCEPT : m_self(self) |
7c673cae | 160 | { |
7c673cae | 161 | } |
20effc67 TL |
162 | |
163 | result_type operator()() const | |
164 | { | |
165 | m_self->run(); | |
166 | } | |
167 | }; | |
168 | ||
169 | //! A scope guard that implements active operation management | |
170 | class scoped_feeding_opereation | |
171 | { | |
172 | private: | |
173 | asynchronous_sink& m_self; | |
174 | ||
175 | public: | |
7c673cae | 176 | //! Initializing constructor |
20effc67 | 177 | explicit scoped_feeding_opereation(asynchronous_sink& self) : m_self(self) |
7c673cae | 178 | { |
7c673cae FG |
179 | } |
180 | //! Destructor | |
20effc67 | 181 | ~scoped_feeding_opereation() |
7c673cae | 182 | { |
20effc67 | 183 | m_self.complete_feeding_operation(); |
7c673cae FG |
184 | } |
185 | ||
20effc67 TL |
186 | BOOST_DELETED_FUNCTION(scoped_feeding_opereation(scoped_feeding_opereation const&)) |
187 | BOOST_DELETED_FUNCTION(scoped_feeding_opereation& operator= (scoped_feeding_opereation const&)) | |
7c673cae FG |
188 | }; |
189 | ||
190 | //! A scope guard that resets a flag on destructor | |
191 | class scoped_flag | |
192 | { | |
193 | private: | |
194 | frontend_mutex_type& m_Mutex; | |
195 | condition_variable_any& m_Cond; | |
196 | boost::atomic< bool >& m_Flag; | |
197 | ||
198 | public: | |
199 | explicit scoped_flag(frontend_mutex_type& mut, condition_variable_any& cond, boost::atomic< bool >& f) : | |
200 | m_Mutex(mut), m_Cond(cond), m_Flag(f) | |
201 | { | |
202 | } | |
203 | ~scoped_flag() | |
204 | { | |
205 | try | |
206 | { | |
207 | lock_guard< frontend_mutex_type > lock(m_Mutex); | |
20effc67 | 208 | m_Flag.store(false, boost::memory_order_relaxed); |
7c673cae FG |
209 | m_Cond.notify_all(); |
210 | } | |
211 | catch (...) | |
212 | { | |
213 | } | |
214 | } | |
215 | ||
216 | BOOST_DELETED_FUNCTION(scoped_flag(scoped_flag const&)) | |
217 | BOOST_DELETED_FUNCTION(scoped_flag& operator= (scoped_flag const&)) | |
218 | }; | |
219 | ||
220 | public: | |
221 | //! Sink implementation type | |
222 | typedef SinkBackendT sink_backend_type; | |
223 | //! \cond | |
224 | BOOST_STATIC_ASSERT_MSG((has_requirement< typename sink_backend_type::frontend_requirements, synchronized_feeding >::value), "Asynchronous sink frontend is incompatible with the specified backend: thread synchronization requirements are not met"); | |
225 | //! \endcond | |
226 | ||
227 | #ifndef BOOST_LOG_DOXYGEN_PASS | |
228 | ||
229 | //! A pointer type that locks the backend until it's destroyed | |
230 | typedef boost::log::aux::locking_ptr< sink_backend_type, backend_mutex_type > locked_backend_ptr; | |
231 | ||
232 | #else // BOOST_LOG_DOXYGEN_PASS | |
233 | ||
234 | //! A pointer type that locks the backend until it's destroyed | |
235 | typedef implementation_defined locked_backend_ptr; | |
236 | ||
237 | #endif // BOOST_LOG_DOXYGEN_PASS | |
238 | ||
239 | private: | |
240 | //! Synchronization mutex | |
241 | backend_mutex_type m_BackendMutex; | |
242 | //! Pointer to the backend | |
243 | const shared_ptr< sink_backend_type > m_pBackend; | |
244 | ||
245 | //! Dedicated record feeding thread | |
246 | thread m_DedicatedFeedingThread; | |
7c673cae FG |
247 | //! Condition variable to implement blocking operations |
248 | condition_variable_any m_BlockCond; | |
249 | ||
20effc67 TL |
250 | //! Currently active operation |
251 | operation m_ActiveOperation; | |
7c673cae FG |
252 | //! The flag indicates that the feeding loop has to be stopped |
253 | boost::atomic< bool > m_StopRequested; | |
254 | //! The flag indicates that queue flush has been requested | |
255 | boost::atomic< bool > m_FlushRequested; | |
256 | ||
257 | public: | |
258 | /*! | |
259 | * Default constructor. Constructs the sink backend instance. | |
260 | * Requires the backend to be default-constructible. | |
261 | * | |
262 | * \param start_thread If \c true, the frontend creates a thread to feed | |
263 | * log records to the backend. Otherwise no thread is | |
264 | * started and it is assumed that the user will call | |
20effc67 | 265 | * \c run, \c feed_records or \c flush himself. |
7c673cae | 266 | */ |
20effc67 | 267 | explicit asynchronous_sink(bool start_thread = true) : |
7c673cae FG |
268 | base_type(true), |
269 | m_pBackend(boost::make_shared< sink_backend_type >()), | |
20effc67 | 270 | m_ActiveOperation(idle), |
7c673cae FG |
271 | m_StopRequested(false), |
272 | m_FlushRequested(false) | |
273 | { | |
274 | if (start_thread) | |
275 | start_feeding_thread(); | |
276 | } | |
277 | /*! | |
278 | * Constructor attaches user-constructed backend instance | |
279 | * | |
280 | * \param backend Pointer to the backend instance. | |
281 | * \param start_thread If \c true, the frontend creates a thread to feed | |
282 | * log records to the backend. Otherwise no thread is | |
283 | * started and it is assumed that the user will call | |
20effc67 | 284 | * \c run, \c feed_records or \c flush himself. |
7c673cae FG |
285 | * |
286 | * \pre \a backend is not \c NULL. | |
287 | */ | |
288 | explicit asynchronous_sink(shared_ptr< sink_backend_type > const& backend, bool start_thread = true) : | |
289 | base_type(true), | |
290 | m_pBackend(backend), | |
20effc67 | 291 | m_ActiveOperation(idle), |
7c673cae FG |
292 | m_StopRequested(false), |
293 | m_FlushRequested(false) | |
294 | { | |
295 | if (start_thread) | |
296 | start_feeding_thread(); | |
297 | } | |
298 | ||
299 | /*! | |
300 | * Constructor that passes arbitrary named parameters to the interprocess sink backend constructor. | |
301 | * Refer to the backend documentation for the list of supported parameters. | |
302 | * | |
303 | * The frontend uses the following named parameters: | |
304 | * | |
305 | * \li start_thread - If \c true, the frontend creates a thread to feed | |
306 | * log records to the backend. Otherwise no thread is | |
307 | * started and it is assumed that the user will call | |
20effc67 | 308 | * \c run, \c feed_records or \c flush himself. |
7c673cae FG |
309 | */ |
310 | #ifndef BOOST_LOG_DOXYGEN_PASS | |
311 | BOOST_LOG_PARAMETRIZED_CONSTRUCTORS_GEN(BOOST_LOG_SINK_CTOR_FORWARD_INTERNAL, ~) | |
312 | #else | |
313 | template< typename... Args > | |
314 | explicit asynchronous_sink(Args&&... args); | |
315 | #endif | |
316 | ||
317 | /*! | |
318 | * Destructor. Implicitly stops the dedicated feeding thread, if one is running. | |
319 | */ | |
20effc67 | 320 | ~asynchronous_sink() BOOST_NOEXCEPT BOOST_OVERRIDE |
7c673cae FG |
321 | { |
322 | try | |
323 | { | |
324 | boost::this_thread::disable_interruption no_interrupts; | |
325 | stop(); | |
326 | } | |
327 | catch (...) | |
328 | { | |
329 | std::terminate(); | |
330 | } | |
331 | } | |
332 | ||
333 | /*! | |
334 | * Locking accessor to the attached backend | |
335 | */ | |
336 | locked_backend_ptr locked_backend() | |
337 | { | |
338 | return locked_backend_ptr(m_pBackend, m_BackendMutex); | |
339 | } | |
340 | ||
341 | /*! | |
342 | * Enqueues the log record to the backend | |
343 | */ | |
20effc67 | 344 | void consume(record_view const& rec) BOOST_OVERRIDE |
7c673cae FG |
345 | { |
346 | if (BOOST_UNLIKELY(m_FlushRequested.load(boost::memory_order_acquire))) | |
347 | { | |
348 | unique_lock< frontend_mutex_type > lock(base_type::frontend_mutex()); | |
349 | // Wait until flush is done | |
350 | while (m_FlushRequested.load(boost::memory_order_acquire)) | |
351 | m_BlockCond.wait(lock); | |
352 | } | |
353 | queue_base_type::enqueue(rec); | |
354 | } | |
355 | ||
356 | /*! | |
357 | * The method attempts to pass logging record to the backend | |
358 | */ | |
20effc67 | 359 | bool try_consume(record_view const& rec) BOOST_OVERRIDE |
7c673cae FG |
360 | { |
361 | if (!m_FlushRequested.load(boost::memory_order_acquire)) | |
362 | { | |
363 | return queue_base_type::try_enqueue(rec); | |
364 | } | |
365 | else | |
366 | return false; | |
367 | } | |
368 | ||
369 | /*! | |
370 | * The method starts record feeding loop and effectively blocks until either of this happens: | |
371 | * | |
372 | * \li the thread is interrupted due to either standard thread interruption or a call to \c stop | |
373 | * \li an exception is thrown while processing a log record in the backend, and the exception is | |
374 | * not terminated by the exception handler, if one is installed | |
375 | * | |
376 | * \pre The sink frontend must be constructed without spawning a dedicated thread | |
377 | */ | |
378 | void run() | |
379 | { | |
380 | // First check that no other thread is running | |
20effc67 TL |
381 | { |
382 | unique_lock< frontend_mutex_type > lock(base_type::frontend_mutex()); | |
383 | if (start_feeding_operation(lock, feeding_records)) | |
384 | return; | |
385 | } | |
386 | ||
387 | scoped_feeding_opereation guard(*this); | |
7c673cae FG |
388 | |
389 | // Now start the feeding loop | |
390 | while (true) | |
391 | { | |
392 | do_feed_records(); | |
393 | if (!m_StopRequested.load(boost::memory_order_acquire)) | |
394 | { | |
395 | // Block until new record is available | |
396 | record_view rec; | |
397 | if (queue_base_type::dequeue_ready(rec)) | |
398 | base_type::feed_record(rec, m_BackendMutex, *m_pBackend); | |
399 | } | |
400 | else | |
401 | break; | |
402 | } | |
403 | } | |
404 | ||
405 | /*! | |
20effc67 TL |
406 | * The method softly interrupts record feeding loop. This method must be called when \c run, |
407 | * \c feed_records or \c flush method execution has to be interrupted. Unlike regular thread | |
408 | * interruption, calling \c stop will not interrupt the record processing in the middle. | |
409 | * Instead, the sink frontend will attempt to finish its business with the record in progress | |
410 | * and return afterwards. This method can be called either if the sink was created with | |
411 | * an internal dedicated thread, or if the feeding loop was initiated by user. | |
412 | * | |
413 | * If no record feeding operation is in progress, calling \c stop marks the sink frontend | |
414 | * so that the next feeding operation stops immediately. | |
7c673cae FG |
415 | * |
416 | * \note Returning from this method does not guarantee that there are no records left buffered | |
417 | * in the sink frontend. It is possible that log records keep coming during and after this | |
418 | * method is called. At some point of execution of this method log records stop being processed, | |
419 | * and all records that come after this point are put into the queue. These records will be | |
420 | * processed upon further calls to \c run or \c feed_records. | |
20effc67 TL |
421 | * |
422 | * \note If the record feeding loop is being run in a user's thread (i.e. \c start_thread was specified | |
423 | * as \c false on frontend construction), this method does not guarantee that upon return the thread | |
424 | * has returned from the record feeding loop or that it won't enter it in the future. The method | |
425 | * only ensures that the record feeding thread will eventually return from the feeding loop. It is | |
426 | * user's responsibility to synchronize with the user's record feeding thread. | |
7c673cae FG |
427 | */ |
428 | void stop() | |
429 | { | |
20effc67 | 430 | boost::thread feeding_thread; |
7c673cae | 431 | { |
20effc67 TL |
432 | lock_guard< frontend_mutex_type > lock(base_type::frontend_mutex()); |
433 | ||
434 | m_StopRequested.store(true, boost::memory_order_release); | |
435 | queue_base_type::interrupt_dequeue(); | |
7c673cae | 436 | |
20effc67 | 437 | m_DedicatedFeedingThread.swap(feeding_thread); |
7c673cae | 438 | } |
20effc67 TL |
439 | |
440 | if (feeding_thread.joinable()) | |
441 | feeding_thread.join(); | |
7c673cae FG |
442 | } |
443 | ||
444 | /*! | |
445 | * The method feeds log records that may have been buffered to the backend and returns | |
446 | * | |
447 | * \pre The sink frontend must be constructed without spawning a dedicated thread | |
448 | */ | |
449 | void feed_records() | |
450 | { | |
451 | // First check that no other thread is running | |
20effc67 TL |
452 | { |
453 | unique_lock< frontend_mutex_type > lock(base_type::frontend_mutex()); | |
454 | if (start_feeding_operation(lock, feeding_records)) | |
455 | return; | |
456 | } | |
457 | ||
458 | scoped_feeding_opereation guard(*this); | |
7c673cae FG |
459 | |
460 | // Now start the feeding loop | |
461 | do_feed_records(); | |
462 | } | |
463 | ||
464 | /*! | |
465 | * The method feeds all log records that may have been buffered to the backend and returns. | |
466 | * Unlike \c feed_records, in case of ordering queueing the method also feeds records | |
20effc67 | 467 | * that were enqueued during the ordering window, attempting to drain the queue completely. |
7c673cae | 468 | */ |
20effc67 | 469 | void flush() BOOST_OVERRIDE |
7c673cae | 470 | { |
7c673cae | 471 | { |
20effc67 TL |
472 | unique_lock< frontend_mutex_type > lock(base_type::frontend_mutex()); |
473 | if (static_cast< unsigned int >(m_ActiveOperation & feeding_records) != 0u) | |
474 | { | |
475 | // There is already a thread feeding records, let it do the job | |
476 | m_FlushRequested.store(true, boost::memory_order_release); | |
477 | queue_base_type::interrupt_dequeue(); | |
478 | while (!m_StopRequested.load(boost::memory_order_acquire) && m_FlushRequested.load(boost::memory_order_acquire)) | |
479 | m_BlockCond.wait(lock); | |
7c673cae | 480 | |
20effc67 TL |
481 | // The condition may have been signalled when the feeding operation was finishing. |
482 | // In that case records may not have been flushed, and we do the flush ourselves. | |
483 | if (m_ActiveOperation != idle) | |
484 | return; | |
485 | } | |
7c673cae | 486 | |
20effc67 TL |
487 | m_ActiveOperation = flushing; |
488 | m_FlushRequested.store(true, boost::memory_order_relaxed); | |
489 | } | |
7c673cae | 490 | |
20effc67 | 491 | scoped_feeding_opereation guard(*this); |
7c673cae FG |
492 | |
493 | do_feed_records(); | |
494 | } | |
495 | ||
496 | private: | |
497 | #ifndef BOOST_LOG_DOXYGEN_PASS | |
498 | //! The method spawns record feeding thread | |
499 | void start_feeding_thread() | |
500 | { | |
20effc67 TL |
501 | boost::thread(run_func(this)).swap(m_DedicatedFeedingThread); |
502 | } | |
503 | ||
504 | //! Starts record feeding operation. The method blocks or throws if another feeding operation is in progress. | |
505 | bool start_feeding_operation(unique_lock< frontend_mutex_type >& lock, operation op) | |
506 | { | |
507 | while (m_ActiveOperation != idle) | |
508 | { | |
509 | if (BOOST_UNLIKELY(op == feeding_records && m_ActiveOperation == feeding_records)) | |
510 | BOOST_LOG_THROW_DESCR(unexpected_call, "Asynchronous sink frontend already runs a record feeding thread"); | |
511 | ||
512 | if (BOOST_UNLIKELY(m_StopRequested.load(boost::memory_order_relaxed))) | |
513 | { | |
514 | m_StopRequested.store(false, boost::memory_order_relaxed); | |
515 | return true; | |
516 | } | |
517 | ||
518 | m_BlockCond.wait(lock); | |
519 | } | |
520 | ||
521 | m_ActiveOperation = op; | |
522 | ||
523 | return false; | |
524 | } | |
525 | ||
526 | //! Completes record feeding operation | |
527 | void complete_feeding_operation() BOOST_NOEXCEPT | |
528 | { | |
529 | try | |
530 | { | |
531 | lock_guard< frontend_mutex_type > lock(base_type::frontend_mutex()); | |
532 | m_ActiveOperation = idle; | |
533 | m_StopRequested.store(false, boost::memory_order_relaxed); | |
534 | m_BlockCond.notify_all(); | |
535 | } | |
536 | catch (...) | |
537 | { | |
538 | } | |
7c673cae FG |
539 | } |
540 | ||
541 | //! The record feeding loop | |
542 | void do_feed_records() | |
543 | { | |
544 | while (!m_StopRequested.load(boost::memory_order_acquire)) | |
545 | { | |
546 | record_view rec; | |
547 | bool dequeued = false; | |
548 | if (BOOST_LIKELY(!m_FlushRequested.load(boost::memory_order_acquire))) | |
549 | dequeued = queue_base_type::try_dequeue_ready(rec); | |
550 | else | |
551 | dequeued = queue_base_type::try_dequeue(rec); | |
552 | ||
553 | if (dequeued) | |
554 | base_type::feed_record(rec, m_BackendMutex, *m_pBackend); | |
555 | else | |
556 | break; | |
557 | } | |
558 | ||
559 | if (BOOST_UNLIKELY(m_FlushRequested.load(boost::memory_order_acquire))) | |
560 | { | |
561 | scoped_flag guard(base_type::frontend_mutex(), m_BlockCond, m_FlushRequested); | |
562 | base_type::flush_backend(m_BackendMutex, *m_pBackend); | |
563 | } | |
564 | } | |
565 | #endif // BOOST_LOG_DOXYGEN_PASS | |
566 | }; | |
567 | ||
568 | #undef BOOST_LOG_SINK_CTOR_FORWARD_INTERNAL_1 | |
569 | #undef BOOST_LOG_SINK_CTOR_FORWARD_INTERNAL_N | |
570 | #undef BOOST_LOG_SINK_CTOR_FORWARD_INTERNAL | |
571 | ||
572 | } // namespace sinks | |
573 | ||
574 | BOOST_LOG_CLOSE_NAMESPACE // namespace log | |
575 | ||
576 | } // namespace boost | |
577 | ||
578 | #include <boost/log/detail/footer.hpp> | |
579 | ||
580 | #endif // BOOST_LOG_SINKS_ASYNC_FRONTEND_HPP_INCLUDED_ |