blob: 32271785afe06071ef67a2b29945b55442dbe6bf [file] [log] [blame]
#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< 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 > 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