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