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 bounded_fifo_queue.hpp 9 * \author Andrey Semashev 10 * \date 04.01.2012 11 * 12 * The header contains implementation of bounded FIFO queueing strategy for 13 * the asynchronous sink frontend. 14 */ 15 16 #ifndef BOOST_LOG_SINKS_BOUNDED_FIFO_QUEUE_HPP_INCLUDED_ 17 #define BOOST_LOG_SINKS_BOUNDED_FIFO_QUEUE_HPP_INCLUDED_ 18 19 #include <boost/log/detail/config.hpp> 20 21 #ifdef BOOST_HAS_PRAGMA_ONCE 22 #pragma once 23 #endif 24 25 #if defined(BOOST_LOG_NO_THREADS) 26 #error Boost.Log: This header content is only supported in multithreaded environment 27 #endif 28 29 #include <cstddef> 30 #include <queue> 31 #include <boost/thread/locks.hpp> 32 #include <boost/thread/mutex.hpp> 33 #include <boost/thread/condition_variable.hpp> 34 #include <boost/log/core/record_view.hpp> 35 #include <boost/log/detail/header.hpp> 36 37 namespace boost { 38 39 BOOST_LOG_OPEN_NAMESPACE 40 41 namespace sinks { 42 43 /*! 44 * \brief Bounded FIFO log record queueing strategy 45 * 46 * The \c bounded_fifo_queue class is intended to be used with 47 * the \c asynchronous_sink frontend as a log record queueing strategy. 48 * 49 * This strategy describes log record queueing logic. 50 * The queue has a limited capacity, upon reaching which the enqueue operation will 51 * invoke the overflow handling strategy specified in the \c OverflowStrategyT 52 * template parameter to handle the situation. The library provides overflow handling 53 * strategies for most common cases: \c drop_on_overflow will silently discard the log record, 54 * and \c block_on_overflow will put the enqueueing thread to wait until there is space 55 * in the queue. 56 * 57 * The log record queue imposes no ordering over the queued 58 * elements aside from the order in which they are enqueued. 59 */ 60 template< std::size_t MaxQueueSizeV, typename OverflowStrategyT > 61 class bounded_fifo_queue : 62 private OverflowStrategyT 63 { 64 private: 65 typedef OverflowStrategyT overflow_strategy; 66 typedef std::queue< record_view > queue_type; 67 typedef boost::mutex mutex_type; 68 69 private: 70 //! Synchronization primitive 71 mutex_type m_mutex; 72 //! Condition to block the consuming thread on 73 condition_variable m_cond; 74 //! Log record queue 75 queue_type m_queue; 76 //! Interruption flag 77 bool m_interruption_requested; 78 79 protected: 80 //! Default constructor bounded_fifo_queue()81 bounded_fifo_queue() : m_interruption_requested(false) 82 { 83 } 84 //! Initializing constructor 85 template< typename ArgsT > bounded_fifo_queue(ArgsT const &)86 explicit bounded_fifo_queue(ArgsT const&) : m_interruption_requested(false) 87 { 88 } 89 90 //! Enqueues log record to the queue enqueue(record_view const & rec)91 void enqueue(record_view const& rec) 92 { 93 unique_lock< mutex_type > lock(m_mutex); 94 std::size_t size = m_queue.size(); 95 for (; size >= MaxQueueSizeV; size = m_queue.size()) 96 { 97 if (!overflow_strategy::on_overflow(rec, lock)) 98 return; 99 } 100 101 m_queue.push(rec); 102 if (size == 0) 103 m_cond.notify_one(); 104 } 105 106 //! Attempts to enqueue log record to the queue try_enqueue(record_view const & rec)107 bool try_enqueue(record_view const& rec) 108 { 109 unique_lock< mutex_type > lock(m_mutex, try_to_lock); 110 if (lock.owns_lock()) 111 { 112 const std::size_t size = m_queue.size(); 113 114 // Do not invoke the bounding strategy in case of overflow as it may block 115 if (size < MaxQueueSizeV) 116 { 117 m_queue.push(rec); 118 if (size == 0) 119 m_cond.notify_one(); 120 return true; 121 } 122 } 123 124 return false; 125 } 126 127 //! Attempts to dequeue a log record ready for processing from the queue, does not block if the queue is empty try_dequeue_ready(record_view & rec)128 bool try_dequeue_ready(record_view& rec) 129 { 130 return try_dequeue(rec); 131 } 132 133 //! Attempts to dequeue log record from the queue, does not block if the queue is empty try_dequeue(record_view & rec)134 bool try_dequeue(record_view& rec) 135 { 136 lock_guard< mutex_type > lock(m_mutex); 137 const std::size_t size = m_queue.size(); 138 if (size > 0) 139 { 140 rec.swap(m_queue.front()); 141 m_queue.pop(); 142 overflow_strategy::on_queue_space_available(); 143 return true; 144 } 145 146 return false; 147 } 148 149 //! Dequeues log record from the queue, blocks if the queue is empty dequeue_ready(record_view & rec)150 bool dequeue_ready(record_view& rec) 151 { 152 unique_lock< mutex_type > lock(m_mutex); 153 154 while (!m_interruption_requested) 155 { 156 const std::size_t size = m_queue.size(); 157 if (size > 0) 158 { 159 rec.swap(m_queue.front()); 160 m_queue.pop(); 161 overflow_strategy::on_queue_space_available(); 162 return true; 163 } 164 else 165 { 166 m_cond.wait(lock); 167 } 168 } 169 m_interruption_requested = false; 170 171 return false; 172 } 173 174 //! Wakes a thread possibly blocked in the \c dequeue method interrupt_dequeue()175 void interrupt_dequeue() 176 { 177 lock_guard< mutex_type > lock(m_mutex); 178 m_interruption_requested = true; 179 overflow_strategy::interrupt(); 180 m_cond.notify_one(); 181 } 182 }; 183 184 } // namespace sinks 185 186 BOOST_LOG_CLOSE_NAMESPACE // namespace log 187 188 } // namespace boost 189 190 #include <boost/log/detail/footer.hpp> 191 192 #endif // BOOST_LOG_SINKS_BOUNDED_FIFO_QUEUE_HPP_INCLUDED_ 193