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 <utility> // for std::move
48 
49 #include <c++-gtk-utils/mutex.h>
50 #include <c++-gtk-utils/thread.h>
52 
53 
54 namespace Cgu {
55 
56 /**
57  * @class AsyncResult async_result.h c++-gtk-utils/async_result.h
58  * @brief A thread-safe asynchronous result class.
59  * @sa AsyncQueueDispatch Thread::Future
60  *
61  * Cgu::Thread::Future operates on the principle of there being one
62  * worker thread per task. In some cases however, it may be better to
63  * have a worker thread, or a limited pool of worker threads,
64  * executing a larger number of tasks. This can be implemented by
65  * having a worker thread or threads waiting on a
66  * Cgu::AsyncQueueDispatch object, onto which other threads push tasks
67  * represented by std::unique_ptr<const Cgu::Callback::Callback> or
68  * Cgu::Callback::SafeFunctor objects.
69  *
70  * Where this model is adopted, when a task completes it may report
71  * its results by dispatching a further callback to a glib main loop
72  * using Cgu::Callback::post(). However, there will also be cases
73  * where, rather than passing a result as an event to a main loop, a
74  * thread is to to wait for the task to complete. This class is
75  * intended to facilitate that. It operates in a way which is similar
76  * to the std::promise class in C++11. The thread which wishes to
77  * extract a result can call the get() method, which will block until
78  * the worker thread has called the set() method or posted an error.
79  *
80  * For safety reasons, the get() method returns by value and so will
81  * cause that value to be copied once. From version 2.0.11 a
82  * move_get() method is provided which will make a move operation
83  * instead of a copy if the value type implements a move constructor,
84  * but see the documentation on move_get() for the caveats with
85  * respect to its use: in particular, if move_get() is to be called by
86  * a thread, then get() may not be called by another thread.
87  *
88  * Here is a compilable example of a calculator class which runs a
89  * dedicated thread on which it carries out all its calculations:
90  *
91  * @code
92  * #include <vector>
93  * #include <numeric>
94  * #include <memory>
95  * #include <ostream>
96  * #include <iostream>
97  *
98  * #include <c++-gtk-utils/async_result.h>
99  * #include <c++-gtk-utils/async_queue.h>
100  * #include <c++-gtk-utils/shared_ptr.h>
101  * #include <c++-gtk-utils/thread.h>
102  * #include <c++-gtk-utils/callback.h>
103  *
104  * using namespace Cgu;
105  *
106  * class Calcs {
107  * AsyncQueueDispatch<std::unique_ptr<const Callback::Callback>> jobs;
108  * Thread::JoinableHandle t;
109  *
110  * void do_jobs() {
111  * for (;;) {
112  * std::unique_ptr<const Callback::Callback> job;
113  * jobs.move_pop_dispatch(job);
114  * job->dispatch();
115  * }
116  * }
117  * public:
118  *
119  * SharedLockPtr<AsyncResult<double>> mean(const std::vector<double>& nums) {
120  * SharedLockPtr<AsyncResult<double>> res(new AsyncResult<double>);
121  * jobs.emplace(Callback::lambda<>([=]() {
122  * if (nums.empty()) res->set(0.0);
123  * else res->set(std::accumulate(nums.begin(), nums.end(), 0.0)/nums.size());
124  * }));
125  * return res;
126  * }
127  *
128  * // ... other calculation methods here
129  *
130  * Calcs() {
131  * t = Thread::JoinableHandle(Thread::Thread::start(Callback::make(*this, &Calcs::do_jobs), true),
132  * Thread::JoinableHandle::join_on_exit);
133  * if (!t.is_managing()) throw "Thread start error";
134  * }
135  * ~Calcs() {
136  * jobs.emplace(Callback::lambda<>([]() {throw Thread::Exit();}));
137  * t.join();
138  * }
139  * };
140  *
141  * int main () {
142  *
143  * Calcs calcs;
144  * auto res1 = calcs.mean({1, 2, 8, 0});
145  * auto res2 = calcs.mean({101, 53.7, 87, 1.2});
146  *
147  * // ... do something else
148  *
149  * std::cout << res1->get() << std::endl;
150  * std::cout << res2->get() << std::endl;
151  *
152  * }
153  * @endcode
154  *
155  * AsyncResult objects cannot be copied by value, and as they need to
156  * be visible both to the set()ing and get()ing threads, it will often
157  * be easiest to construct them on free store and copy them by smart
158  * pointer, as in the example above. However, if the library is
159  * compiled with the \--with-glib-memory-slices-compat or
160  * \--with-glib-memory-slices-no-compat configuration options, any
161  * AsyncResult object constructed on free store will be constructed in
162  * glib memory slices, which are an efficient small object allocator.
163  */
164 
165 template <class T> class AsyncResult {
166  T res;
167  bool done;
168  int error;
169  mutable Thread::Mutex mutex;
170  mutable Thread::Cond cond;
171 
172 public:
173 /**
174  * @exception Thread::MutexError The constructor might throw this
175  * exception if initialisation of the contained mutex fails. (It is
176  * often not worth checking for this, as it means either memory is
177  * exhausted or pthread has run out of other resources to create new
178  * mutexes.) The constructor will also throw if the default
179  * constructor of the result type represented by this object throws.
180  * @exception Thread::CondError The constructor might throw this
181  * exception if initialisation of the contained condition variable
182  * fails. (It is often not worth checking for this, as it means
183  * either memory is exhausted or pthread has run out of other
184  * resources to create new condition variables.) The constructor will
185  * also throw if the default constructor of the result type
186  * represented by this object throws.
187  *
188  * Since 2.0.8
189  */
190  AsyncResult(): res(), done(false), error(0) {}
191 
193  // lock and unlock the mutex in the destructor so that we have an
194  // acquire operation to ensure that when this object is destroyed
195  // memory is synchronised, so any thread may destroy this object
196  Thread::Mutex::Lock lock{mutex};
197  }
198 
199  // AsyncResult objects cannot be copied - they are mainly
200  // intended to pass a result between two known threads
201  /**
202  * This class cannot be copied. The copy constructor is deleted.
203  *
204  * Since 2.0.8
205  */
206  AsyncResult(const AsyncResult&) = delete;
207 
208  /**
209  * This class cannot be copied. The assignment operator is deleted.
210  *
211  * Since 2.0.8
212  */
213  AsyncResult& operator=(const AsyncResult&) = delete;
214 
215  /**
216  * This method sets the value represented by the AsyncResult object,
217  * provided that set() has not previously been called and
218  * set_error() has not previously been called with a value other
219  * than 0. If set() has previously been called or set_error()
220  * called with a value other than 0 (so that is_done() will return
221  * true) this method does nothing. It is thread safe. It is not a
222  * cancellation point. It will not throw unless the copy assignment
223  * operator of the value type throws.
224  * @param val The value which this object is to represent and which
225  * calls to get() or a call to move_get() will return. Any thread
226  * waiting on get() or move_get() will unblock, and any subsequent
227  * calls to is_done() will return true.
228  * @return true if the call to this method is effective because
229  * set() has not previously been called and set_error() has not
230  * previously been called with a value other than 0, otherwise
231  * false.
232  *
233  * Since 2.0.8
234  */
235  bool set(const T& val) {
236  Thread::Mutex::Lock lock{mutex};
237  if (!done) {
238  res = val;
239  done = true;
240  cond.broadcast();
241  return true;
242  }
243  return false;
244  }
245 
246  /**
247  * This method sets the value represented by the AsyncResult object,
248  * provided that set() has not previously been called and
249  * set_error() has not previously been called with a value other
250  * than 0. If set() has previously been called or set_error()
251  * called with a value other than 0 (so that is_done() will return
252  * true) this method does nothing. It is thread safe. It is not a
253  * cancellation point. It will not throw unless the copy or move
254  * assignment operator of the value type throws.
255  * @param val The value which this object is to represent and which
256  * calls to get() or a call to move_get() will return. Any thread
257  * waiting on get() or move_get() will unblock, and any subsequent
258  * calls to is_done() will return true.
259  * @return true if the call to this method is effective because
260  * set() has not previously been called and set_error() has not
261  * previously been called with a value other than 0, otherwise
262  * false.
263  *
264  * Since 2.0.8
265  */
266  bool set(T&& val) {
267  Thread::Mutex::Lock lock{mutex};
268  if (!done) {
269  res = std::move(val);
270  done = true;
271  cond.broadcast();
272  return true;
273  }
274  return false;
275  }
276 
277  /**
278  * This method gets the stored value represented by the AsyncResult
279  * object. It is thread safe. It is a cancellation point if it
280  * blocks, and is cancellation safe if the stack unwinds on
281  * cancellation. Any number of threads may call this method and
282  * block on it. It will not throw unless the copy constructor of
283  * the return type throws. It is strongly exception safe.
284  * @return the value represented by this object as set by a call to
285  * set(). If no such value has been set (and no error has been set)
286  * so that is_done() will return false, this method will block until
287  * either a value or an error has been set. If an error has been
288  * set, this method will return a default constructed object of the
289  * template type (and the error can be obtained with get_error()).
290  * @note 1. This method calls Thread::Cond::wait(). Between
291  * versions 2.2.3 and 2.2.13 inclusive, Thread::Cond::wait() was
292  * marked 'noexcept'. This was a mistake because it prevented a
293  * thread being cancelled while in a wait, including in this method
294  * (the cancellation pseudo-exception conflicted with the noexcept
295  * specifier). This was fixed in version 2.2.14.
296  * @note 2. Question: Couldn't this method return the stored value
297  * by lvalue reference to const? Answer: It could. However,
298  * because of return value optimization, which will be implemented
299  * by any compiler capable of compiling this library, no advantage
300  * would be gained by doing so when initializing a local variable
301  * with the return value of this method (the copy constructor will
302  * only be called once whether returning by value or const
303  * reference). The advantage of returning by value is that the call
304  * to the copy constructor is forced to be within the AsyncResult
305  * object's mutex, so different threads' calls to the copy
306  * constructor are serialized, and also with blocked cancellation,
307  * so this method is cancellation safe. All calls to this method by
308  * different threads are therefore isolated and we do not have to
309  * worry about the thread safety of direct access to the stored
310  * value via its const methods outside the mutex (which would not be
311  * thread safe if the stored value has data members declared
312  * mutable) nor about the cancellation safety of the copy
313  * constructor. Of course, for objects which do not have mutable
314  * data, a hit arises by returning by value in cases where it is not
315  * intended to initialize a local variable at all nor to cancel a
316  * thread: where, say, only const methods are to be called on the
317  * return value (which could be done directly if this method
318  * returned by const reference). However, in many use cases this
319  * will be mitigated by the move_get() method.
320  *
321  * Since 2.0.8
322  */
323  T get() const {
324  Thread::Mutex::Lock lock{mutex};
325  while (!done) cond.wait(mutex);
327  return res;
328  }
329 
330  /**
331  * This method gets the stored value represented by the AsyncResult
332  * object by a move operation, if the type of that value implements
333  * a move constructor (otherwise this method does the same as the
334  * get() method). It is provided as an option for cases where a
335  * move is required for efficiency reasons, but although it may be
336  * called by any thread, a move operation may normally only be made
337  * once (except where the return type has been designed to be moved
338  * more than once for the limited purpose of inspecting a flag
339  * indicating whether its value is valid or not). If this method is
340  * to be called then no calls to get() by another thread should
341  * normally be made. This method is a cancellation point if it
342  * blocks, and is cancellation safe if the stack unwinds on
343  * cancellation. It will not throw unless the copy or move
344  * constructor of the return type throws. It is only exception safe
345  * if the return type's move constructor is exception safe.
346  * @return The value represented by this object as set by a call to
347  * set(). If no such value has been set (and no error has been set)
348  * so that is_done() will return false, this method will block until
349  * either a value or an error has been set. If an error has been
350  * set, until a move operation has been carried out this method will
351  * return a default constructed object of the template type (and the
352  * error can be obtained with get_error()).
353  * @note 1. This method calls Thread::Cond::wait(). Between
354  * versions 2.2.3 and 2.2.13 inclusive, Thread::Cond::wait() was
355  * marked 'noexcept'. This was a mistake because it prevented a
356  * thread being cancelled while in a wait, including in this method
357  * (the cancellation pseudo-exception conflicted with the noexcept
358  * specifier). This was fixed in version 2.2.14.
359  * @note 2. Question: Couldn't this method return the stored value
360  * by rvalue reference? Answer: It could. However, because of
361  * return value optimization, which will be implemented by any
362  * compiler capable of compiling this library, no advantage would be
363  * gained by doing so when initializing a local variable with the
364  * return value of this method (the move constructor will only be
365  * called once, and no call will be made to the copy constructor,
366  * whether returning by value or rvalue reference). The advantage
367  * of returning by value is that the call to the move constructor is
368  * forced to be within the AsyncResult object's mutex, so different
369  * threads' calls to the move constructor are serialized, and also
370  * with blocked cancellation, so this method is cancellation safe.
371  * All calls to this method by different threads are therefore
372  * isolated and we do not have to worry about the thread safety of
373  * the mutating first call to this method, nor about direct access
374  * to the stored value via a rvalue reference outside the mutex nor
375  * the cancellation safety of the move constructor.
376  *
377  * Since 2.0.11
378  */
379  T move_get() {
380  Thread::Mutex::Lock lock{mutex};
381  while (!done) cond.wait(mutex);
383  return std::move(res);
384  }
385 
386  /**
387  * This method sets an error if called with a value other than 0,
388  * provided that set() has not previously been called and this
389  * method has not previously been called with a value other than 0.
390  * If set() has been called or this method previously called with a
391  * value other than 0 (so that is_done() will return true), this
392  * method does nothing. This method is thread safe. It is not a
393  * cancellation point. It will not throw.
394  * @param err The value which subsequent calls to get_error() will
395  * report. If the value of err is 0, or if this method has been
396  * called with a value other than 0 or set() has previously been
397  * called, this method will do nothing. Otherwise, any thread
398  * waiting on get() or move_get() will unblock (they will return a
399  * default constructed object of the template type), and any
400  * subsequent calls to is_done() will return true.
401  * @return true if the call to this method is effective because the
402  * error value passed is not 0, set() has not previously been called
403  * and this method has not previously been called with a value other
404  * than 0, otherwise false.
405  *
406  * Since 2.0.8
407  */
408  bool set_error(int err = -1) {
409  Thread::Mutex::Lock lock{mutex};
410  if (err && !done) {
411  error = err;
412  done = true;
413  cond.broadcast();
414  return true;
415  }
416  return false;
417  }
418 
419  /**
420  * This method is thread safe. It is not a cancellation point. It
421  * will not throw.
422  * @return the error value set by a call to set_error(), or 0 if no
423  * error has been set.
424  *
425  * Since 2.0.8
426  */
427  int get_error() const {
428  Thread::Mutex::Lock lock{mutex};
429  return error;
430  }
431 
432  /**
433  * This method is thread safe. It is not a cancellation point. It
434  * will not throw.
435  * @return true if set() has been called, or set_error() has been
436  * called with a value other than 0, otherwise false.
437  *
438  * Since 2.0.8
439  */
440  bool is_done() const {
441  Thread::Mutex::Lock lock{mutex};
442  return done;
443  }
444 
445 /* Only has effect if --with-glib-memory-slices-compat or
446  * --with-glib-memory-slices-no-compat option picked */
448 };
449 
450 } // namespace Cgu
451 
452 #endif
Cgu::AsyncResult::AsyncResult
AsyncResult(const AsyncResult &)=delete
Cgu
Definition: application.h:44
Cgu::AsyncResult::get
T get() const
Definition: async_result.h:323
Cgu::Thread::Cond::broadcast
int broadcast() noexcept
Definition: mutex.h:483
Cgu::Thread::Cond
A wrapper class for pthread condition variables.
Definition: mutex.h:449
Cgu::AsyncResult::set_error
bool set_error(int err=-1)
Definition: async_result.h:408
Cgu::AsyncResult
A thread-safe asynchronous result class.
Definition: async_result.h:165
Cgu::Thread::Cond::wait
int wait(Mutex &mutex)
Definition: mutex.h:513
Cgu::AsyncResult::set
bool set(T &&val)
Definition: async_result.h:266
Cgu::AsyncResult::AsyncResult
AsyncResult()
Definition: async_result.h:190
Cgu::AsyncResult::operator=
AsyncResult & operator=(const AsyncResult &)=delete
Cgu::AsyncResult::set
bool set(const T &val)
Definition: async_result.h:235
Cgu::AsyncResult::get_error
int get_error() const
Definition: async_result.h:427
Cgu::AsyncResult::~AsyncResult
~AsyncResult()
Definition: async_result.h:192
Cgu::Thread::Mutex::Lock
A scoped locking class for exception safe Mutex locking.
Definition: mutex.h:207
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:723
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:440
thread.h
Cgu::Thread::Mutex
A wrapper class for pthread mutexes.
Definition: mutex.h:117
cgu_config.h
Cgu::AsyncResult::move_get
T move_get()
Definition: async_result.h:379