• 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   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