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 core.cpp
9 * \author Andrey Semashev
10 * \date 19.04.2007
11 *
12 * \brief This header 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
16#include <boost/log/detail/config.hpp>
17#include <cstddef>
18#include <new>
19#include <vector>
20#include <algorithm>
21#include <boost/cstdint.hpp>
22#include <boost/assert.hpp>
23#include <boost/core/invoke_swap.hpp>
24#include <boost/filesystem/path.hpp>
25#include <boost/smart_ptr/weak_ptr.hpp>
26#include <boost/smart_ptr/shared_ptr.hpp>
27#include <boost/smart_ptr/make_shared_object.hpp>
28#include <boost/range/iterator_range_core.hpp>
29#include <boost/date_time/posix_time/posix_time_types.hpp>
30#include <boost/random/taus88.hpp>
31#include <boost/move/core.hpp>
32#include <boost/move/utility_core.hpp>
33#include <boost/log/core/core.hpp>
34#include <boost/log/core/record.hpp>
35#include <boost/log/core/record_view.hpp>
36#include <boost/log/sinks/sink.hpp>
37#include <boost/log/attributes/attribute_value_set.hpp>
38#include <boost/log/detail/singleton.hpp>
39#if !defined(BOOST_LOG_NO_THREADS)
40#include <boost/memory_order.hpp>
41#include <boost/atomic/atomic.hpp>
42#include <boost/thread/tss.hpp>
43#include <boost/thread/exceptions.hpp>
44#include <boost/log/detail/locks.hpp>
45#include <boost/log/detail/light_rw_mutex.hpp>
46#include <boost/log/detail/thread_id.hpp>
47#endif
48#include "unique_ptr.hpp"
49#include "default_sink.hpp"
50#include "stateless_allocator.hpp"
51#include "alignment_gap_between.hpp"
52#include <boost/log/detail/header.hpp>
53
54namespace boost {
55
56BOOST_LOG_OPEN_NAMESPACE
57
58namespace aux {
59
60BOOST_LOG_ANONYMOUS_NAMESPACE {
61
62//! Sequence shuffling algorithm. Very similar to std::random_shuffle, used for forward portability with compilers that removed it from the standard library (C++17).
63template< typename Iterator, typename RandomNumberGenerator >
64inline void random_shuffle(Iterator begin, Iterator end, RandomNumberGenerator& rng)
65{
66 Iterator it = begin;
67 ++it;
68 while (it != end)
69 {
70 Iterator where = begin + rng() % (it - begin + 1u);
71 if (where != it)
72 boost::core::invoke_swap(*where, *it);
73 ++it;
74 }
75}
76
77} // namespace
78
79} // namespace aux
80
81//! Private record data information, with core-specific structures
82struct record_view::private_data :
83 public public_data
84{
85 //! Underlying memory allocator
86 typedef boost::log::aux::stateless_allocator< char > stateless_allocator;
87 //! Sink pointer type
88 typedef weak_ptr< sinks::sink > sink_ptr;
89 //! Iterator range with pointers to the accepting sinks
90 typedef iterator_range< sink_ptr* > sink_list;
91
92private:
93 //! Number of sinks accepting the record
94 uint32_t m_accepting_sink_count;
95 //! Maximum number of sinks accepting the record
96 const uint32_t m_accepting_sink_capacity;
97 //! The flag indicates that the record has to be detached from the current thread
98 bool m_detach_from_thread_needed;
99
100private:
101 //! Initializing constructor
102 private_data(BOOST_RV_REF(attribute_value_set) values, uint32_t capacity) BOOST_NOEXCEPT :
103 public_data(boost::move(t&: values)),
104 m_accepting_sink_count(0),
105 m_accepting_sink_capacity(capacity),
106 m_detach_from_thread_needed(false)
107 {
108 }
109
110public:
111 //! Creates the object with the specified capacity
112 static private_data* create(BOOST_RV_REF(attribute_value_set) values, uint32_t capacity)
113 {
114 private_data* p = reinterpret_cast< private_data* >(stateless_allocator().allocate
115 (
116 n: sizeof(private_data) +
117 boost::log::aux::alignment_gap_between< private_data, sink_ptr >::value +
118 capacity * sizeof(sink_ptr)
119 ));
120 new (p) private_data(boost::move(t&: values), capacity);
121 return p;
122 }
123
124 //! Destroys the object and frees the underlying storage
125 void destroy() BOOST_NOEXCEPT
126 {
127 sink_ptr* psink = begin();
128 for (uint32_t i = 0u, n = m_accepting_sink_count; i < n; ++i)
129 {
130 psink[i].~sink_ptr();
131 }
132
133 const uint32_t capacity = m_accepting_sink_capacity;
134 this->~private_data();
135
136 stateless_allocator().deallocate
137 (
138 p: reinterpret_cast< stateless_allocator::pointer >(this),
139 sizeof(private_data) +
140 boost::log::aux::alignment_gap_between< private_data, sink_ptr >::value +
141 capacity * sizeof(sink_ptr)
142 );
143 }
144
145 //! Returns iterator range with the pointers to the accepting sinks
146 sink_list get_accepting_sinks() BOOST_NOEXCEPT
147 {
148 sink_ptr* p = begin();
149 return sink_list(p, p + m_accepting_sink_count);
150 }
151
152 //! Adds an accepting sink
153 void push_back_accepting_sink(shared_ptr< sinks::sink > const& sink)
154 {
155 BOOST_ASSERT(m_accepting_sink_count < m_accepting_sink_capacity);
156 sink_ptr* p = begin() + m_accepting_sink_count;
157 new (p) sink_ptr(sink);
158 ++m_accepting_sink_count;
159 m_detach_from_thread_needed |= sink->is_cross_thread();
160 }
161
162 //! Returns the number of accepting sinks
163 uint32_t accepting_sink_count() const BOOST_NOEXCEPT { return m_accepting_sink_count; }
164
165 //! Returns the flag indicating whether it is needed to detach the record from the current thread
166 bool is_detach_from_thread_needed() const BOOST_NOEXCEPT { return m_detach_from_thread_needed; }
167
168 BOOST_DELETED_FUNCTION(private_data(private_data const&))
169 BOOST_DELETED_FUNCTION(private_data& operator= (private_data const&))
170
171private:
172 //! Returns a pointer to the first accepting sink
173 sink_ptr* begin() BOOST_NOEXCEPT
174 {
175 return reinterpret_cast< sink_ptr* >
176 (
177 reinterpret_cast< char* >(this) +
178 sizeof(private_data) +
179 boost::log::aux::alignment_gap_between< private_data, sink_ptr >::value
180 );
181 }
182};
183
184//! Destructor
185BOOST_LOG_API void record_view::public_data::destroy(const public_data* p) BOOST_NOEXCEPT
186{
187 const_cast< private_data* >(static_cast< const private_data* >(p))->destroy();
188}
189
190//! The function ensures that the log record does not depend on any thread-specific data.
191BOOST_LOG_API record_view record::lock()
192{
193 BOOST_ASSERT(m_impl != NULL);
194
195 record_view::private_data* const impl = static_cast< record_view::private_data* >(m_impl);
196 if (impl->is_detach_from_thread_needed())
197 {
198 attribute_value_set::const_iterator
199 it = impl->m_attribute_values.begin(),
200 end = impl->m_attribute_values.end();
201 for (; it != end; ++it)
202 {
203 // Yep, a bit hackish. I'll need a better backdoor to do it gracefully.
204 const_cast< attribute_value_set::mapped_type& >(it->second).detach_from_thread();
205 }
206 }
207
208 // Move the implementation to the view
209 m_impl = NULL;
210 return record_view(impl);
211}
212
213//! Logging system implementation
214struct core::implementation :
215 public log::aux::lazy_singleton<
216 implementation,
217 core_ptr
218 >
219{
220public:
221 //! Base type of singleton holder
222 typedef log::aux::lazy_singleton<
223 implementation,
224 core_ptr
225 > base_type;
226
227#if !defined(BOOST_LOG_NO_THREADS)
228 //! Read lock type
229 typedef log::aux::shared_lock_guard< log::aux::light_rw_mutex > scoped_read_lock;
230 //! Write lock type
231 typedef log::aux::exclusive_lock_guard< log::aux::light_rw_mutex > scoped_write_lock;
232#endif
233
234 //! Sinks container type
235 typedef std::vector< shared_ptr< sinks::sink > > sink_list;
236
237 //! Thread-specific data
238 struct thread_data
239 {
240 //! Thread-specific attribute set
241 attribute_set m_thread_attributes;
242 //! Random number generator for shuffling
243 random::taus88 m_rng;
244
245 thread_data() : m_rng(get_random_seed())
246 {
247 }
248
249 private:
250 //! Creates a seed for RNG
251 static uint32_t get_random_seed()
252 {
253 uint32_t seed = static_cast< uint32_t >(posix_time::microsec_clock::universal_time().time_of_day().ticks());
254#if !defined(BOOST_LOG_NO_THREADS)
255 seed += static_cast< uint32_t >(log::aux::this_thread::get_id().native_id());
256#endif
257 return seed;
258 }
259 };
260
261public:
262#if !defined(BOOST_LOG_NO_THREADS)
263 //! Synchronization mutex
264 log::aux::light_rw_mutex m_mutex;
265#endif
266
267 //! List of sinks involved into output
268 sink_list m_sinks;
269 //! Default sink
270 const shared_ptr< sinks::sink > m_default_sink;
271
272 //! Global attribute set
273 attribute_set m_global_attributes;
274#if !defined(BOOST_LOG_NO_THREADS)
275 //! Thread-specific data
276 thread_specific_ptr< thread_data > m_thread_data;
277
278#if defined(BOOST_LOG_USE_COMPILER_TLS)
279 //! Cached pointer to the thread-specific data
280 static BOOST_LOG_TLS thread_data* m_thread_data_cache;
281#endif
282
283#else
284 //! Thread-specific data
285 log::aux::unique_ptr< thread_data > m_thread_data;
286#endif
287
288 //! The global state of logging
289#if !defined(BOOST_LOG_NO_THREADS)
290 boost::atomic< bool > m_enabled;
291#else
292 bool m_enabled;
293#endif
294 //! Global filter
295 filter m_filter;
296
297 //! Exception handler
298 exception_handler_type m_exception_handler;
299
300public:
301 //! Constructor
302 implementation() :
303 m_default_sink(boost::make_shared< sinks::aux::default_sink >()),
304 m_enabled(true)
305 {
306 }
307
308 //! Opens a record
309 template< typename SourceAttributesT >
310 BOOST_FORCEINLINE record open_record(BOOST_FWD_REF(SourceAttributesT) source_attributes)
311 {
312 record_view::private_data* rec_impl = NULL;
313 bool invoke_exception_handler = true;
314
315 // Try a quick win first
316#if !defined(BOOST_LOG_NO_THREADS)
317 if (BOOST_LIKELY(m_enabled.load(boost::memory_order_relaxed)))
318#else
319 if (BOOST_LIKELY(m_enabled))
320#endif
321 try
322 {
323 thread_data* tsd = get_thread_data();
324
325#if !defined(BOOST_LOG_NO_THREADS)
326 // Lock the core to be safe against any attribute or sink set modifications
327 scoped_read_lock lock(m_mutex);
328
329 if (BOOST_LIKELY(m_enabled.load(boost::memory_order_relaxed)))
330#endif
331 {
332 // Compose a view of attribute values (unfrozen, yet)
333 attribute_value_set attr_values(boost::forward< SourceAttributesT >(source_attributes), tsd->m_thread_attributes, m_global_attributes);
334 if (m_filter(attr_values))
335 {
336 // The global filter passed, trying the sinks
337 attribute_value_set* values = &attr_values;
338
339 // apply_sink_filter will invoke the exception handler if it has to
340 invoke_exception_handler = false;
341
342 if (!m_sinks.empty())
343 {
344 uint32_t remaining_capacity = static_cast< uint32_t >(m_sinks.size());
345 sink_list::iterator it = m_sinks.begin(), end = m_sinks.end();
346 for (; it != end; ++it, --remaining_capacity)
347 {
348 apply_sink_filter(sink: *it, rec_impl, attr_values&: values, remaining_capacity);
349 }
350 }
351 else
352 {
353 // Use the default sink
354 apply_sink_filter(sink: m_default_sink, rec_impl, attr_values&: values, remaining_capacity: 1);
355 }
356
357 invoke_exception_handler = true;
358
359 if (rec_impl && rec_impl->accepting_sink_count() == 0)
360 {
361 // No sinks accepted the record
362 rec_impl->destroy();
363 rec_impl = NULL;
364 goto done;
365 }
366
367 // Some sinks have accepted the record
368 values->freeze();
369 }
370 }
371 }
372#if !defined(BOOST_LOG_NO_THREADS)
373 catch (thread_interrupted&)
374 {
375 if (rec_impl)
376 rec_impl->destroy();
377 throw;
378 }
379#endif // !defined(BOOST_LOG_NO_THREADS)
380 catch (...)
381 {
382 if (rec_impl)
383 {
384 rec_impl->destroy();
385 rec_impl = NULL;
386 }
387
388 if (invoke_exception_handler)
389 {
390 // Lock the core to be safe against any attribute or sink set modifications
391 BOOST_LOG_EXPR_IF_MT(scoped_read_lock lock(m_mutex);)
392 if (m_exception_handler.empty())
393 throw;
394
395 m_exception_handler();
396 }
397 else
398 throw;
399 }
400
401 done:
402 return record(rec_impl);
403 }
404
405 //! The method returns the current thread-specific data
406 thread_data* get_thread_data()
407 {
408#if defined(BOOST_LOG_USE_COMPILER_TLS)
409 thread_data* p = m_thread_data_cache;
410#else
411 thread_data* p = m_thread_data.get();
412#endif
413 if (BOOST_UNLIKELY(!p))
414 {
415 init_thread_data();
416#if defined(BOOST_LOG_USE_COMPILER_TLS)
417 p = m_thread_data_cache;
418#else
419 p = m_thread_data.get();
420#endif
421 }
422 return p;
423 }
424
425 //! The function initializes the logging system
426 static void init_instance()
427 {
428 base_type::get_instance().reset(p: new core());
429 }
430
431private:
432 //! The method initializes thread-specific data
433 void init_thread_data()
434 {
435 BOOST_LOG_EXPR_IF_MT(scoped_write_lock lock(m_mutex);)
436 if (!m_thread_data.get())
437 {
438 log::aux::unique_ptr< thread_data > p(new thread_data());
439 m_thread_data.reset(new_value: p.get());
440#if defined(BOOST_LOG_USE_COMPILER_TLS)
441 m_thread_data_cache = p.release();
442#else
443 p.release();
444#endif
445 }
446 }
447
448 //! Invokes sink-specific filter and adds the sink to the record if the filter passes the log record
449 void apply_sink_filter(shared_ptr< sinks::sink > const& sink, record_view::private_data*& rec_impl, attribute_value_set*& attr_values, uint32_t remaining_capacity)
450 {
451 try
452 {
453 if (sink->will_consume(attributes: *attr_values))
454 {
455 // If at least one sink accepts the record, it's time to create it
456 record_view::private_data* impl = rec_impl;
457 if (!impl)
458 {
459 rec_impl = impl = record_view::private_data::create(values: boost::move(t&: *attr_values), capacity: remaining_capacity);
460 attr_values = &impl->m_attribute_values;
461 }
462
463 impl->push_back_accepting_sink(sink);
464 }
465 }
466#if !defined(BOOST_LOG_NO_THREADS)
467 catch (thread_interrupted&)
468 {
469 throw;
470 }
471#endif // !defined(BOOST_LOG_NO_THREADS)
472 catch (...)
473 {
474 if (m_exception_handler.empty())
475 throw;
476 m_exception_handler();
477 }
478 }
479};
480
481#if defined(BOOST_LOG_USE_COMPILER_TLS)
482//! Cached pointer to the thread-specific data
483BOOST_LOG_TLS core::implementation::thread_data* core::implementation::m_thread_data_cache = NULL;
484#endif // defined(BOOST_LOG_USE_COMPILER_TLS)
485
486//! Logging system constructor
487core::core() :
488 m_impl(new implementation())
489{
490}
491
492//! Logging system destructor
493core::~core()
494{
495 delete m_impl;
496 m_impl = NULL;
497}
498
499//! The method returns a pointer to the logging system instance
500BOOST_LOG_API core_ptr core::get()
501{
502 return implementation::get();
503}
504
505//! The method enables or disables logging and returns the previous state of logging flag
506BOOST_LOG_API bool core::set_logging_enabled(bool enabled)
507{
508#if !defined(BOOST_LOG_NO_THREADS)
509 return m_impl->m_enabled.exchange(v: enabled, order: boost::memory_order_relaxed);
510#else
511 const bool old_value = m_impl->m_enabled;
512 m_impl->m_enabled = enabled;
513 return old_value;
514#endif
515}
516
517//! The method allows to detect if logging is enabled
518BOOST_LOG_API bool core::get_logging_enabled() const
519{
520#if !defined(BOOST_LOG_NO_THREADS)
521 return m_impl->m_enabled.load(order: boost::memory_order_relaxed);
522#else
523 return m_impl->m_enabled;
524#endif
525}
526
527//! The method adds a new sink
528BOOST_LOG_API void core::add_sink(shared_ptr< sinks::sink > const& s)
529{
530 BOOST_LOG_EXPR_IF_MT(implementation::scoped_write_lock lock(m_impl->m_mutex);)
531 implementation::sink_list::iterator it =
532 std::find(first: m_impl->m_sinks.begin(), last: m_impl->m_sinks.end(), val: s);
533 if (it == m_impl->m_sinks.end())
534 m_impl->m_sinks.push_back(x: s);
535}
536
537//! The method removes the sink from the output
538BOOST_LOG_API void core::remove_sink(shared_ptr< sinks::sink > const& s)
539{
540 BOOST_LOG_EXPR_IF_MT(implementation::scoped_write_lock lock(m_impl->m_mutex);)
541 implementation::sink_list::iterator it =
542 std::find(first: m_impl->m_sinks.begin(), last: m_impl->m_sinks.end(), val: s);
543 if (it != m_impl->m_sinks.end())
544 m_impl->m_sinks.erase(position: it);
545}
546
547//! The method removes all registered sinks from the output
548BOOST_LOG_API void core::remove_all_sinks()
549{
550 BOOST_LOG_EXPR_IF_MT(implementation::scoped_write_lock lock(m_impl->m_mutex);)
551 m_impl->m_sinks.clear();
552}
553
554
555//! The method adds an attribute to the global attribute set
556BOOST_LOG_API std::pair< attribute_set::iterator, bool >
557core::add_global_attribute(attribute_name const& name, attribute const& attr)
558{
559 BOOST_LOG_EXPR_IF_MT(implementation::scoped_write_lock lock(m_impl->m_mutex);)
560 return m_impl->m_global_attributes.insert(key: name, data: attr);
561}
562
563//! The method removes an attribute from the global attribute set
564BOOST_LOG_API void core::remove_global_attribute(attribute_set::iterator it)
565{
566 BOOST_LOG_EXPR_IF_MT(implementation::scoped_write_lock lock(m_impl->m_mutex);)
567 m_impl->m_global_attributes.erase(it);
568}
569
570//! The method returns the complete set of currently registered global attributes
571BOOST_LOG_API attribute_set core::get_global_attributes() const
572{
573 BOOST_LOG_EXPR_IF_MT(implementation::scoped_read_lock lock(m_impl->m_mutex);)
574 return m_impl->m_global_attributes;
575}
576
577//! The method replaces the complete set of currently registered global attributes with the provided set
578BOOST_LOG_API void core::set_global_attributes(attribute_set const& attrs)
579{
580 BOOST_LOG_EXPR_IF_MT(implementation::scoped_write_lock lock(m_impl->m_mutex);)
581 m_impl->m_global_attributes = attrs;
582}
583
584//! The method adds an attribute to the thread-specific attribute set
585BOOST_LOG_API std::pair< attribute_set::iterator, bool >
586core::add_thread_attribute(attribute_name const& name, attribute const& attr)
587{
588 implementation::thread_data* p = m_impl->get_thread_data();
589 return p->m_thread_attributes.insert(key: name, data: attr);
590}
591
592//! The method removes an attribute from the thread-specific attribute set
593BOOST_LOG_API void core::remove_thread_attribute(attribute_set::iterator it)
594{
595 implementation::thread_data* p = m_impl->get_thread_data();
596 p->m_thread_attributes.erase(it);
597}
598
599//! The method returns the complete set of currently registered thread-specific attributes
600BOOST_LOG_API attribute_set core::get_thread_attributes() const
601{
602 implementation::thread_data* p = m_impl->get_thread_data();
603 return p->m_thread_attributes;
604}
605//! The method replaces the complete set of currently registered thread-specific attributes with the provided set
606BOOST_LOG_API void core::set_thread_attributes(attribute_set const& attrs)
607{
608 implementation::thread_data* p = m_impl->get_thread_data();
609 p->m_thread_attributes = attrs;
610}
611
612//! An internal method to set the global filter
613BOOST_LOG_API void core::set_filter(filter const& filter)
614{
615 BOOST_LOG_EXPR_IF_MT(implementation::scoped_write_lock lock(m_impl->m_mutex);)
616 m_impl->m_filter = filter;
617}
618
619//! The method removes the global logging filter
620BOOST_LOG_API void core::reset_filter()
621{
622 BOOST_LOG_EXPR_IF_MT(implementation::scoped_write_lock lock(m_impl->m_mutex);)
623 m_impl->m_filter.reset();
624}
625
626//! The method sets exception handler function
627BOOST_LOG_API void core::set_exception_handler(exception_handler_type const& handler)
628{
629 BOOST_LOG_EXPR_IF_MT(implementation::scoped_write_lock lock(m_impl->m_mutex);)
630 m_impl->m_exception_handler = handler;
631}
632
633//! The method performs flush on all registered sinks.
634BOOST_LOG_API void core::flush()
635{
636 // Acquire exclusive lock to prevent any logging attempts while flushing
637 BOOST_LOG_EXPR_IF_MT(implementation::scoped_write_lock lock(m_impl->m_mutex);)
638 if (BOOST_LIKELY(!m_impl->m_sinks.empty()))
639 {
640 implementation::sink_list::iterator it = m_impl->m_sinks.begin(), end = m_impl->m_sinks.end();
641 for (; it != end; ++it)
642 {
643 try
644 {
645 it->get()->flush();
646 }
647#if !defined(BOOST_LOG_NO_THREADS)
648 catch (thread_interrupted&)
649 {
650 throw;
651 }
652#endif // !defined(BOOST_LOG_NO_THREADS)
653 catch (...)
654 {
655 if (m_impl->m_exception_handler.empty())
656 throw;
657 m_impl->m_exception_handler();
658 }
659 }
660 }
661 else
662 {
663 try
664 {
665 m_impl->m_default_sink->flush();
666 }
667#if !defined(BOOST_LOG_NO_THREADS)
668 catch (thread_interrupted&)
669 {
670 throw;
671 }
672#endif // !defined(BOOST_LOG_NO_THREADS)
673 catch (...)
674 {
675 if (m_impl->m_exception_handler.empty())
676 throw;
677 m_impl->m_exception_handler();
678 }
679 }
680}
681
682//! The method attempts to open a new record to be written
683BOOST_LOG_API record core::open_record(attribute_set const& source_attributes)
684{
685 return m_impl->open_record(source_attributes);
686}
687
688//! The method attempts to open a new record to be written
689BOOST_LOG_API record core::open_record(attribute_value_set const& source_attributes)
690{
691 return m_impl->open_record(source_attributes);
692}
693
694//! The method attempts to open a new record to be written.
695BOOST_LOG_API record core::open_record_move(attribute_value_set& source_attributes)
696{
697 return m_impl->open_record(source_attributes: boost::move(t&: source_attributes));
698}
699
700//! The method pushes the record
701BOOST_LOG_API void core::push_record_move(record& rec)
702{
703 try
704 {
705 record_view rec_view(rec.lock());
706 record_view::private_data* data = static_cast< record_view::private_data* >(rec_view.m_impl.get());
707
708 typedef std::vector< shared_ptr< sinks::sink > > accepting_sinks_t;
709 accepting_sinks_t accepting_sinks(data->accepting_sink_count());
710 shared_ptr< sinks::sink >* const begin = &*accepting_sinks.begin();
711 shared_ptr< sinks::sink >* end = begin;
712
713 // Lock sinks that are willing to consume the record
714 record_view::private_data::sink_list weak_sinks = data->get_accepting_sinks();
715 record_view::private_data::sink_list::iterator
716 weak_it = weak_sinks.begin(),
717 weak_end = weak_sinks.end();
718 for (; weak_it != weak_end; ++weak_it)
719 {
720 shared_ptr< sinks::sink >& last = *end;
721 weak_it->lock().swap(other&: last);
722 if (last.get())
723 ++end;
724 }
725
726 bool shuffled = (end - begin) <= 1;
727 shared_ptr< sinks::sink >* it = begin;
728 while (true) try
729 {
730 // First try to distribute load between different sinks
731 bool all_locked = true;
732 while (it != end)
733 {
734 if (it->get()->try_consume(rec: rec_view))
735 {
736 --end;
737 end->swap(other&: *it);
738 all_locked = false;
739 }
740 else
741 ++it;
742 }
743
744 it = begin;
745 if (begin != end)
746 {
747 if (all_locked)
748 {
749 // If all sinks are busy then block on any
750 if (!shuffled)
751 {
752 implementation::thread_data* tsd = m_impl->get_thread_data();
753 log::aux::random_shuffle(begin, end, rng&: tsd->m_rng);
754 shuffled = true;
755 }
756
757 it->get()->consume(rec: rec_view);
758 --end;
759 end->swap(other&: *it);
760 }
761 }
762 else
763 break;
764 }
765#if !defined(BOOST_LOG_NO_THREADS)
766 catch (thread_interrupted&)
767 {
768 throw;
769 }
770#endif // !defined(BOOST_LOG_NO_THREADS)
771 catch (...)
772 {
773 // Lock the core to be safe against any attribute or sink set modifications
774 BOOST_LOG_EXPR_IF_MT(implementation::scoped_read_lock lock(m_impl->m_mutex);)
775 if (m_impl->m_exception_handler.empty())
776 throw;
777
778 m_impl->m_exception_handler();
779
780 // Skip the sink that failed to consume the record
781 --end;
782 end->swap(other&: *it);
783 }
784 }
785#if !defined(BOOST_LOG_NO_THREADS)
786 catch (thread_interrupted&)
787 {
788 throw;
789 }
790#endif // !defined(BOOST_LOG_NO_THREADS)
791 catch (...)
792 {
793 // Lock the core to be safe against any attribute or sink set modifications
794 BOOST_LOG_EXPR_IF_MT(implementation::scoped_read_lock lock(m_impl->m_mutex);)
795 if (m_impl->m_exception_handler.empty())
796 throw;
797
798 m_impl->m_exception_handler();
799 }
800}
801
802BOOST_LOG_CLOSE_NAMESPACE // namespace log
803
804} // namespace boost
805
806#include <boost/log/detail/footer.hpp>
807

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