fifo_worker.hpp 5.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208
  1. #ifndef BOOST_STATECHART_FIFO_WORKER_HPP_INCLUDED
  2. #define BOOST_STATECHART_FIFO_WORKER_HPP_INCLUDED
  3. //////////////////////////////////////////////////////////////////////////////
  4. // Copyright 2002-2008 Andreas Huber Doenni
  5. // Distributed under the Boost Software License, Version 1.0. (See accompany-
  6. // ing file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
  7. //////////////////////////////////////////////////////////////////////////////
  8. #include <boost/assert.hpp>
  9. #include <boost/noncopyable.hpp>
  10. #include <boost/function/function0.hpp>
  11. #include <boost/bind.hpp>
  12. // BOOST_HAS_THREADS, BOOST_MSVC
  13. #include <boost/config.hpp>
  14. #include <boost/detail/allocator_utilities.hpp>
  15. #ifdef BOOST_HAS_THREADS
  16. # ifdef BOOST_MSVC
  17. # pragma warning( push )
  18. // "conditional expression is constant" in basic_timed_mutex.hpp
  19. # pragma warning( disable: 4127 )
  20. // "conversion from 'int' to 'unsigned short'" in microsec_time_clock.hpp
  21. # pragma warning( disable: 4244 )
  22. // "... needs to have dll-interface to be used by clients of class ..."
  23. # pragma warning( disable: 4251 )
  24. // "... assignment operator could not be generated"
  25. # pragma warning( disable: 4512 )
  26. // "Function call with parameters that may be unsafe" in
  27. // condition_variable.hpp
  28. # pragma warning( disable: 4996 )
  29. # endif
  30. # include <boost/thread/mutex.hpp>
  31. # include <boost/thread/condition.hpp>
  32. # ifdef BOOST_MSVC
  33. # pragma warning( pop )
  34. # endif
  35. #endif
  36. #include <list>
  37. #include <memory> // std::allocator
  38. namespace boost
  39. {
  40. namespace statechart
  41. {
  42. template< class Allocator = std::allocator< none > >
  43. class fifo_worker : noncopyable
  44. {
  45. public:
  46. //////////////////////////////////////////////////////////////////////////
  47. #ifdef BOOST_HAS_THREADS
  48. fifo_worker( bool waitOnEmptyQueue = false ) :
  49. waitOnEmptyQueue_( waitOnEmptyQueue ),
  50. #else
  51. fifo_worker() :
  52. #endif
  53. terminated_( false )
  54. {
  55. }
  56. typedef function0< void > work_item;
  57. // We take a non-const reference so that we can move (i.e. swap) the item
  58. // into the queue, what avoids copying the (possibly heap-allocated)
  59. // implementation object inside work_item.
  60. void queue_work_item( work_item & item )
  61. {
  62. if ( item.empty() )
  63. {
  64. return;
  65. }
  66. #ifdef BOOST_HAS_THREADS
  67. mutex::scoped_lock lock( mutex_ );
  68. #endif
  69. workQueue_.push_back( work_item() );
  70. workQueue_.back().swap( item );
  71. #ifdef BOOST_HAS_THREADS
  72. queueNotEmpty_.notify_one();
  73. #endif
  74. }
  75. // Convenience overload so that temporary objects can be passed directly
  76. // instead of having to create a work_item object first. Under most
  77. // circumstances, this will lead to one unnecessary copy of the
  78. // function implementation object.
  79. void queue_work_item( const work_item & item )
  80. {
  81. work_item copy = item;
  82. queue_work_item( copy );
  83. }
  84. void terminate()
  85. {
  86. work_item item = boost::bind( &fifo_worker::terminate_impl, this );
  87. queue_work_item( item );
  88. }
  89. // Is not mutex-protected! Must only be called from the thread that also
  90. // calls operator().
  91. bool terminated() const
  92. {
  93. return terminated_;
  94. }
  95. unsigned long operator()( unsigned long maxItemCount = 0 )
  96. {
  97. unsigned long itemCount = 0;
  98. while ( !terminated() &&
  99. ( ( maxItemCount == 0 ) || ( itemCount < maxItemCount ) ) )
  100. {
  101. work_item item = dequeue_item();
  102. if ( item.empty() )
  103. {
  104. // item can only be empty when the queue is empty, which only
  105. // happens in ST builds or when users pass false to the fifo_worker
  106. // constructor
  107. return itemCount;
  108. }
  109. item();
  110. ++itemCount;
  111. }
  112. return itemCount;
  113. }
  114. private:
  115. //////////////////////////////////////////////////////////////////////////
  116. work_item dequeue_item()
  117. {
  118. #ifdef BOOST_HAS_THREADS
  119. mutex::scoped_lock lock( mutex_ );
  120. if ( !waitOnEmptyQueue_ && workQueue_.empty() )
  121. {
  122. return work_item();
  123. }
  124. while ( workQueue_.empty() )
  125. {
  126. queueNotEmpty_.wait( lock );
  127. }
  128. #else
  129. // If the queue happens to run empty in a single-threaded system,
  130. // waiting for new work items (which means to loop indefinitely!) is
  131. // pointless as there is no way that new work items could find their way
  132. // into the queue. The only sensible thing is to exit the loop and
  133. // return to the caller in this case.
  134. // Users can then queue new work items before calling operator() again.
  135. if ( workQueue_.empty() )
  136. {
  137. return work_item();
  138. }
  139. #endif
  140. // Optimization: Swap rather than assign to avoid the copy of the
  141. // implementation object inside function
  142. work_item result;
  143. result.swap( workQueue_.front() );
  144. workQueue_.pop_front();
  145. return result;
  146. }
  147. void terminate_impl()
  148. {
  149. terminated_ = true;
  150. }
  151. typedef std::list<
  152. work_item,
  153. typename boost::detail::allocator::rebind_to<
  154. Allocator, work_item >::type
  155. > work_queue_type;
  156. work_queue_type workQueue_;
  157. #ifdef BOOST_HAS_THREADS
  158. mutex mutex_;
  159. condition queueNotEmpty_;
  160. const bool waitOnEmptyQueue_;
  161. #endif
  162. bool terminated_;
  163. };
  164. } // namespace statechart
  165. } // namespace boost
  166. #endif