1 #ifndef BOOST_STATECHART_FIFO_WORKER_HPP_INCLUDED
2 #define BOOST_STATECHART_FIFO_WORKER_HPP_INCLUDED
3 //////////////////////////////////////////////////////////////////////////////
4 // Copyright 2002-2008 Andreas Huber Doenni
5 // Distributed under the Boost Software License, Version 1.0. (See accompany-
6 // ing file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
7 //////////////////////////////////////////////////////////////////////////////
8 
9 
10 
11 #include <boost/assert.hpp>
12 #include <boost/noncopyable.hpp>
13 #include <boost/function/function0.hpp>
14 #include <boost/bind.hpp>
15 // BOOST_HAS_THREADS, BOOST_MSVC
16 #include <boost/config.hpp>
17 
18 #include <boost/detail/allocator_utilities.hpp>
19 
20 #ifdef BOOST_HAS_THREADS
21 #  ifdef BOOST_MSVC
22 #    pragma warning( push )
23      // "conditional expression is constant" in basic_timed_mutex.hpp
24 #    pragma warning( disable: 4127 )
25      // "conversion from 'int' to 'unsigned short'" in microsec_time_clock.hpp
26 #    pragma warning( disable: 4244 )
27      // "... needs to have dll-interface to be used by clients of class ..."
28 #    pragma warning( disable: 4251 )
29      // "... assignment operator could not be generated"
30 #    pragma warning( disable: 4512 )
31      // "Function call with parameters that may be unsafe" in
32      // condition_variable.hpp
33 #    pragma warning( disable: 4996 )
34 #  endif
35 
36 #  include <boost/thread/mutex.hpp>
37 #  include <boost/thread/condition.hpp>
38 
39 #  ifdef BOOST_MSVC
40 #    pragma warning( pop )
41 #  endif
42 #endif
43 
44 #include <list>
45 #include <memory>   // std::allocator
46 
47 
48 namespace boost
49 {
50 namespace statechart
51 {
52 
53 
54 
55 template< class Allocator = std::allocator< none > >
56 class fifo_worker : noncopyable
57 {
58   public:
59     //////////////////////////////////////////////////////////////////////////
60     #ifdef BOOST_HAS_THREADS
fifo_worker(bool waitOnEmptyQueue=false)61     fifo_worker( bool waitOnEmptyQueue = false ) :
62       waitOnEmptyQueue_( waitOnEmptyQueue ),
63     #else
64     fifo_worker() :
65     #endif
66       terminated_( false )
67     {
68     }
69 
70     typedef function0< void > work_item;
71 
72     // We take a non-const reference so that we can move (i.e. swap) the item
73     // into the queue, what avoids copying the (possibly heap-allocated)
74     // implementation object inside work_item.
queue_work_item(work_item & item)75     void queue_work_item( work_item & item )
76     {
77       if ( item.empty() )
78       {
79         return;
80       }
81 
82       #ifdef BOOST_HAS_THREADS
83       mutex::scoped_lock lock( mutex_ );
84       #endif
85 
86       workQueue_.push_back( work_item() );
87       workQueue_.back().swap( item );
88 
89       #ifdef BOOST_HAS_THREADS
90       queueNotEmpty_.notify_one();
91       #endif
92     }
93 
94     // Convenience overload so that temporary objects can be passed directly
95     // instead of having to create a work_item object first. Under most
96     // circumstances, this will lead to one unnecessary copy of the
97     // function implementation object.
queue_work_item(const work_item & item)98     void queue_work_item( const work_item & item )
99     {
100       work_item copy = item;
101       queue_work_item( copy );
102     }
103 
terminate()104     void terminate()
105     {
106       work_item item = boost::bind( &fifo_worker::terminate_impl, this );
107       queue_work_item( item );
108     }
109 
110     // Is not mutex-protected! Must only be called from the thread that also
111     // calls operator().
terminated() const112     bool terminated() const
113     {
114       return terminated_;
115     }
116 
operator ()(unsigned long maxItemCount=0)117     unsigned long operator()( unsigned long maxItemCount = 0 )
118     {
119       unsigned long itemCount = 0;
120 
121       while ( !terminated() &&
122         ( ( maxItemCount == 0 ) || ( itemCount < maxItemCount ) ) )
123       {
124         work_item item = dequeue_item();
125 
126         if ( item.empty() )
127         {
128           // item can only be empty when the queue is empty, which only
129           // happens in ST builds or when users pass false to the fifo_worker
130           // constructor
131           return itemCount;
132         }
133 
134         item();
135         ++itemCount;
136       }
137 
138       return itemCount;
139     }
140 
141   private:
142     //////////////////////////////////////////////////////////////////////////
dequeue_item()143     work_item dequeue_item()
144     {
145       #ifdef BOOST_HAS_THREADS
146       mutex::scoped_lock lock( mutex_ );
147 
148       if ( !waitOnEmptyQueue_ && workQueue_.empty() )
149       {
150         return work_item();
151       }
152 
153       while ( workQueue_.empty() )
154       {
155         queueNotEmpty_.wait( lock );
156       }
157       #else
158       // If the queue happens to run empty in a single-threaded system,
159       // waiting for new work items (which means to loop indefinitely!) is
160       // pointless as there is no way that new work items could find their way
161       // into the queue. The only sensible thing is to exit the loop and
162       // return to the caller in this case.
163       // Users can then queue new work items before calling operator() again.
164       if ( workQueue_.empty() )
165       {
166         return work_item();
167       }
168       #endif
169 
170       // Optimization: Swap rather than assign to avoid the copy of the
171       // implementation object inside function
172       work_item result;
173       result.swap( workQueue_.front() );
174       workQueue_.pop_front();
175       return result;
176     }
177 
terminate_impl()178     void terminate_impl()
179     {
180       terminated_ = true;
181     }
182 
183 
184     typedef std::list<
185       work_item,
186       typename boost::detail::allocator::rebind_to<
187         Allocator, work_item >::type
188     > work_queue_type;
189 
190     work_queue_type workQueue_;
191 
192     #ifdef BOOST_HAS_THREADS
193     mutex mutex_;
194     condition queueNotEmpty_;
195     const bool waitOnEmptyQueue_;
196     #endif
197 
198     bool terminated_;
199 };
200 
201 
202 
203 } // namespace statechart
204 } // namespace boost
205 
206 
207 
208 #endif
209