• 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   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/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 
54 namespace boost {
55 
56 BOOST_LOG_OPEN_NAMESPACE
57 
58 namespace aux {
59 
60 BOOST_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).
63 template< typename Iterator, typename RandomNumberGenerator >
64 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::swap(*where, *it);
73         ++it;
74     }
75 }
76 
77 } // namespace
78 
79 } // namespace aux
80 
81 //! Private record data information, with core-specific structures
82 struct 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 
92 private:
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 
100 private:
101     //! Initializing constructor
private_databoost::record_view::private_data102     private_data(BOOST_RV_REF(attribute_value_set) values, uint32_t capacity) BOOST_NOEXCEPT :
103         public_data(boost::move(values)),
104         m_accepting_sink_count(0),
105         m_accepting_sink_capacity(capacity),
106         m_detach_from_thread_needed(false)
107     {
108     }
109 
110 public:
111     //! Creates the object with the specified capacity
createboost::record_view::private_data112     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             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(values), capacity);
121         return p;
122     }
123 
124     //! Destroys the object and frees the underlying storage
destroyboost::record_view::private_data125     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             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
get_accepting_sinksboost::record_view::private_data146     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
push_back_accepting_sinkboost::record_view::private_data153     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
accepting_sink_countboost::record_view::private_data163     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
is_detach_from_thread_neededboost::record_view::private_data166     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 
171 private:
172     //! Returns a pointer to the first accepting sink
beginboost::record_view::private_data173     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
destroy(const public_data * p)185 BOOST_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.
lock()191 BOOST_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
214 struct core::implementation :
215     public log::aux::lazy_singleton<
216         implementation,
217         core_ptr
218     >
219 {
220 public:
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 
thread_databoost::core::implementation::thread_data245         thread_data() : m_rng(get_random_seed())
246         {
247         }
248 
249     private:
250         //! Creates a seed for RNG
get_random_seedboost::core::implementation::thread_data251         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 
261 public:
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 
300 public:
301     //! Constructor
implementationboost::core::implementation302     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 >
open_recordboost::core::implementation310     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(*it, rec_impl, values, remaining_capacity);
349                         }
350                     }
351                     else
352                     {
353                         // Use the default sink
354                         apply_sink_filter(m_default_sink, rec_impl, values, 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
get_thread_databoost::core::implementation406     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
init_instanceboost::core::implementation426     static void init_instance()
427     {
428         base_type::get_instance().reset(new core());
429     }
430 
431 private:
432     //! The method initializes thread-specific data
init_thread_databoost::core::implementation433     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(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
apply_sink_filterboost::core::implementation449     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(*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(boost::move(*attr_values), 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
483 BOOST_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
core()487 core::core() :
488     m_impl(new implementation())
489 {
490 }
491 
492 //! Logging system destructor
~core()493 core::~core()
494 {
495     delete m_impl;
496     m_impl = NULL;
497 }
498 
499 //! The method returns a pointer to the logging system instance
get()500 BOOST_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
set_logging_enabled(bool enabled)506 BOOST_LOG_API bool core::set_logging_enabled(bool enabled)
507 {
508 #if !defined(BOOST_LOG_NO_THREADS)
509     return m_impl->m_enabled.exchange(enabled, 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
get_logging_enabled() const518 BOOST_LOG_API bool core::get_logging_enabled() const
519 {
520 #if !defined(BOOST_LOG_NO_THREADS)
521     return m_impl->m_enabled.load(boost::memory_order_relaxed);
522 #else
523     return m_impl->m_enabled;
524 #endif
525 }
526 
527 //! The method adds a new sink
add_sink(shared_ptr<sinks::sink> const & s)528 BOOST_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(m_impl->m_sinks.begin(), m_impl->m_sinks.end(), s);
533     if (it == m_impl->m_sinks.end())
534         m_impl->m_sinks.push_back(s);
535 }
536 
537 //! The method removes the sink from the output
remove_sink(shared_ptr<sinks::sink> const & s)538 BOOST_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(m_impl->m_sinks.begin(), m_impl->m_sinks.end(), s);
543     if (it != m_impl->m_sinks.end())
544         m_impl->m_sinks.erase(it);
545 }
546 
547 //! The method removes all registered sinks from the output
remove_all_sinks()548 BOOST_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
556 BOOST_LOG_API std::pair< attribute_set::iterator, bool >
add_global_attribute(attribute_name const & name,attribute const & attr)557 core::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(name, attr);
561 }
562 
563 //! The method removes an attribute from the global attribute set
remove_global_attribute(attribute_set::iterator it)564 BOOST_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
get_global_attributes() const571 BOOST_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
set_global_attributes(attribute_set const & attrs)578 BOOST_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
585 BOOST_LOG_API std::pair< attribute_set::iterator, bool >
add_thread_attribute(attribute_name const & name,attribute const & attr)586 core::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(name, attr);
590 }
591 
592 //! The method removes an attribute from the thread-specific attribute set
remove_thread_attribute(attribute_set::iterator it)593 BOOST_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
get_thread_attributes() const600 BOOST_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
set_thread_attributes(attribute_set const & attrs)606 BOOST_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
set_filter(filter const & filter)613 BOOST_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
reset_filter()620 BOOST_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
set_exception_handler(exception_handler_type const & handler)627 BOOST_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.
flush()634 BOOST_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
open_record(attribute_set const & source_attributes)683 BOOST_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
open_record(attribute_value_set const & source_attributes)689 BOOST_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.
open_record_move(attribute_value_set & source_attributes)695 BOOST_LOG_API record core::open_record_move(attribute_value_set& source_attributes)
696 {
697     return m_impl->open_record(boost::move(source_attributes));
698 }
699 
700 //! The method pushes the record
push_record_move(record & rec)701 BOOST_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(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_view))
735                 {
736                     --end;
737                     end->swap(*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, tsd->m_rng);
754                         shuffled = true;
755                     }
756 
757                     it->get()->consume(rec_view);
758                     --end;
759                     end->swap(*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(*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 
802 BOOST_LOG_CLOSE_NAMESPACE // namespace log
803 
804 } // namespace boost
805 
806 #include <boost/log/detail/footer.hpp>
807