Chris@16
|
1 /*
|
Chris@101
|
2 * Copyright Andrey Semashev 2007 - 2015.
|
Chris@16
|
3 * Distributed under the Boost Software License, Version 1.0.
|
Chris@16
|
4 * (See accompanying file LICENSE_1_0.txt or copy at
|
Chris@16
|
5 * http://www.boost.org/LICENSE_1_0.txt)
|
Chris@16
|
6 */
|
Chris@16
|
7 /*!
|
Chris@16
|
8 * \file async_frontend.hpp
|
Chris@16
|
9 * \author Andrey Semashev
|
Chris@16
|
10 * \date 14.07.2009
|
Chris@16
|
11 *
|
Chris@16
|
12 * The header contains implementation of asynchronous sink frontend.
|
Chris@16
|
13 */
|
Chris@16
|
14
|
Chris@16
|
15 #ifndef BOOST_LOG_SINKS_ASYNC_FRONTEND_HPP_INCLUDED_
|
Chris@16
|
16 #define BOOST_LOG_SINKS_ASYNC_FRONTEND_HPP_INCLUDED_
|
Chris@16
|
17
|
Chris@16
|
18 #include <boost/log/detail/config.hpp>
|
Chris@16
|
19
|
Chris@16
|
20 #ifdef BOOST_HAS_PRAGMA_ONCE
|
Chris@16
|
21 #pragma once
|
Chris@16
|
22 #endif
|
Chris@16
|
23
|
Chris@16
|
24 #if defined(BOOST_LOG_NO_THREADS)
|
Chris@16
|
25 #error Boost.Log: Asynchronous sink frontend is only supported in multithreaded environment
|
Chris@16
|
26 #endif
|
Chris@16
|
27
|
Chris@16
|
28 #include <boost/bind.hpp>
|
Chris@16
|
29 #include <boost/static_assert.hpp>
|
Chris@16
|
30 #include <boost/smart_ptr/shared_ptr.hpp>
|
Chris@16
|
31 #include <boost/smart_ptr/make_shared_object.hpp>
|
Chris@16
|
32 #include <boost/thread/locks.hpp>
|
Chris@101
|
33 #include <boost/thread/recursive_mutex.hpp>
|
Chris@16
|
34 #include <boost/thread/thread.hpp>
|
Chris@16
|
35 #include <boost/thread/condition_variable.hpp>
|
Chris@16
|
36 #include <boost/log/exceptions.hpp>
|
Chris@16
|
37 #include <boost/log/detail/locking_ptr.hpp>
|
Chris@16
|
38 #include <boost/log/detail/parameter_tools.hpp>
|
Chris@16
|
39 #include <boost/log/core/record_view.hpp>
|
Chris@16
|
40 #include <boost/log/sinks/basic_sink_frontend.hpp>
|
Chris@16
|
41 #include <boost/log/sinks/frontend_requirements.hpp>
|
Chris@16
|
42 #include <boost/log/sinks/unbounded_fifo_queue.hpp>
|
Chris@16
|
43 #include <boost/log/keywords/start_thread.hpp>
|
Chris@16
|
44 #include <boost/log/detail/header.hpp>
|
Chris@16
|
45
|
Chris@16
|
46 namespace boost {
|
Chris@16
|
47
|
Chris@16
|
48 BOOST_LOG_OPEN_NAMESPACE
|
Chris@16
|
49
|
Chris@16
|
50 namespace sinks {
|
Chris@16
|
51
|
Chris@16
|
52 #ifndef BOOST_LOG_DOXYGEN_PASS
|
Chris@16
|
53
|
Chris@16
|
54 #define BOOST_LOG_SINK_CTOR_FORWARD_INTERNAL(z, n, types)\
|
Chris@16
|
55 template< BOOST_PP_ENUM_PARAMS(n, typename T) >\
|
Chris@16
|
56 explicit asynchronous_sink(BOOST_PP_ENUM_BINARY_PARAMS(n, T, const& arg)) :\
|
Chris@16
|
57 base_type(true),\
|
Chris@16
|
58 queue_base_type((BOOST_PP_ENUM_PARAMS(n, arg))),\
|
Chris@16
|
59 m_pBackend(boost::make_shared< sink_backend_type >(BOOST_PP_ENUM_PARAMS(n, arg))),\
|
Chris@16
|
60 m_StopRequested(false),\
|
Chris@16
|
61 m_FlushRequested(false)\
|
Chris@16
|
62 {\
|
Chris@16
|
63 if ((BOOST_PP_ENUM_PARAMS(n, arg))[keywords::start_thread | true])\
|
Chris@16
|
64 start_feeding_thread();\
|
Chris@16
|
65 }\
|
Chris@16
|
66 template< BOOST_PP_ENUM_PARAMS(n, typename T) >\
|
Chris@16
|
67 explicit asynchronous_sink(shared_ptr< sink_backend_type > const& backend, BOOST_PP_ENUM_BINARY_PARAMS(n, T, const& arg)) :\
|
Chris@16
|
68 base_type(true),\
|
Chris@16
|
69 queue_base_type((BOOST_PP_ENUM_PARAMS(n, arg))),\
|
Chris@16
|
70 m_pBackend(backend),\
|
Chris@16
|
71 m_StopRequested(false),\
|
Chris@16
|
72 m_FlushRequested(false)\
|
Chris@16
|
73 {\
|
Chris@16
|
74 if ((BOOST_PP_ENUM_PARAMS(n, arg))[keywords::start_thread | true])\
|
Chris@16
|
75 start_feeding_thread();\
|
Chris@16
|
76 }
|
Chris@16
|
77
|
Chris@16
|
78 #endif // BOOST_LOG_DOXYGEN_PASS
|
Chris@16
|
79
|
Chris@16
|
80 /*!
|
Chris@16
|
81 * \brief Asynchronous logging sink frontend
|
Chris@16
|
82 *
|
Chris@16
|
83 * The frontend starts a separate thread on construction. All logging records are passed
|
Chris@16
|
84 * to the backend in this dedicated thread only.
|
Chris@16
|
85 */
|
Chris@16
|
86 template< typename SinkBackendT, typename QueueingStrategyT = unbounded_fifo_queue >
|
Chris@16
|
87 class asynchronous_sink :
|
Chris@16
|
88 public aux::make_sink_frontend_base< SinkBackendT >::type,
|
Chris@16
|
89 public QueueingStrategyT
|
Chris@16
|
90 {
|
Chris@16
|
91 typedef typename aux::make_sink_frontend_base< SinkBackendT >::type base_type;
|
Chris@16
|
92 typedef QueueingStrategyT queue_base_type;
|
Chris@16
|
93
|
Chris@16
|
94 private:
|
Chris@16
|
95 //! Backend synchronization mutex type
|
Chris@101
|
96 typedef boost::recursive_mutex backend_mutex_type;
|
Chris@16
|
97 //! Frontend synchronization mutex type
|
Chris@16
|
98 typedef typename base_type::mutex_type frontend_mutex_type;
|
Chris@16
|
99
|
Chris@16
|
100 //! A scope guard that implements thread ID management
|
Chris@16
|
101 class scoped_thread_id
|
Chris@16
|
102 {
|
Chris@16
|
103 private:
|
Chris@16
|
104 frontend_mutex_type& m_Mutex;
|
Chris@16
|
105 condition_variable_any& m_Cond;
|
Chris@16
|
106 thread::id& m_ThreadID;
|
Chris@16
|
107 bool volatile& m_StopRequested;
|
Chris@16
|
108
|
Chris@16
|
109 public:
|
Chris@16
|
110 //! Initializing constructor
|
Chris@16
|
111 scoped_thread_id(frontend_mutex_type& mut, condition_variable_any& cond, thread::id& tid, bool volatile& sr)
|
Chris@16
|
112 : m_Mutex(mut), m_Cond(cond), m_ThreadID(tid), m_StopRequested(sr)
|
Chris@16
|
113 {
|
Chris@16
|
114 lock_guard< frontend_mutex_type > lock(m_Mutex);
|
Chris@16
|
115 if (m_ThreadID != thread::id())
|
Chris@16
|
116 BOOST_LOG_THROW_DESCR(unexpected_call, "Asynchronous sink frontend already runs a record feeding thread");
|
Chris@16
|
117 m_ThreadID = this_thread::get_id();
|
Chris@16
|
118 }
|
Chris@16
|
119 //! Initializing constructor
|
Chris@16
|
120 scoped_thread_id(unique_lock< frontend_mutex_type >& l, condition_variable_any& cond, thread::id& tid, bool volatile& sr)
|
Chris@16
|
121 : m_Mutex(*l.mutex()), m_Cond(cond), m_ThreadID(tid), m_StopRequested(sr)
|
Chris@16
|
122 {
|
Chris@16
|
123 unique_lock< frontend_mutex_type > lock(move(l));
|
Chris@16
|
124 if (m_ThreadID != thread::id())
|
Chris@16
|
125 BOOST_LOG_THROW_DESCR(unexpected_call, "Asynchronous sink frontend already runs a record feeding thread");
|
Chris@16
|
126 m_ThreadID = this_thread::get_id();
|
Chris@16
|
127 }
|
Chris@16
|
128 //! Destructor
|
Chris@16
|
129 ~scoped_thread_id()
|
Chris@16
|
130 {
|
Chris@16
|
131 try
|
Chris@16
|
132 {
|
Chris@16
|
133 lock_guard< frontend_mutex_type > lock(m_Mutex);
|
Chris@16
|
134 m_StopRequested = false;
|
Chris@16
|
135 m_ThreadID = thread::id();
|
Chris@16
|
136 m_Cond.notify_all();
|
Chris@16
|
137 }
|
Chris@16
|
138 catch (...)
|
Chris@16
|
139 {
|
Chris@16
|
140 }
|
Chris@16
|
141 }
|
Chris@16
|
142
|
Chris@16
|
143 private:
|
Chris@16
|
144 scoped_thread_id(scoped_thread_id const&);
|
Chris@16
|
145 scoped_thread_id& operator= (scoped_thread_id const&);
|
Chris@16
|
146 };
|
Chris@16
|
147
|
Chris@16
|
148 //! A scope guard that resets a flag on destructor
|
Chris@16
|
149 class scoped_flag
|
Chris@16
|
150 {
|
Chris@16
|
151 private:
|
Chris@16
|
152 frontend_mutex_type& m_Mutex;
|
Chris@16
|
153 condition_variable_any& m_Cond;
|
Chris@16
|
154 volatile bool& m_Flag;
|
Chris@16
|
155
|
Chris@16
|
156 public:
|
Chris@16
|
157 explicit scoped_flag(frontend_mutex_type& mut, condition_variable_any& cond, volatile bool& f) :
|
Chris@16
|
158 m_Mutex(mut), m_Cond(cond), m_Flag(f)
|
Chris@16
|
159 {
|
Chris@16
|
160 }
|
Chris@16
|
161 ~scoped_flag()
|
Chris@16
|
162 {
|
Chris@16
|
163 try
|
Chris@16
|
164 {
|
Chris@16
|
165 lock_guard< frontend_mutex_type > lock(m_Mutex);
|
Chris@16
|
166 m_Flag = false;
|
Chris@16
|
167 m_Cond.notify_all();
|
Chris@16
|
168 }
|
Chris@16
|
169 catch (...)
|
Chris@16
|
170 {
|
Chris@16
|
171 }
|
Chris@16
|
172 }
|
Chris@16
|
173
|
Chris@16
|
174 private:
|
Chris@16
|
175 scoped_flag(scoped_flag const&);
|
Chris@16
|
176 scoped_flag& operator= (scoped_flag const&);
|
Chris@16
|
177 };
|
Chris@16
|
178
|
Chris@16
|
179 public:
|
Chris@16
|
180 //! Sink implementation type
|
Chris@16
|
181 typedef SinkBackendT sink_backend_type;
|
Chris@16
|
182 //! \cond
|
Chris@16
|
183 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");
|
Chris@16
|
184 //! \endcond
|
Chris@16
|
185
|
Chris@16
|
186 #ifndef BOOST_LOG_DOXYGEN_PASS
|
Chris@16
|
187
|
Chris@16
|
188 //! A pointer type that locks the backend until it's destroyed
|
Chris@101
|
189 typedef boost::log::aux::locking_ptr< sink_backend_type, backend_mutex_type > locked_backend_ptr;
|
Chris@16
|
190
|
Chris@16
|
191 #else // BOOST_LOG_DOXYGEN_PASS
|
Chris@16
|
192
|
Chris@16
|
193 //! A pointer type that locks the backend until it's destroyed
|
Chris@16
|
194 typedef implementation_defined locked_backend_ptr;
|
Chris@16
|
195
|
Chris@16
|
196 #endif // BOOST_LOG_DOXYGEN_PASS
|
Chris@16
|
197
|
Chris@16
|
198 private:
|
Chris@16
|
199 //! Synchronization mutex
|
Chris@16
|
200 backend_mutex_type m_BackendMutex;
|
Chris@16
|
201 //! Pointer to the backend
|
Chris@16
|
202 const shared_ptr< sink_backend_type > m_pBackend;
|
Chris@16
|
203
|
Chris@16
|
204 //! Dedicated record feeding thread
|
Chris@16
|
205 thread m_DedicatedFeedingThread;
|
Chris@16
|
206 //! Feeding thread ID
|
Chris@16
|
207 thread::id m_FeedingThreadID;
|
Chris@16
|
208 //! Condition variable to implement blocking operations
|
Chris@16
|
209 condition_variable_any m_BlockCond;
|
Chris@16
|
210
|
Chris@16
|
211 //! The flag indicates that the feeding loop has to be stopped
|
Chris@16
|
212 volatile bool m_StopRequested; // TODO: make it a real atomic
|
Chris@16
|
213 //! The flag indicates that queue flush has been requested
|
Chris@16
|
214 volatile bool m_FlushRequested; // TODO: make it a real atomic
|
Chris@16
|
215
|
Chris@16
|
216 public:
|
Chris@16
|
217 /*!
|
Chris@16
|
218 * Default constructor. Constructs the sink backend instance.
|
Chris@16
|
219 * Requires the backend to be default-constructible.
|
Chris@16
|
220 *
|
Chris@16
|
221 * \param start_thread If \c true, the frontend creates a thread to feed
|
Chris@16
|
222 * log records to the backend. Otherwise no thread is
|
Chris@16
|
223 * started and it is assumed that the user will call
|
Chris@16
|
224 * either \c run or \c feed_records himself.
|
Chris@16
|
225 */
|
Chris@16
|
226 asynchronous_sink(bool start_thread = true) :
|
Chris@16
|
227 base_type(true),
|
Chris@16
|
228 m_pBackend(boost::make_shared< sink_backend_type >()),
|
Chris@16
|
229 m_StopRequested(false),
|
Chris@16
|
230 m_FlushRequested(false)
|
Chris@16
|
231 {
|
Chris@16
|
232 if (start_thread)
|
Chris@16
|
233 start_feeding_thread();
|
Chris@16
|
234 }
|
Chris@16
|
235 /*!
|
Chris@16
|
236 * Constructor attaches user-constructed backend instance
|
Chris@16
|
237 *
|
Chris@16
|
238 * \param backend Pointer to the backend instance.
|
Chris@16
|
239 * \param start_thread If \c true, the frontend creates a thread to feed
|
Chris@16
|
240 * log records to the backend. Otherwise no thread is
|
Chris@16
|
241 * started and it is assumed that the user will call
|
Chris@16
|
242 * either \c run or \c feed_records himself.
|
Chris@16
|
243 *
|
Chris@16
|
244 * \pre \a backend is not \c NULL.
|
Chris@16
|
245 */
|
Chris@16
|
246 explicit asynchronous_sink(shared_ptr< sink_backend_type > const& backend, bool start_thread = true) :
|
Chris@16
|
247 base_type(true),
|
Chris@16
|
248 m_pBackend(backend),
|
Chris@16
|
249 m_StopRequested(false),
|
Chris@16
|
250 m_FlushRequested(false)
|
Chris@16
|
251 {
|
Chris@16
|
252 if (start_thread)
|
Chris@16
|
253 start_feeding_thread();
|
Chris@16
|
254 }
|
Chris@16
|
255
|
Chris@16
|
256 // Constructors that pass arbitrary parameters to the backend constructor
|
Chris@16
|
257 BOOST_LOG_PARAMETRIZED_CONSTRUCTORS_GEN(BOOST_LOG_SINK_CTOR_FORWARD_INTERNAL, ~)
|
Chris@16
|
258
|
Chris@16
|
259 /*!
|
Chris@16
|
260 * Destructor. Implicitly stops the dedicated feeding thread, if one is running.
|
Chris@16
|
261 */
|
Chris@16
|
262 ~asynchronous_sink()
|
Chris@16
|
263 {
|
Chris@16
|
264 boost::this_thread::disable_interruption no_interrupts;
|
Chris@16
|
265 stop();
|
Chris@16
|
266 }
|
Chris@16
|
267
|
Chris@16
|
268 /*!
|
Chris@16
|
269 * Locking accessor to the attached backend
|
Chris@16
|
270 */
|
Chris@16
|
271 locked_backend_ptr locked_backend()
|
Chris@16
|
272 {
|
Chris@101
|
273 return locked_backend_ptr(m_pBackend, m_BackendMutex);
|
Chris@16
|
274 }
|
Chris@16
|
275
|
Chris@16
|
276 /*!
|
Chris@16
|
277 * Enqueues the log record to the backend
|
Chris@16
|
278 */
|
Chris@16
|
279 void consume(record_view const& rec)
|
Chris@16
|
280 {
|
Chris@16
|
281 if (m_FlushRequested)
|
Chris@16
|
282 {
|
Chris@16
|
283 unique_lock< frontend_mutex_type > lock(base_type::frontend_mutex());
|
Chris@16
|
284 // Wait until flush is done
|
Chris@16
|
285 while (m_FlushRequested)
|
Chris@16
|
286 m_BlockCond.wait(lock);
|
Chris@16
|
287 }
|
Chris@16
|
288 queue_base_type::enqueue(rec);
|
Chris@16
|
289 }
|
Chris@16
|
290
|
Chris@16
|
291 /*!
|
Chris@16
|
292 * The method attempts to pass logging record to the backend
|
Chris@16
|
293 */
|
Chris@16
|
294 bool try_consume(record_view const& rec)
|
Chris@16
|
295 {
|
Chris@16
|
296 if (!m_FlushRequested)
|
Chris@16
|
297 {
|
Chris@16
|
298 return queue_base_type::try_enqueue(rec);
|
Chris@16
|
299 }
|
Chris@16
|
300 else
|
Chris@16
|
301 return false;
|
Chris@16
|
302 }
|
Chris@16
|
303
|
Chris@16
|
304 /*!
|
Chris@16
|
305 * The method starts record feeding loop and effectively blocks until either of this happens:
|
Chris@16
|
306 *
|
Chris@16
|
307 * \li the thread is interrupted due to either standard thread interruption or a call to \c stop
|
Chris@16
|
308 * \li an exception is thrown while processing a log record in the backend, and the exception is
|
Chris@16
|
309 * not terminated by the exception handler, if one is installed
|
Chris@16
|
310 *
|
Chris@16
|
311 * \pre The sink frontend must be constructed without spawning a dedicated thread
|
Chris@16
|
312 */
|
Chris@16
|
313 void run()
|
Chris@16
|
314 {
|
Chris@16
|
315 // First check that no other thread is running
|
Chris@16
|
316 scoped_thread_id guard(base_type::frontend_mutex(), m_BlockCond, m_FeedingThreadID, m_StopRequested);
|
Chris@16
|
317
|
Chris@16
|
318 // Now start the feeding loop
|
Chris@16
|
319 while (true)
|
Chris@16
|
320 {
|
Chris@16
|
321 do_feed_records();
|
Chris@16
|
322 if (!m_StopRequested)
|
Chris@16
|
323 {
|
Chris@16
|
324 // Block until new record is available
|
Chris@16
|
325 record_view rec;
|
Chris@16
|
326 if (queue_base_type::dequeue_ready(rec))
|
Chris@16
|
327 base_type::feed_record(rec, m_BackendMutex, *m_pBackend);
|
Chris@16
|
328 }
|
Chris@16
|
329 else
|
Chris@16
|
330 break;
|
Chris@16
|
331 }
|
Chris@16
|
332 }
|
Chris@16
|
333
|
Chris@16
|
334 /*!
|
Chris@16
|
335 * The method softly interrupts record feeding loop. This method must be called when the \c run
|
Chris@16
|
336 * method execution has to be interrupted. Unlike regular thread interruption, calling
|
Chris@16
|
337 * \c stop will not interrupt the record processing in the middle. Instead, the sink frontend
|
Chris@16
|
338 * will attempt to finish its business with the record in progress and return afterwards.
|
Chris@16
|
339 * This method can be called either if the sink was created with a dedicated thread,
|
Chris@16
|
340 * or if the feeding loop was initiated by user.
|
Chris@16
|
341 *
|
Chris@16
|
342 * \note Returning from this method does not guarantee that there are no records left buffered
|
Chris@16
|
343 * in the sink frontend. It is possible that log records keep coming during and after this
|
Chris@16
|
344 * method is called. At some point of execution of this method log records stop being processed,
|
Chris@16
|
345 * and all records that come after this point are put into the queue. These records will be
|
Chris@16
|
346 * processed upon further calls to \c run or \c feed_records.
|
Chris@16
|
347 */
|
Chris@16
|
348 void stop()
|
Chris@16
|
349 {
|
Chris@16
|
350 unique_lock< frontend_mutex_type > lock(base_type::frontend_mutex());
|
Chris@16
|
351 if (m_FeedingThreadID != thread::id() || m_DedicatedFeedingThread.joinable())
|
Chris@16
|
352 {
|
Chris@16
|
353 try
|
Chris@16
|
354 {
|
Chris@16
|
355 m_StopRequested = true;
|
Chris@16
|
356 queue_base_type::interrupt_dequeue();
|
Chris@16
|
357 while (m_StopRequested)
|
Chris@16
|
358 m_BlockCond.wait(lock);
|
Chris@16
|
359 }
|
Chris@16
|
360 catch (...)
|
Chris@16
|
361 {
|
Chris@16
|
362 m_StopRequested = false;
|
Chris@16
|
363 throw;
|
Chris@16
|
364 }
|
Chris@16
|
365
|
Chris@16
|
366 lock.unlock();
|
Chris@16
|
367 m_DedicatedFeedingThread.join();
|
Chris@16
|
368 }
|
Chris@16
|
369 }
|
Chris@16
|
370
|
Chris@16
|
371 /*!
|
Chris@16
|
372 * The method feeds log records that may have been buffered to the backend and returns
|
Chris@16
|
373 *
|
Chris@16
|
374 * \pre The sink frontend must be constructed without spawning a dedicated thread
|
Chris@16
|
375 */
|
Chris@16
|
376 void feed_records()
|
Chris@16
|
377 {
|
Chris@16
|
378 // First check that no other thread is running
|
Chris@16
|
379 scoped_thread_id guard(base_type::frontend_mutex(), m_BlockCond, m_FeedingThreadID, m_StopRequested);
|
Chris@16
|
380
|
Chris@16
|
381 // Now start the feeding loop
|
Chris@16
|
382 do_feed_records();
|
Chris@16
|
383 }
|
Chris@16
|
384
|
Chris@16
|
385 /*!
|
Chris@16
|
386 * The method feeds all log records that may have been buffered to the backend and returns.
|
Chris@16
|
387 * Unlike \c feed_records, in case of ordering queueing the method also feeds records
|
Chris@16
|
388 * that were enqueued during the ordering window, attempting to empty the queue completely.
|
Chris@16
|
389 *
|
Chris@16
|
390 * \pre The sink frontend must be constructed without spawning a dedicated thread
|
Chris@16
|
391 */
|
Chris@16
|
392 void flush()
|
Chris@16
|
393 {
|
Chris@16
|
394 unique_lock< frontend_mutex_type > lock(base_type::frontend_mutex());
|
Chris@16
|
395 if (m_FeedingThreadID != thread::id() || m_DedicatedFeedingThread.joinable())
|
Chris@16
|
396 {
|
Chris@16
|
397 // There is already a thread feeding records, let it do the job
|
Chris@16
|
398 m_FlushRequested = true;
|
Chris@16
|
399 queue_base_type::interrupt_dequeue();
|
Chris@16
|
400 while (!m_StopRequested && m_FlushRequested)
|
Chris@16
|
401 m_BlockCond.wait(lock);
|
Chris@16
|
402
|
Chris@16
|
403 // The condition may have been signalled when the feeding thread was finishing.
|
Chris@16
|
404 // In that case records may not have been flushed, and we do the flush ourselves.
|
Chris@16
|
405 if (m_FeedingThreadID != thread::id())
|
Chris@16
|
406 return;
|
Chris@16
|
407 }
|
Chris@16
|
408
|
Chris@16
|
409 m_FlushRequested = true;
|
Chris@16
|
410
|
Chris@16
|
411 // Flush records ourselves. The guard releases the lock.
|
Chris@16
|
412 scoped_thread_id guard(lock, m_BlockCond, m_FeedingThreadID, m_StopRequested);
|
Chris@16
|
413
|
Chris@16
|
414 do_feed_records();
|
Chris@16
|
415 }
|
Chris@16
|
416
|
Chris@16
|
417 private:
|
Chris@16
|
418 #ifndef BOOST_LOG_DOXYGEN_PASS
|
Chris@16
|
419 //! The method spawns record feeding thread
|
Chris@16
|
420 void start_feeding_thread()
|
Chris@16
|
421 {
|
Chris@16
|
422 boost::thread(boost::bind(&asynchronous_sink::run, this)).swap(m_DedicatedFeedingThread);
|
Chris@16
|
423 }
|
Chris@16
|
424
|
Chris@16
|
425 //! The record feeding loop
|
Chris@16
|
426 void do_feed_records()
|
Chris@16
|
427 {
|
Chris@16
|
428 while (!m_StopRequested)
|
Chris@16
|
429 {
|
Chris@16
|
430 record_view rec;
|
Chris@101
|
431 bool dequeued = false;
|
Chris@16
|
432 if (!m_FlushRequested)
|
Chris@16
|
433 dequeued = queue_base_type::try_dequeue_ready(rec);
|
Chris@16
|
434 else
|
Chris@16
|
435 dequeued = queue_base_type::try_dequeue(rec);
|
Chris@16
|
436
|
Chris@16
|
437 if (dequeued)
|
Chris@16
|
438 base_type::feed_record(rec, m_BackendMutex, *m_pBackend);
|
Chris@16
|
439 else
|
Chris@16
|
440 break;
|
Chris@16
|
441 }
|
Chris@16
|
442
|
Chris@16
|
443 if (m_FlushRequested)
|
Chris@16
|
444 {
|
Chris@16
|
445 scoped_flag guard(base_type::frontend_mutex(), m_BlockCond, m_FlushRequested);
|
Chris@16
|
446 base_type::flush_backend(m_BackendMutex, *m_pBackend);
|
Chris@16
|
447 }
|
Chris@16
|
448 }
|
Chris@16
|
449 #endif // BOOST_LOG_DOXYGEN_PASS
|
Chris@16
|
450 };
|
Chris@16
|
451
|
Chris@16
|
452 #undef BOOST_LOG_SINK_CTOR_FORWARD_INTERNAL
|
Chris@16
|
453
|
Chris@16
|
454 } // namespace sinks
|
Chris@16
|
455
|
Chris@16
|
456 BOOST_LOG_CLOSE_NAMESPACE // namespace log
|
Chris@16
|
457
|
Chris@16
|
458 } // namespace boost
|
Chris@16
|
459
|
Chris@16
|
460 #include <boost/log/detail/footer.hpp>
|
Chris@16
|
461
|
Chris@16
|
462 #endif // BOOST_LOG_SINKS_ASYNC_FRONTEND_HPP_INCLUDED_
|