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/cgu_config.h
>
51
52
53
namespace
Cgu {
54
55
/**
56
* @class AsyncResult async_result.h c++-gtk-utils/async_result.h
57
* @brief A thread-safe asynchronous result class.
58
* @sa AsyncQueueDispatch Thread::Future
59
*
60
* Cgu::Thread::Future operates on the principle of there being one
61
* worker thread per task. In some cases however, it may be better to
62
* have a worker thread, or a limited pool of worker threads,
63
* executing a larger number of tasks. This can be implemented by
64
* having a worker thread or threads waiting on a
65
* Cgu::AsyncQueueDispatch object, onto which other threads push tasks
66
* represented by Cgu::Callback::SafeFunctor objects.
67
*
68
* Where this model is adopted, when a task completes it may report
69
* its results by dispatching a further callback to a glib main loop
70
* using Cgu::Callback::post(). However, there will also be cases
71
* where, rather than passing a result as an event to a main loop, a
72
* thread is to to wait for the task to complete. This class is
73
* intended to facilitate that. It operates in a way which is similar
74
* to the std::promise class in C++11. The thread which wishes to
75
* extract a result can call the get() method, which will block until
76
* the worker thread has called the set() method or posted an error.
77
*
78
* Here is a compilable example of a calculator class which runs a
79
* dedicated thread on which it carries out all its calculations:
80
*
81
* @code
82
* #include <vector>
83
* #include <numeric>
84
* #include <ostream>
85
* #include <iostream>
86
*
87
* #include <c++-gtk-utils/async_result.h>
88
* #include <c++-gtk-utils/async_queue.h>
89
* #include <c++-gtk-utils/shared_ptr.h>
90
* #include <c++-gtk-utils/thread.h>
91
* #include <c++-gtk-utils/callback.h>
92
*
93
* using namespace Cgu;
94
*
95
* class Calcs {
96
* AsyncQueueDispatch<Callback::SafeFunctor> jobs;
97
* Thread::JoinableHandle t;
98
*
99
* void do_jobs() {
100
* for (;;) {
101
* Callback::SafeFunctor job;
102
* jobs.pop_dispatch(job);
103
* job();
104
* }
105
* }
106
* public:
107
*
108
* SharedLockPtr<AsyncResult<double>> mean(const std::vector<double>& nums) {
109
* SharedLockPtr<AsyncResult<double>> res(new AsyncResult<double>);
110
* jobs.emplace(Callback::lambda<>([=]() {
111
* if (nums.empty()) res->set(0.0);
112
* else res->set(std::accumulate(nums.begin(), nums.end(), 0.0)/nums.size());
113
* return;
114
* }));
115
* return res;
116
* }
117
*
118
* // ... other calculation methods here
119
*
120
* Calcs() {
121
* t = Thread::JoinableHandle(Thread::Thread::start(Callback::make(*this, &Calcs::do_jobs), true),
122
* Thread::JoinableHandle::join_on_exit);
123
* if (!t.is_managing()) throw "Thread start error";
124
* }
125
* ~Calcs() {
126
* jobs.emplace(Callback::lambda<>([]() {throw Thread::Exit();}));
127
* t.join();
128
* }
129
* };
130
*
131
* int main () {
132
*
133
* Calcs calcs;
134
* auto res1 = calcs.mean(std::vector<double>({1, 2, 8, 0}));
135
* auto res2 = calcs.mean(std::vector<double>({101, 53.7, 87, 1.2}));
136
*
137
* // ... do something else
138
*
139
* std::cout << res1->get() << std::endl;
140
* std::cout << res2->get() << std::endl;
141
*
142
* }
143
* @endcode
144
*
145
* AsyncResult objects cannot be copied by value, and as they need to
146
* be visible both to the set()ing and get()ing threads, it will often
147
* be easiest to construct them on free store and copy them by smart
148
* pointer, as in the example above. However, if the library is
149
* compiled with the --with-glib-memory-slices-compat or
150
* --with-glib-memory-slices-no-compat configuration options, any
151
* AsyncResult object constructed on free store will be constructed in
152
* glib memory slices, which are an efficient small object allocator.
153
*/
154
155
template
<
class
T>
class
AsyncResult
{
156
T res;
157
bool
done;
158
int
error;
159
mutable
Thread::Mutex
mutex;
160
mutable
Thread::Cond
cond;
161
162
public
:
163
/**
164
* @exception Thread::MutexError The constructor might throw this
165
* exception if initialisation of the contained mutex fails. (It is
166
* often not worth checking for this, as it means either memory is
167
* exhausted or pthread has run out of other resources to create new
168
* mutexes.) The constructor will also throw if the default
169
* constructor of the result type represented by this object throws.
170
* @exception Thread::CondError The constructor might throw this
171
* exception if initialisation of the contained condition variable
172
* fails. (It is often not worth checking for this, as it means
173
* either memory is exhausted or pthread has run out of other
174
* resources to create new condition variables.) The constructor will
175
* also throw if the default constructor of the result type
176
* represented by this object throws.
177
*
178
* Since 2.0.8
179
*/
180
AsyncResult
(): res(), done(false), error(0) {}
181
182
// AsyncResult objects cannot be copied - they are mainly
183
// intended to pass a result between two known threads
184
/**
185
* This class cannot be copied. The copy constructor is deleted.
186
*
187
* Since 2.0.8
188
*/
189
AsyncResult
(
const
AsyncResult
&) =
delete
;
190
191
/**
192
* This class cannot be copied. The assignment operator is deleted.
193
*
194
* Since 2.0.8
195
*/
196
AsyncResult
&
operator=
(
const
AsyncResult
&) =
delete
;
197
198
/**
199
* This method sets the value represented by the AsyncResult object,
200
* provided that set() has not previously been called and
201
* set_error() has not previously been called with a value other
202
* than 0. If set() has previously been called or set_error()
203
* called with a value other than 0 (so that is_done() will return
204
* true) this method does nothing. It is thread safe. It is not a
205
* cancellation point. It will not throw.
206
* @param val The value which this object is to represent and which
207
* calls to get() will return. Any threads waiting on get() will
208
* unblock, and any subsequent calls to is_done() will return true.
209
* @return true if the call to this method is effective because
210
* set() has not previously been called and set_error() has not
211
* previously been called with a value other than 0, otherwise
212
* false.
213
*
214
* Since 2.0.8
215
*/
216
bool
set
(
const
T& val) {
217
Thread::Mutex::Lock
lock{mutex};
218
if
(!done) {
219
res = val;
220
done =
true
;
221
cond.
broadcast
();
222
return
true
;
223
}
224
return
false
;
225
}
226
227
/**
228
* This method sets the value represented by the AsyncResult object,
229
* provided that set() has not previously been called and
230
* set_error() has not previously been called with a value other
231
* than 0. If set() has previously been called or set_error()
232
* called with a value other than 0 (so that is_done() will return
233
* true) this method does nothing. It is thread safe. It is not a
234
* cancellation point. It will not throw.
235
* @param val The value which this object is to represent and which
236
* calls to get() will return. Any threads waiting on get() will
237
* unblock, and any subsequent calls to is_done() will return true.
238
* @return true if the call to this method is effective because
239
* set() has not previously been called and set_error() has not
240
* previously been called with a value other than 0, otherwise
241
* false.
242
*
243
* Since 2.0.8
244
*/
245
bool
set
(T&& val) {
246
Thread::Mutex::Lock
lock{mutex};
247
if
(!done) {
248
res = std::move(val);
249
done =
true
;
250
cond.
broadcast
();
251
return
true
;
252
}
253
return
false
;
254
}
255
256
/**
257
* This method is thread safe. It is a cancellation point if it
258
* blocks, and is cancellation safe if the stack unwinds on
259
* cancellation. Any number of threads may call this method and
260
* block on it. It will not throw.
261
* @return the value represented by this object as set by a call to
262
* set(). If no such value has been set (and no error has been set)
263
* so that is_done() will return false, this method will block until
264
* either a value or an error has been set. If an error has been
265
* set, this method will return a default constructed object of the
266
* template type (and the error can be obtained with get_error()).
267
*
268
* Since 2.0.8
269
*/
270
T
get
()
const
{
271
Thread::Mutex::Lock
lock{mutex};
272
while
(!done) cond.
wait
(mutex);
273
return
res;
274
}
275
276
/**
277
* This method sets an error if called with a value other than 0,
278
* provided that set() has not previously been called and this
279
* method has not previously been called with a value other than 0.
280
* If set() has been called or this method previously called with a
281
* value other than 0 (so that is_done() will return true), this
282
* method does nothing. This method is thread safe. It is not a
283
* cancellation point. It will not throw.
284
* @param err The value which subsequent calls to get_error() will
285
* report. If the value of err is 0, or if this method has been
286
* called with a value other than 0 or set() has previously been
287
* called, this method will do nothing. Otherwise, any threads
288
* waiting on get() will unblock (get() will return a default
289
* constructed object of the template type), and any subsequent
290
* calls to is_done() will return true.
291
* @return true if the call to this method is effective because the
292
* error value passed is not 0, set() has not previously been called
293
* and this method has not previously been called with a value other
294
* than 0, otherwise false.
295
*
296
* Since 2.0.8
297
*/
298
bool
set_error
(
int
err = -1) {
299
Thread::Mutex::Lock
lock{mutex};
300
if
(err && !done) {
301
error = err;
302
done =
true
;
303
cond.
broadcast
();
304
return
true
;
305
}
306
return
false
;
307
}
308
309
/**
310
* This method is thread safe. It is not a cancellation point. It
311
* will not throw.
312
* @return the error value set by a call to set_error(), or 0 if no
313
* error has been set.
314
*
315
* Since 2.0.8
316
*/
317
int
get_error
()
const
{
318
Thread::Mutex::Lock
lock{mutex};
319
return
error;
320
}
321
322
/**
323
* This method is thread safe. It is not a cancellation point. It
324
* will not throw.
325
* @return true if set() has been called, or set_error() has been
326
* called with a value other than 0, otherwise false.
327
*
328
* Since 2.0.8
329
*/
330
bool
is_done
()
const
{
331
Thread::Mutex::Lock
lock{mutex};
332
return
done;
333
}
334
335
/* Only has effect if --with-glib-memory-slices-compat or
336
* --with-glib-memory-slices-no-compat option picked */
337
CGU_GLIB_MEMORY_SLICES_FUNCS
338
};
339
340
}
// namespace Cgu
341
342
#endif
Generated on Sun Jul 1 2012 09:21:43 for c++-gtk-utils by
1.8.1.1