c++-gtk-utils
Main Page
Related Pages
Modules
Namespaces
Classes
Files
File List
File Members
c++-gtk-utils
async_result.h
Go to the documentation of this file.
1
/* Copyright (C) 2012 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
>
51
#include <
c++-gtk-utils/cgu_config.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 Cgu::Callback::SafeFunctor objects.
68
*
69
* Where this model is adopted, when a task completes it may report
70
* its results by dispatching a further callback to a glib main loop
71
* using Cgu::Callback::post(). However, there will also be cases
72
* where, rather than passing a result as an event to a main loop, a
73
* thread is to to wait for the task to complete. This class is
74
* intended to facilitate that. It operates in a way which is similar
75
* to the std::promise class in C++11. The thread which wishes to
76
* extract a result can call the get() method, which will block until
77
* the worker thread has called the set() method or posted an error.
78
*
79
* For safety reasons, the get() method returns by value and so will
80
* cause that value to be copied once. From version 2.0.11 a
81
* move_get() method is provided which will attempt a move operation
82
* instead of a copy, but see the documentation on move_get() for the
83
* caveats with respect to its use: in particular, if move_get() is to
84
* be called by a thread, then get() may not be called by another
85
* thread.
86
*
87
* Here is a compilable example of a calculator class which runs a
88
* dedicated thread on which it carries out all its calculations:
89
*
90
* @code
91
* #include <vector>
92
* #include <numeric>
93
* #include <ostream>
94
* #include <iostream>
95
*
96
* #include <c++-gtk-utils/async_result.h>
97
* #include <c++-gtk-utils/async_queue.h>
98
* #include <c++-gtk-utils/shared_ptr.h>
99
* #include <c++-gtk-utils/thread.h>
100
* #include <c++-gtk-utils/callback.h>
101
*
102
* using namespace Cgu;
103
*
104
* class Calcs {
105
* AsyncQueueDispatch<Callback::SafeFunctor> jobs;
106
* Thread::JoinableHandle t;
107
*
108
* void do_jobs() {
109
* for (;;) {
110
* Callback::SafeFunctor job;
111
* jobs.move_pop_dispatch(job);
112
* job();
113
* }
114
* }
115
* public:
116
*
117
* SharedLockPtr<AsyncResult<double>> mean(const std::vector<double>& nums) {
118
* SharedLockPtr<AsyncResult<double>> res(new AsyncResult<double>);
119
* jobs.emplace(Callback::lambda<>([=]() {
120
* if (nums.empty()) res->set(0.0);
121
* else res->set(std::accumulate(nums.begin(), nums.end(), 0.0)/nums.size());
122
* return;
123
* }));
124
* return res;
125
* }
126
*
127
* // ... other calculation methods here
128
*
129
* Calcs() {
130
* t = Thread::JoinableHandle(Thread::Thread::start(Callback::make(*this, &Calcs::do_jobs), true),
131
* Thread::JoinableHandle::join_on_exit);
132
* if (!t.is_managing()) throw "Thread start error";
133
* }
134
* ~Calcs() {
135
* jobs.emplace(Callback::lambda<>([]() {throw Thread::Exit();}));
136
* t.join();
137
* }
138
* };
139
*
140
* int main () {
141
*
142
* Calcs calcs;
143
* auto res1 = calcs.mean(std::vector<double>({1, 2, 8, 0}));
144
* auto res2 = calcs.mean(std::vector<double>({101, 53.7, 87, 1.2}));
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
public
:
172
/**
173
* @exception Thread::MutexError The constructor might throw this
174
* exception if initialisation of the contained mutex fails. (It is
175
* often not worth checking for this, as it means either memory is
176
* exhausted or pthread has run out of other resources to create new
177
* mutexes.) The constructor will also throw if the default
178
* constructor of the result type represented by this object throws.
179
* @exception Thread::CondError The constructor might throw this
180
* exception if initialisation of the contained condition variable
181
* fails. (It is often not worth checking for this, as it means
182
* either memory is exhausted or pthread has run out of other
183
* resources to create new condition variables.) The constructor will
184
* also throw if the default constructor of the result type
185
* represented by this object throws.
186
*
187
* Since 2.0.8
188
*/
189
AsyncResult
(): res(), done(false), error(0) {}
190
191
~AsyncResult
() {
192
// lock and unlock the mutex in the destructor so that we have an
193
// acquire operation to ensure that when this object is destroyed
194
// memory is synchronised, so any thread may destroy this object
195
Thread::Mutex::Lock
lock{mutex};
196
}
197
198
// AsyncResult objects cannot be copied - they are mainly
199
// intended to pass a result between two known threads
200
/**
201
* This class cannot be copied. The copy constructor is deleted.
202
*
203
* Since 2.0.8
204
*/
205
AsyncResult
(
const
AsyncResult
&) =
delete
;
206
207
/**
208
* This class cannot be copied. The assignment operator is deleted.
209
*
210
* Since 2.0.8
211
*/
212
AsyncResult
&
operator=
(
const
AsyncResult
&) =
delete
;
213
214
/**
215
* This method sets the value represented by the AsyncResult object,
216
* provided that set() has not previously been called and
217
* set_error() has not previously been called with a value other
218
* than 0. If set() has previously been called or set_error()
219
* called with a value other than 0 (so that is_done() will return
220
* true) this method does nothing. It is thread safe. It is not a
221
* cancellation point. It will not throw unless the copy assignment
222
* operator of the value type throws.
223
* @param val The value which this object is to represent and which
224
* calls to get() or a call to move_get() will return. Any thread
225
* waiting on get() or move_get() will unblock, and any subsequent
226
* calls to is_done() will return true.
227
* @return true if the call to this method is effective because
228
* set() has not previously been called and set_error() has not
229
* previously been called with a value other than 0, otherwise
230
* false.
231
*
232
* Since 2.0.8
233
*/
234
bool
set
(
const
T& val) {
235
Thread::Mutex::Lock
lock{mutex};
236
if
(!done) {
237
res = val;
238
done =
true
;
239
cond.
broadcast
();
240
return
true
;
241
}
242
return
false
;
243
}
244
245
/**
246
* This method sets the value represented by the AsyncResult object,
247
* provided that set() has not previously been called and
248
* set_error() has not previously been called with a value other
249
* than 0. If set() has previously been called or set_error()
250
* called with a value other than 0 (so that is_done() will return
251
* true) this method does nothing. It is thread safe. It is not a
252
* cancellation point. It will not throw unless the copy or move
253
* assignment operator of the value type throws.
254
* @param val The value which this object is to represent and which
255
* calls to get() or a call to move_get() will return. Any thread
256
* waiting on get() or move_get() will unblock, and any subsequent
257
* calls to is_done() will return true.
258
* @return true if the call to this method is effective because
259
* set() has not previously been called and set_error() has not
260
* previously been called with a value other than 0, otherwise
261
* false.
262
*
263
* Since 2.0.8
264
*/
265
bool
set
(T&& val) {
266
Thread::Mutex::Lock
lock{mutex};
267
if
(!done) {
268
res = std::move(val);
269
done =
true
;
270
cond.
broadcast
();
271
return
true
;
272
}
273
return
false
;
274
}
275
276
/**
277
* This method gets the value represented by the AsyncResult object.
278
* It is thread safe. It is a cancellation point if it blocks, and
279
* is cancellation safe if the stack unwinds on cancellation. Any
280
* number of threads may call this method and block on it. It will
281
* not throw unless the copy constructor of the return type throws.
282
* It is strongly exception safe.
283
* @return the value represented by this object as set by a call to
284
* set(). If no such value has been set (and no error has been set)
285
* so that is_done() will return false, this method will block until
286
* either a value or an error has been set. If an error has been
287
* set, this method will return a default constructed object of the
288
* template type (and the error can be obtained with get_error()).
289
*
290
* Since 2.0.8
291
*/
292
T
get
()
const
{
293
Thread::Mutex::Lock
lock{mutex};
294
while
(!done) cond.
wait
(mutex);
295
Thread::CancelBlock
b;
296
return
res;
297
}
298
299
/**
300
* This method gets the value represented by the AsyncResult object
301
* by a move operation, if the type of that value implements a move
302
* constructor (otherwise this method does the same as the get()
303
* method). It is provided as an option for cases where a move is
304
* required for efficiency reasons, but although it may be called by
305
* any thread, a move operation may normally only be made once
306
* (except where the return type has been designed to be moved more
307
* than once for the limited purpose of inspecting a flag indicating
308
* whether its value is valid or not). If this method is to be
309
* called then no calls to get() by another thread should normally
310
* be made. This method is a cancellation point if it blocks, and
311
* is cancellation safe if the stack unwinds on cancellation. It
312
* will not throw unless the copy or move constructor of the return
313
* type throws. It is only exception safe if the return type's move
314
* constructor is exception safe.
315
* @return The value represented by this object as set by a call to
316
* set(). If no such value has been set (and no error has been set)
317
* so that is_done() will return false, this method will block until
318
* either a value or an error has been set. If an error has been
319
* set, until a move operation has been carried out this method will
320
* return a default constructed object of the template type (and the
321
* error can be obtained with get_error()).
322
*
323
* Since 2.0.11
324
*/
325
T
move_get
() {
326
Thread::Mutex::Lock
lock{mutex};
327
while
(!done) cond.
wait
(mutex);
328
Thread::CancelBlock
b;
329
return
std::move(res);
330
}
331
332
/**
333
* This method sets an error if called with a value other than 0,
334
* provided that set() has not previously been called and this
335
* method has not previously been called with a value other than 0.
336
* If set() has been called or this method previously called with a
337
* value other than 0 (so that is_done() will return true), this
338
* method does nothing. This method is thread safe. It is not a
339
* cancellation point. It will not throw.
340
* @param err The value which subsequent calls to get_error() will
341
* report. If the value of err is 0, or if this method has been
342
* called with a value other than 0 or set() has previously been
343
* called, this method will do nothing. Otherwise, any thread
344
* waiting on get() or move_get() will unblock (they will return a
345
* default constructed object of the template type), and any
346
* subsequent calls to is_done() will return true.
347
* @return true if the call to this method is effective because the
348
* error value passed is not 0, set() has not previously been called
349
* and this method has not previously been called with a value other
350
* than 0, otherwise false.
351
*
352
* Since 2.0.8
353
*/
354
bool
set_error
(
int
err = -1) {
355
Thread::Mutex::Lock
lock{mutex};
356
if
(err && !done) {
357
error = err;
358
done =
true
;
359
cond.
broadcast
();
360
return
true
;
361
}
362
return
false
;
363
}
364
365
/**
366
* This method is thread safe. It is not a cancellation point. It
367
* will not throw.
368
* @return the error value set by a call to set_error(), or 0 if no
369
* error has been set.
370
*
371
* Since 2.0.8
372
*/
373
int
get_error
()
const
{
374
Thread::Mutex::Lock
lock{mutex};
375
return
error;
376
}
377
378
/**
379
* This method is thread safe. It is not a cancellation point. It
380
* will not throw.
381
* @return true if set() has been called, or set_error() has been
382
* called with a value other than 0, otherwise false.
383
*
384
* Since 2.0.8
385
*/
386
bool
is_done
()
const
{
387
Thread::Mutex::Lock
lock{mutex};
388
return
done;
389
}
390
391
/* Only has effect if --with-glib-memory-slices-compat or
392
* --with-glib-memory-slices-no-compat option picked */
393
CGU_GLIB_MEMORY_SLICES_FUNCS
394
};
395
396
}
// namespace Cgu
397
398
#endif
Generated on Thu Aug 2 2012 12:24:18 for c++-gtk-utils by
1.8.1.1