209 lines
5.5 KiB
C++
209 lines
5.5 KiB
C++
|
#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
|