#ifndef BOOST_STATECHART_FIFO_WORKER_HPP_INCLUDED #define BOOST_STATECHART_FIFO_WORKER_HPP_INCLUDED ////////////////////////////////////////////////////////////////////////////// // Copyright 2002-2006 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 #include #include #include // BOOST_HAS_THREADS, BOOST_MSVC #include #include #ifdef BOOST_HAS_THREADS # ifdef BOOST_MSVC # pragma warning( push ) # pragma warning( disable: 4275 ) // non-dll class used as base for dll class # endif # include # include # ifdef BOOST_MSVC # pragma warning( pop ) # endif #endif #include #include // std::allocator namespace boost { namespace statechart { template< class Allocator = std::allocator< void > > 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, Allocator > 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 = 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