123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208 |
- #ifndef BOOST_STATECHART_FIFO_WORKER_HPP_INCLUDED
- #define BOOST_STATECHART_FIFO_WORKER_HPP_INCLUDED
- //////////////////////////////////////////////////////////////////////////////
- // Copyright 2002-2008 Andreas Huber Doenni
- // Distributed under the Boost Software License, Version 1.0. (See accompany-
- // ing file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
- //////////////////////////////////////////////////////////////////////////////
- #include <boost/assert.hpp>
- #include <boost/noncopyable.hpp>
- #include <boost/function/function0.hpp>
- #include <boost/bind.hpp>
- // BOOST_HAS_THREADS, BOOST_MSVC
- #include <boost/config.hpp>
- #include <boost/detail/allocator_utilities.hpp>
- #ifdef BOOST_HAS_THREADS
- # ifdef BOOST_MSVC
- # pragma warning( push )
- // "conditional expression is constant" in basic_timed_mutex.hpp
- # pragma warning( disable: 4127 )
- // "conversion from 'int' to 'unsigned short'" in microsec_time_clock.hpp
- # pragma warning( disable: 4244 )
- // "... needs to have dll-interface to be used by clients of class ..."
- # pragma warning( disable: 4251 )
- // "... assignment operator could not be generated"
- # pragma warning( disable: 4512 )
- // "Function call with parameters that may be unsafe" in
- // condition_variable.hpp
- # pragma warning( disable: 4996 )
- # endif
- # include <boost/thread/mutex.hpp>
- # include <boost/thread/condition.hpp>
- # ifdef BOOST_MSVC
- # pragma warning( pop )
- # endif
- #endif
- #include <list>
- #include <memory> // std::allocator
- namespace boost
- {
- namespace statechart
- {
- template< class Allocator = std::allocator< none > >
- class fifo_worker : noncopyable
- {
- public:
- //////////////////////////////////////////////////////////////////////////
- #ifdef BOOST_HAS_THREADS
- fifo_worker( bool waitOnEmptyQueue = false ) :
- waitOnEmptyQueue_( waitOnEmptyQueue ),
- #else
- fifo_worker() :
- #endif
- terminated_( false )
- {
- }
- typedef function0< void > work_item;
- // We take a non-const reference so that we can move (i.e. swap) the item
- // into the queue, what avoids copying the (possibly heap-allocated)
- // implementation object inside work_item.
- void queue_work_item( work_item & item )
- {
- if ( item.empty() )
- {
- return;
- }
- #ifdef BOOST_HAS_THREADS
- mutex::scoped_lock lock( mutex_ );
- #endif
- workQueue_.push_back( work_item() );
- workQueue_.back().swap( item );
- #ifdef BOOST_HAS_THREADS
- queueNotEmpty_.notify_one();
- #endif
- }
- // Convenience overload so that temporary objects can be passed directly
- // instead of having to create a work_item object first. Under most
- // circumstances, this will lead to one unnecessary copy of the
- // function implementation object.
- void queue_work_item( const work_item & item )
- {
- work_item copy = item;
- queue_work_item( copy );
- }
- void terminate()
- {
- work_item item = boost::bind( &fifo_worker::terminate_impl, this );
- queue_work_item( item );
- }
- // Is not mutex-protected! Must only be called from the thread that also
- // calls operator().
- bool terminated() const
- {
- return terminated_;
- }
- unsigned long operator()( unsigned long maxItemCount = 0 )
- {
- unsigned long itemCount = 0;
- while ( !terminated() &&
- ( ( maxItemCount == 0 ) || ( itemCount < maxItemCount ) ) )
- {
- work_item item = dequeue_item();
- if ( item.empty() )
- {
- // item can only be empty when the queue is empty, which only
- // happens in ST builds or when users pass false to the fifo_worker
- // constructor
- return itemCount;
- }
- item();
- ++itemCount;
- }
- return itemCount;
- }
- private:
- //////////////////////////////////////////////////////////////////////////
- work_item dequeue_item()
- {
- #ifdef BOOST_HAS_THREADS
- mutex::scoped_lock lock( mutex_ );
- if ( !waitOnEmptyQueue_ && workQueue_.empty() )
- {
- return work_item();
- }
- while ( workQueue_.empty() )
- {
- queueNotEmpty_.wait( lock );
- }
- #else
- // If the queue happens to run empty in a single-threaded system,
- // waiting for new work items (which means to loop indefinitely!) is
- // pointless as there is no way that new work items could find their way
- // into the queue. The only sensible thing is to exit the loop and
- // return to the caller in this case.
- // Users can then queue new work items before calling operator() again.
- if ( workQueue_.empty() )
- {
- return work_item();
- }
- #endif
- // Optimization: Swap rather than assign to avoid the copy of the
- // implementation object inside function
- work_item result;
- result.swap( workQueue_.front() );
- workQueue_.pop_front();
- return result;
- }
- void terminate_impl()
- {
- terminated_ = true;
- }
- typedef std::list<
- work_item,
- typename boost::detail::allocator::rebind_to<
- Allocator, work_item >::type
- > work_queue_type;
- work_queue_type workQueue_;
- #ifdef BOOST_HAS_THREADS
- mutex mutex_;
- condition queueNotEmpty_;
- const bool waitOnEmptyQueue_;
- #endif
- bool terminated_;
- };
- } // namespace statechart
- } // namespace boost
- #endif
|