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