threadsafe_queue.hpp 8.3 KB

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