You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and dots ('.'), can be up to 35 characters long. Letters must be lowercase.
208 lines
5.3 KiB
208 lines
5.3 KiB
#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
|
|
|