c++-gtk-utils
async_result.h
Go to the documentation of this file.
1 /* Copyright (C) 2012 and 2013 Chris Vine
2 
3 The library comprised in this file or of which this file is part is
4 distributed by Chris Vine under the GNU Lesser General Public
5 License as follows:
6 
7  This library is free software; you can redistribute it and/or
8  modify it under the terms of the GNU Lesser General Public License
9  as published by the Free Software Foundation; either version 2.1 of
10  the License, or (at your option) any later version.
11 
12  This library is distributed in the hope that it will be useful, but
13  WITHOUT ANY WARRANTY; without even the implied warranty of
14  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15  Lesser General Public License, version 2.1, for more details.
16 
17  You should have received a copy of the GNU Lesser General Public
18  License, version 2.1, along with this library (see the file LGPL.TXT
19  which came with this source code package in the c++-gtk-utils
20  sub-directory); if not, write to the Free Software Foundation, Inc.,
21  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
22 
23 However, it is not intended that the object code of a program whose
24 source code instantiates a template from this file or uses macros or
25 inline functions (of any length) should by reason only of that
26 instantiation or use be subject to the restrictions of use in the GNU
27 Lesser General Public License. With that in mind, the words "and
28 macros, inline functions and instantiations of templates (of any
29 length)" shall be treated as substituted for the words "and small
30 macros and small inline functions (ten lines or less in length)" in
31 the fourth paragraph of section 5 of that licence. This does not
32 affect any other reason why object code may be subject to the
33 restrictions in that licence (nor for the avoidance of doubt does it
34 affect the application of section 2 of that licence to modifications
35 of the source code in this file).
36 
37 */
38 
39 /**
40  * @file async_result.h
41  * @brief This file provides a thread-safe asynchronous result class.
42  */
43 
44 #ifndef CGU_ASYNC_RESULT_H
45 #define CGU_ASYNC_RESULT_H
46 
47 #include <c++-gtk-utils/mutex.h>
48 #include <c++-gtk-utils/thread.h>
50 
51 
52 namespace Cgu {
53 
54 /**
55  * @class AsyncResult async_result.h c++-gtk-utils/async_result.h
56  * @brief A thread-safe asynchronous result class.
57  * @sa AsyncQueueDispatch Thread::Future
58  *
59  * Cgu::Thread::Future operates on the principle of there being one
60  * worker thread per task. In some cases however, it may be better to
61  * have a worker thread, or a limited pool of worker threads,
62  * executing a larger number of tasks. This can be implemented by
63  * having a worker thread or threads waiting on a
64  * Cgu::AsyncQueueDispatch object, onto which other threads push tasks
65  * represented by Cgu::Callback::SafeFunctor objects.
66  *
67  * Where this model is adopted, when a task completes it may report
68  * its results by dispatching a further callback to a glib main loop
69  * using Cgu::Callback::post(). However, there will also be cases
70  * where, rather than passing a result as an event to a main loop, a
71  * thread is to to wait for the task to complete. This class is
72  * intended to facilitate that. It operates in a way which is similar
73  * to the std::promise class in C++11. The thread which wishes to
74  * extract a result can call the get() method, which will block until
75  * the worker thread has called the set() method or posted an error.
76  *
77  * Here is a compilable example of a calculator class which runs a
78  * dedicated thread on which it carries out all its calculations:
79  *
80  * @code
81  * #include <vector>
82  * #include <numeric>
83  * #include <ostream>
84  * #include <iostream>
85  *
86  * #include <c++-gtk-utils/async_result.h>
87  * #include <c++-gtk-utils/async_queue.h>
88  * #include <c++-gtk-utils/shared_ptr.h>
89  * #include <c++-gtk-utils/thread.h>
90  * #include <c++-gtk-utils/callback.h>
91  *
92  * using namespace Cgu;
93  *
94  * class Calcs {
95  * AsyncQueueDispatch<Callback::SafeFunctor> jobs;
96  * Thread::JoinableHandle t;
97  *
98  * void do_jobs() {
99  * for (;;) {
100  * Callback::SafeFunctor job;
101  * jobs.pop_dispatch(job);
102  * job();
103  * }
104  * }
105  *
106  * static void mean_impl(const std::vector<double>& nums,
107  * const SharedLockPtr<AsyncResult<double> >& res) {
108  * if (nums.empty()) res->set(0.0);
109  * else res->set(std::accumulate(nums.begin(), nums.end(), 0.0)/nums.size());
110  * }
111  *
112  * static void all_done() {throw Thread::Exit();}
113  *
114  * // ... other calculation implementation methods here
115  *
116  * public:
117  *
118  * SharedLockPtr<AsyncResult<double> > mean(const std::vector<double>& nums) {
119  * SharedLockPtr<AsyncResult<double> > res(new AsyncResult<double>);
120  * jobs.push(Callback::make_ref(&mean_impl, nums, res));
121  * return res;
122  * }
123  *
124  * // ... other calculation methods here
125  *
126  * Calcs() {
127  * Thread::JoinableHandle temp(Thread::Thread::start(Callback::make(*this, &Calcs::do_jobs), true),
128  * Thread::JoinableHandle::join_on_exit);
129  * if (!temp.is_managing()) throw "Thread start error";
130  * t = temp;
131  * }
132  * ~Calcs() {
133  * jobs.push(Callback::make(&all_done));
134  * t.join();
135  * }
136  * };
137  *
138  * int main () {
139  *
140  * Calcs calcs;
141  * const double ar1[] = {1, 2, 8, 0};
142  * const double ar2[] = {101, 53.7, 87, 1.2};
143  * SharedLockPtr<AsyncResult<double> > res1 = calcs.mean(std::vector<double>(ar1, ar1 + 4));
144  * SharedLockPtr<AsyncResult<double> > res2 = calcs.mean(std::vector<double>(ar2, ar2 + 4));
145  *
146  * // ... do something else
147  *
148  * std::cout << res1->get() << std::endl;
149  * std::cout << res2->get() << std::endl;
150  *
151  * }
152  * @endcode
153  *
154  * AsyncResult objects cannot be copied by value, and as they need to
155  * be visible both to the set()ing and get()ing threads, it will often
156  * be easiest to construct them on free store and copy them by smart
157  * pointer, as in the example above. However, if the library is
158  * compiled with the \--with-glib-memory-slices-compat or
159  * \--with-glib-memory-slices-no-compat configuration options, any
160  * AsyncResult object constructed on free store will be constructed in
161  * glib memory slices, which are an efficient small object allocator.
162  */
163 
164 template <class T> class AsyncResult {
165  T res;
166  bool done;
167  int error;
168  mutable Thread::Mutex mutex;
169  mutable Thread::Cond cond;
170 
171  // AsyncResult objects cannot be copied - they are mainly
172  // intended to pass a result between two known threads
173  AsyncResult(const AsyncResult&);
174  AsyncResult& operator=(const AsyncResult&);
175 
176 public:
177 /**
178  * @exception Thread::MutexError The constructor might throw this
179  * exception if initialisation of the contained mutex fails. (It is
180  * often not worth checking for this, as it means either memory is
181  * exhausted or pthread has run out of other resources to create new
182  * mutexes.) The constructor will also throw if the default
183  * constructor of the result type represented by this object throws.
184  * @exception Thread::CondError The constructor might throw this
185  * exception if initialisation of the contained condition variable
186  * fails. (It is often not worth checking for this, as it means
187  * either memory is exhausted or pthread has run out of other
188  * resources to create new condition variables.) The constructor will
189  * also throw if the default constructor of the result type
190  * represented by this object throws.
191  *
192  * Since 1.2.22
193  */
194  AsyncResult(): res(), done(false), error(0) {}
195 
197  // lock and unlock the mutex in the destructor so that we have an
198  // acquire operation to ensure that when this object is destroyed
199  // memory is synchronised, so any thread may destroy this object
200  Thread::Mutex::Lock lock(mutex);
201  }
202 
203  /**
204  * This method sets the value represented by the AsyncResult object,
205  * provided that set() has not previously been called and
206  * set_error() has not previously been called with a value other
207  * than 0. If set() has previously been called or set_error()
208  * called with a value other than 0 (so that is_done() will return
209  * true) this method does nothing. It is thread safe. It is not a
210  * cancellation point. It will not throw unless the assignment
211  * operator of the value type throws.
212  * @param val The value which this object is to represent and which
213  * calls to get() will return. Any threads waiting on get() will
214  * unblock, and any subsequent calls to is_done() will return true.
215  * @return true if the call to this method is effective because
216  * set() has not previously been called and set_error() has not
217  * previously been called with a value other than 0, otherwise
218  * false.
219  *
220  * Since 1.2.22
221  */
222  bool set(const T& val) {
223  Thread::Mutex::Lock lock(mutex);
224  if (!done) {
225  res = val;
226  done = true;
227  cond.broadcast();
228  return true;
229  }
230  return false;
231  }
232 
233  /**
234  * This method gets the stored value represented by the AsyncResult
235  * object. It is thread safe. It is a cancellation point if it
236  * blocks, and is cancellation safe if the stack unwinds on
237  * cancellation. Any number of threads may call this method and
238  * block on it. It will not throw unless the copy constructor of
239  * the return value type throws. It is strongly exception safe.
240  * @return the value represented by this object as set by a call to
241  * set(). If no such value has been set (and no error has been set)
242  * so that is_done() will return false, this method will block until
243  * either a value or an error has been set. If an error has been
244  * set, this method will return a default constructed object of the
245  * template type (and the error can be obtained with get_error()).
246  * @note Question: Couldn't this method return the stored value by
247  * reference to const? Answer: It could. However, because of
248  * return value optimization, which will be implemented by any
249  * compiler capable of compiling this library, no advantage would be
250  * gained by doing so when initializing a local variable with the
251  * return value of this method (the copy constructor will only be
252  * called once whether returning by value or const reference). The
253  * advantage of returning by value is that the call to the copy
254  * constructor is forced to be within the AsyncResult object's
255  * mutex, so different threads' calls to the copy constructor are
256  * serialized, and also with blocked cancellation, so this method is
257  * cancellation safe. All calls to this method by different threads
258  * are therefore isolated and we do not have to worry about the
259  * thread safety of direct access to the stored value via its const
260  * methods outside the mutex (which would not be thread safe if the
261  * stored value has data members declared mutable) nor about the
262  * cancellation safety of the copy constructor. Of course, for
263  * objects which do not have mutable data, a hit arises by returning
264  * by value in cases where it is not intended to initialize a local
265  * variable at all nor to cancel a thread: where, say, only const
266  * methods are to be called on the return value (which could be done
267  * directly if this method returned by const reference). However,
268  * that is a design trade-off.
269  *
270  * Since 1.2.22
271  */
272  T get() const {
273  Thread::Mutex::Lock lock(mutex);
274  while (!done) cond.wait(mutex);
276  return res;
277  }
278 
279  /**
280  * This method sets an error if called with a value other than 0,
281  * provided that set() has not previously been called and this
282  * method has not previously been called with a value other than 0.
283  * If set() has been called or this method previously called with a
284  * value other than 0 (so that is_done() will return true), this
285  * method does nothing. This method is thread safe. It is not a
286  * cancellation point. It will not throw.
287  * @param err The value which subsequent calls to get_error() will
288  * report. If the value of err is 0, or if this method has been
289  * called with a value other than 0 or set() has previously been
290  * called, this method will do nothing. Otherwise, any threads
291  * waiting on get() will unblock (get() will return a default
292  * constructed object of the template type), and any subsequent
293  * calls to is_done() will return true.
294  * @return true if the call to this method is effective because the
295  * error value passed is not 0, set() has not previously been called
296  * and this method has not previously been called with a value other
297  * than 0, otherwise false.
298  *
299  * Since 1.2.22
300  */
301  bool set_error(int err = -1) {
302  Thread::Mutex::Lock lock(mutex);
303  if (err && !done) {
304  error = err;
305  done = true;
306  cond.broadcast();
307  return true;
308  }
309  return false;
310  }
311 
312  /**
313  * This method is thread safe. It is not a cancellation point. It
314  * will not throw.
315  * @return the error value set by a call to set_error(), or 0 if no
316  * error has been set.
317  *
318  * Since 1.2.22
319  */
320  int get_error() const {
321  Thread::Mutex::Lock lock(mutex);
322  return error;
323  }
324 
325  /**
326  * This method is thread safe. It is not a cancellation point. It
327  * will not throw.
328  * @return true if set() has been called, or set_error() has been
329  * called with a value other than 0, otherwise false.
330  *
331  * Since 1.2.22
332  */
333  bool is_done() const {
334  Thread::Mutex::Lock lock(mutex);
335  return done;
336  }
337 
338 /* Only has effect if --with-glib-memory-slices-compat or
339  * --with-glib-memory-slices-no-compat option picked */
341 };
342 
343 } // namespace Cgu
344 
345 #endif
Cgu
Definition: application.h:45
Cgu::Thread::Cond::broadcast
int broadcast()
Definition: mutex.h:451
Cgu::AsyncResult::get
T get() const
Definition: async_result.h:272
Cgu::Thread::Cond
A wrapper class for pthread condition variables.
Definition: mutex.h:424
Cgu::AsyncResult::set_error
bool set_error(int err=-1)
Definition: async_result.h:301
Cgu::AsyncResult
A thread-safe asynchronous result class.
Definition: async_result.h:164
Cgu::Thread::Cond::wait
int wait(Mutex &mutex)
Definition: mutex.h:476
Cgu::AsyncResult::AsyncResult
AsyncResult()
Definition: async_result.h:194
Cgu::AsyncResult::set
bool set(const T &val)
Definition: async_result.h:222
Cgu::AsyncResult::get_error
int get_error() const
Definition: async_result.h:320
Cgu::AsyncResult::~AsyncResult
~AsyncResult()
Definition: async_result.h:196
Cgu::Thread::Mutex::Lock
A scoped locking class for exception safe Mutex locking.
Definition: mutex.h:192
CGU_GLIB_MEMORY_SLICES_FUNCS
#define CGU_GLIB_MEMORY_SLICES_FUNCS
Definition: cgu_config.h:84
Cgu::Thread::CancelBlock
A class enabling the cancellation state of a thread to be controlled.
Definition: thread.h:571
mutex.h
Provides wrapper classes for pthread mutexes and condition variables, and scoped locking classes for ...
Cgu::AsyncResult::is_done
bool is_done() const
Definition: async_result.h:333
thread.h
Cgu::Thread::Mutex
A wrapper class for pthread mutexes.
Definition: mutex.h:109
cgu_config.h