threadsafe_queue.hpp 7.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277
  1. /*
  2. * Copyright Andrey Semashev 2007 - 2013.
  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 threadsafe_queue.hpp
  9. * \author Andrey Semashev
  10. * \date 05.11.2010
  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. #ifndef BOOST_LOG_DETAIL_THREADSAFE_QUEUE_HPP_INCLUDED_
  16. #define BOOST_LOG_DETAIL_THREADSAFE_QUEUE_HPP_INCLUDED_
  17. #include <boost/log/detail/config.hpp>
  18. #ifdef BOOST_HAS_PRAGMA_ONCE
  19. #pragma once
  20. #endif
  21. #ifndef BOOST_LOG_NO_THREADS
  22. #include <new>
  23. #include <memory>
  24. #include <cstddef>
  25. #include <boost/aligned_storage.hpp>
  26. #include <boost/move/core.hpp>
  27. #include <boost/move/utility.hpp>
  28. #include <boost/type_traits/alignment_of.hpp>
  29. #include <boost/type_traits/type_with_alignment.hpp>
  30. #include <boost/log/detail/header.hpp>
  31. namespace boost {
  32. BOOST_LOG_OPEN_NAMESPACE
  33. namespace aux {
  34. //! Base class for the thread-safe queue implementation
  35. struct threadsafe_queue_impl
  36. {
  37. struct
  38. #if defined(__GNUC__)
  39. // Explicitly mark the type so that it may alias other types
  40. __attribute__ ((__may_alias__))
  41. #endif
  42. pointer_storage
  43. {
  44. union
  45. {
  46. void* data[2];
  47. type_with_alignment< 2 * sizeof(void*) >::type alignment;
  48. };
  49. };
  50. struct node_base
  51. {
  52. pointer_storage next;
  53. };
  54. static BOOST_LOG_API threadsafe_queue_impl* create(node_base* first_node);
  55. static BOOST_LOG_API void* operator new (std::size_t size);
  56. static BOOST_LOG_API void operator delete (void* p, std::size_t);
  57. virtual ~threadsafe_queue_impl() {}
  58. virtual node_base* reset_last_node() = 0;
  59. virtual bool unsafe_empty() = 0;
  60. virtual void push(node_base* p) = 0;
  61. virtual bool try_pop(node_base*& node_to_free, node_base*& node_with_value) = 0;
  62. };
  63. //! A helper class to compose some of the types used by the queue
  64. template< typename T, typename AllocatorT >
  65. struct threadsafe_queue_types
  66. {
  67. struct node :
  68. public threadsafe_queue_impl::node_base
  69. {
  70. typedef typename aligned_storage< sizeof(T), alignment_of< T >::value >::type storage_type;
  71. storage_type storage;
  72. node() {}
  73. explicit node(T const& val) { new (storage.address()) T(val); }
  74. T& value() { return *static_cast< T* >(storage.address()); }
  75. void destroy() { static_cast< T* >(storage.address())->~T(); }
  76. };
  77. typedef typename AllocatorT::BOOST_NESTED_TEMPLATE rebind< node >::other allocator_type;
  78. };
  79. /*!
  80. * \brief An unbounded thread-safe queue
  81. *
  82. * The implementation is based on algorithms published in the "Simple, Fast,
  83. * and Practical Non-Blocking and Blocking Concurrent Queue Algorithms" article
  84. * in PODC96 by Maged M. Michael and Michael L. Scott. Pseudocode is available here:
  85. * http://www.cs.rochester.edu/research/synchronization/pseudocode/queues.html
  86. *
  87. * The implementation provides thread-safe \c push and \c try_pop operations, as well as
  88. * a thread-unsafe \c empty operation. The queue imposes the following requirements
  89. * on the element type:
  90. *
  91. * \li Default constructible, the default constructor must not throw.
  92. * \li Copy constructible.
  93. * \li Movable (i.e. there should be an efficient move assignment for this type).
  94. *
  95. * The last requirement is not mandatory but is crucial for decent performance.
  96. */
  97. template< typename T, typename AllocatorT = std::allocator< void > >
  98. class threadsafe_queue :
  99. private threadsafe_queue_types< T, AllocatorT >::allocator_type
  100. {
  101. private:
  102. typedef typename threadsafe_queue_types< T, AllocatorT >::allocator_type base_type;
  103. typedef typename threadsafe_queue_types< T, AllocatorT >::node node;
  104. //! A simple scope guard to automate memory reclaiming
  105. struct auto_deallocate;
  106. friend struct auto_deallocate;
  107. struct auto_deallocate
  108. {
  109. auto_deallocate(base_type* alloc, node* dealloc, node* destr) :
  110. m_pAllocator(alloc),
  111. m_pDeallocate(dealloc),
  112. m_pDestroy(destr)
  113. {
  114. }
  115. ~auto_deallocate()
  116. {
  117. m_pAllocator->deallocate(m_pDeallocate, 1);
  118. m_pDestroy->destroy();
  119. }
  120. private:
  121. base_type* m_pAllocator;
  122. node* m_pDeallocate;
  123. node* m_pDestroy;
  124. };
  125. public:
  126. typedef T value_type;
  127. typedef T& reference;
  128. typedef T const& const_reference;
  129. typedef T* pointer;
  130. typedef T const* const_pointer;
  131. typedef std::ptrdiff_t difference_type;
  132. typedef std::size_t size_type;
  133. typedef AllocatorT allocator_type;
  134. public:
  135. /*!
  136. * Default constructor, creates an empty queue. Unlike most containers,
  137. * the constructor requires memory allocation.
  138. *
  139. * \throw std::bad_alloc if there is not sufficient memory
  140. */
  141. threadsafe_queue(base_type const& alloc = base_type()) :
  142. base_type(alloc)
  143. {
  144. node* p = base_type::allocate(1);
  145. if (p)
  146. {
  147. try
  148. {
  149. new (p) node();
  150. try
  151. {
  152. m_pImpl = threadsafe_queue_impl::create(p);
  153. }
  154. catch (...)
  155. {
  156. p->~node();
  157. throw;
  158. }
  159. }
  160. catch (...)
  161. {
  162. base_type::deallocate(p, 1);
  163. throw;
  164. }
  165. }
  166. else
  167. throw std::bad_alloc();
  168. }
  169. /*!
  170. * Destructor
  171. */
  172. ~threadsafe_queue()
  173. {
  174. // Clear the queue
  175. if (!unsafe_empty())
  176. {
  177. value_type value;
  178. while (try_pop(value));
  179. }
  180. // Remove the last dummy node
  181. node* p = static_cast< node* >(m_pImpl->reset_last_node());
  182. p->~node();
  183. base_type::deallocate(p, 1);
  184. delete m_pImpl;
  185. }
  186. /*!
  187. * Checks if the queue is empty. Not thread-safe, the returned result may not be actual.
  188. */
  189. bool unsafe_empty() const { return m_pImpl->unsafe_empty(); }
  190. /*!
  191. * Puts a new element to the end of the queue. Thread-safe, can be called
  192. * concurrently by several threads, and concurrently with the \c pop operation.
  193. */
  194. void push(const_reference value)
  195. {
  196. node* p = base_type::allocate(1);
  197. if (p)
  198. {
  199. try
  200. {
  201. new (p) node(value);
  202. }
  203. catch (...)
  204. {
  205. base_type::deallocate(p, 1);
  206. throw;
  207. }
  208. m_pImpl->push(p);
  209. }
  210. else
  211. throw std::bad_alloc();
  212. }
  213. /*!
  214. * Attempts to pop an element from the beginning of the queue. Thread-safe, can
  215. * be called concurrently with the \c push operation. Should not be called by
  216. * several threads concurrently.
  217. */
  218. bool try_pop(reference value)
  219. {
  220. threadsafe_queue_impl::node_base *dealloc, *destr;
  221. if (m_pImpl->try_pop(dealloc, destr))
  222. {
  223. register node* p = static_cast< node* >(destr);
  224. auto_deallocate guard(static_cast< base_type* >(this), static_cast< node* >(dealloc), p);
  225. value = boost::move(p->value());
  226. return true;
  227. }
  228. else
  229. return false;
  230. }
  231. // Copying and assignment is prohibited
  232. BOOST_DELETED_FUNCTION(threadsafe_queue(threadsafe_queue const&))
  233. BOOST_DELETED_FUNCTION(threadsafe_queue& operator= (threadsafe_queue const&))
  234. private:
  235. //! Pointer to the implementation
  236. threadsafe_queue_impl* m_pImpl;
  237. };
  238. } // namespace aux
  239. BOOST_LOG_CLOSE_NAMESPACE // namespace log
  240. } // namespace boost
  241. #include <boost/log/detail/footer.hpp>
  242. #endif // BOOST_LOG_NO_THREADS
  243. #endif // BOOST_LOG_DETAIL_THREADSAFE_QUEUE_HPP_INCLUDED_