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