| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208 | #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::allocatornamespace boost{namespace statechart{template< class Allocator = std::allocator< none > >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
 |