c++-gtk-utils
|
00001 /* Copyright (C) 2012 Chris Vine 00002 00003 The library comprised in this file or of which this file is part is 00004 distributed by Chris Vine under the GNU Lesser General Public 00005 License as follows: 00006 00007 This library is free software; you can redistribute it and/or 00008 modify it under the terms of the GNU Lesser General Public License 00009 as published by the Free Software Foundation; either version 2.1 of 00010 the License, or (at your option) any later version. 00011 00012 This library is distributed in the hope that it will be useful, but 00013 WITHOUT ANY WARRANTY; without even the implied warranty of 00014 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 00015 Lesser General Public License, version 2.1, for more details. 00016 00017 You should have received a copy of the GNU Lesser General Public 00018 License, version 2.1, along with this library (see the file LGPL.TXT 00019 which came with this source code package in the c++-gtk-utils 00020 sub-directory); if not, write to the Free Software Foundation, Inc., 00021 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 00022 00023 However, it is not intended that the object code of a program whose 00024 source code instantiates a template from this file or uses macros or 00025 inline functions (of any length) should by reason only of that 00026 instantiation or use be subject to the restrictions of use in the GNU 00027 Lesser General Public License. With that in mind, the words "and 00028 macros, inline functions and instantiations of templates (of any 00029 length)" shall be treated as substituted for the words "and small 00030 macros and small inline functions (ten lines or less in length)" in 00031 the fourth paragraph of section 5 of that licence. This does not 00032 affect any other reason why object code may be subject to the 00033 restrictions in that licence (nor for the avoidance of doubt does it 00034 affect the application of section 2 of that licence to modifications 00035 of the source code in this file). 00036 00037 */ 00038 00039 /** 00040 * @file async_result.h 00041 * @brief This file provides a thread-safe asynchronous result class. 00042 */ 00043 00044 #ifndef CGU_ASYNC_RESULT_H 00045 #define CGU_ASYNC_RESULT_H 00046 00047 #include <utility> // for std::move 00048 00049 #include <c++-gtk-utils/mutex.h> 00050 #include <c++-gtk-utils/cgu_config.h> 00051 00052 00053 namespace Cgu { 00054 00055 /** 00056 * @class AsyncResult async_result.h c++-gtk-utils/async_result.h 00057 * @brief A thread-safe asynchronous result class. 00058 * @sa AsyncQueueDispatch Thread::Future 00059 * 00060 * Cgu::Thread::Future operates on the principle of there being one 00061 * worker thread per task. In some cases however, it may be better to 00062 * have a worker thread, or a limited pool of worker threads, 00063 * executing a larger number of tasks. This can be implemented by 00064 * having a worker thread or threads waiting on a 00065 * Cgu::AsyncQueueDispatch object, onto which other threads push tasks 00066 * represented by Cgu::Callback::SafeFunctor objects. 00067 * 00068 * Where this model is adopted, when a task completes it may report 00069 * its results by dispatching a further callback to a glib main loop 00070 * using Cgu::Callback::post(). However, there will also be cases 00071 * where, rather than passing a result as an event to a main loop, a 00072 * thread is to to wait for the task to complete. This class is 00073 * intended to facilitate that. It operates in a way which is similar 00074 * to the std::promise class in C++11. The thread which wishes to 00075 * extract a result can call the get() method, which will block until 00076 * the worker thread has called the set() method or posted an error. 00077 * 00078 * Here is a compilable example of a calculator class which runs a 00079 * dedicated thread on which it carries out all its calculations: 00080 * 00081 * @code 00082 * #include <vector> 00083 * #include <numeric> 00084 * #include <ostream> 00085 * #include <iostream> 00086 * 00087 * #include <c++-gtk-utils/async_result.h> 00088 * #include <c++-gtk-utils/async_queue.h> 00089 * #include <c++-gtk-utils/shared_ptr.h> 00090 * #include <c++-gtk-utils/thread.h> 00091 * #include <c++-gtk-utils/callback.h> 00092 * 00093 * using namespace Cgu; 00094 * 00095 * class Calcs { 00096 * AsyncQueueDispatch<Callback::SafeFunctor> jobs; 00097 * Thread::JoinableHandle t; 00098 * 00099 * void do_jobs() { 00100 * for (;;) { 00101 * Callback::SafeFunctor job; 00102 * jobs.pop_dispatch(job); 00103 * job(); 00104 * } 00105 * } 00106 * 00107 * static void mean_impl(const std::vector<double>& nums, 00108 * const SharedLockPtr<AsyncResult<double>>& res) { 00109 * if (nums.empty()) res->set(0.0); 00110 * else res->set(std::accumulate(nums.begin(), nums.end(), 0.0)/nums.size()); 00111 * } 00112 * 00113 * // ... other calculation implementation methods here 00114 * 00115 * public: 00116 * 00117 * SharedLockPtr<AsyncResult<double>> mean(const std::vector<double>& nums) { 00118 * SharedLockPtr<AsyncResult<double>> res(new AsyncResult<double>); 00119 * jobs.push(Callback::make_ref(&mean_impl, nums, res)); 00120 * return res; 00121 * } 00122 * 00123 * // ... other calculation methods here 00124 * 00125 * Calcs() { 00126 * t = Thread::JoinableHandle(Thread::Thread::start(Callback::make(*this, &Calcs::do_jobs), true), 00127 * Thread::JoinableHandle::join_on_exit); 00128 * if (!t.is_managing()) throw "Thread start error"; 00129 * } 00130 * ~Calcs() { 00131 * jobs.push(Callback::lambda<>([]() {throw Thread::Exit();})); 00132 * t.join(); 00133 * } 00134 * }; 00135 * 00136 * int main () { 00137 * 00138 * Calcs calcs; 00139 * auto res1 = calcs.mean(std::vector<double>({1, 2, 8, 0})); 00140 * auto res2 = calcs.mean(std::vector<double>({101, 53.7, 87, 1.2})); 00141 * 00142 * // ... do something else 00143 * 00144 * std::cout << res1->get() << std::endl; 00145 * std::cout << res2->get() << std::endl; 00146 * 00147 * } 00148 * @endcode 00149 * 00150 * AsyncResult objects cannot be copied by value, and as they need to 00151 * be visible both to the set()ing and get()ing threads, it will often 00152 * be easiest to construct them on free store and copy them by smart 00153 * pointer, as in the example above. However, if the library is 00154 * compiled with the --with-glib-memory-slices-compat or 00155 * --with-glib-memory-slices-no-compat configuration options, any 00156 * AsyncResult object constructed on free store will be constructed in 00157 * glib memory slices, which are an efficient small object allocator. 00158 */ 00159 00160 template <class T> class AsyncResult { 00161 T res; 00162 bool done; 00163 int error; 00164 mutable Thread::Mutex mutex; 00165 mutable Thread::Cond cond; 00166 00167 public: 00168 /** 00169 * @exception Thread::MutexError The constructor might throw this 00170 * exception if initialisation of the contained mutex fails. (It is 00171 * often not worth checking for this, as it means either memory is 00172 * exhausted or pthread has run out of other resources to create new 00173 * mutexes.) The constructor will also throw if the default 00174 * constructor of the result type represented by this object throws. 00175 * @exception Thread::CondError The constructor might throw this 00176 * exception if initialisation of the contained condition variable 00177 * fails. (It is often not worth checking for this, as it means 00178 * either memory is exhausted or pthread has run out of other 00179 * resources to create new condition variables.) The constructor will 00180 * also throw if the default constructor of the result type 00181 * represented by this object throws. 00182 * 00183 * Since 2.0.8 00184 */ 00185 AsyncResult(): res(), done(false), error(0) {} 00186 00187 // AsyncResult objects cannot be copied - they are mainly 00188 // intended to pass a result between two known threads 00189 /** 00190 * This class cannot be copied. The copy constructor is deleted. 00191 * 00192 * Since 2.0.8 00193 */ 00194 AsyncResult(const AsyncResult&) = delete; 00195 00196 /** 00197 * This class cannot be copied. The assignment operator is deleted. 00198 * 00199 * Since 2.0.8 00200 */ 00201 AsyncResult& operator=(const AsyncResult&) = delete; 00202 00203 /** 00204 * This method sets the value represented by the AsyncResult object, 00205 * provided that set() has not previously been called and 00206 * set_error() has not previously been called with a value other 00207 * than 0. If set() has previously been called or set_error() 00208 * called with a value other than 0 (so that is_done() will return 00209 * true) this method does nothing. It is thread safe. It is not a 00210 * cancellation point. It will not throw. 00211 * @param val The value which this object is to represent and which 00212 * calls to get() will return. Any threads waiting on get() will 00213 * unblock, and any subsequent calls to is_done() will return true. 00214 * @return true if the call to this method is effective because 00215 * set() has not previously been called and set_error() has not 00216 * previously been called with a value other than 0, otherwise 00217 * false. 00218 * 00219 * Since 2.0.8 00220 */ 00221 bool set(const T& val) { 00222 Thread::Mutex::Lock lock{mutex}; 00223 if (!done) { 00224 res = val; 00225 done = true; 00226 cond.broadcast(); 00227 return true; 00228 } 00229 return false; 00230 } 00231 00232 /** 00233 * This method sets the value represented by the AsyncResult object, 00234 * provided that set() has not previously been called and 00235 * set_error() has not previously been called with a value other 00236 * than 0. If set() has previously been called or set_error() 00237 * called with a value other than 0 (so that is_done() will return 00238 * true) this method does nothing. It is thread safe. It is not a 00239 * cancellation point. It will not throw. 00240 * @param val The value which this object is to represent and which 00241 * calls to get() will return. Any threads waiting on get() will 00242 * unblock, and any subsequent calls to is_done() will return true. 00243 * @return true if the call to this method is effective because 00244 * set() has not previously been called and set_error() has not 00245 * previously been called with a value other than 0, otherwise 00246 * false. 00247 * 00248 * Since 2.0.8 00249 */ 00250 bool set(T&& val) { 00251 Thread::Mutex::Lock lock{mutex}; 00252 if (!done) { 00253 res = std::move(val); 00254 done = true; 00255 cond.broadcast(); 00256 return true; 00257 } 00258 return false; 00259 } 00260 00261 /** 00262 * This method is thread safe. It is a cancellation point if it 00263 * blocks, and is cancellation safe if the stack unwinds on 00264 * cancellation. Any number of threads may call this method and 00265 * block on it. It will not throw. 00266 * @return the value represented by this object as set by a call to 00267 * set(). If no such value has been set (and no error has been set) 00268 * so that is_done() will return false, this method will block until 00269 * a value has been set. 00270 * 00271 * Since 2.0.8 00272 */ 00273 T get() const { 00274 Thread::Mutex::Lock lock{mutex}; 00275 while (!done) cond.wait(mutex); 00276 return res; 00277 } 00278 00279 /** 00280 * This method sets an error if called with a value other than 0, 00281 * provided that set() has not previously been called and this 00282 * method has not previously been called with a value other than 0. 00283 * If set() has been called or this method previously called with a 00284 * value other than 0 (so that is_done() will return true), this 00285 * method does nothing. This method is thread safe. It is not a 00286 * cancellation point. It will not throw. 00287 * @param err The value which subsequent calls to get_error() will 00288 * report. If the value of err is 0, or if this method has been 00289 * called with a value other than 0 or set() has previously been 00290 * called, this method will do nothing. Otherwise, any threads 00291 * waiting on get() will unblock (get() will return a default 00292 * constructed object of the template type), and any subsequent 00293 * calls to is_done() will return true. 00294 * @return true if the call to this method is effective because the 00295 * error value passed is not 0, set() has not previously been called 00296 * and this method has not previously been called with a value other 00297 * than 0, otherwise false. 00298 * 00299 * Since 2.0.8 00300 */ 00301 bool set_error(int err = -1) { 00302 Thread::Mutex::Lock lock{mutex}; 00303 if (err && !done) { 00304 error = err; 00305 done = true; 00306 cond.broadcast(); 00307 return true; 00308 } 00309 return false; 00310 } 00311 00312 /** 00313 * This method is thread safe. It is not a cancellation point. It 00314 * will not throw. 00315 * @return the error value set by a call to set_error(), or 0 if no 00316 * error has been set. 00317 * 00318 * Since 2.0.8 00319 */ 00320 int get_error() const { 00321 Thread::Mutex::Lock lock{mutex}; 00322 return error; 00323 } 00324 00325 /** 00326 * This method is thread safe. It is not a cancellation point. It 00327 * will not throw. 00328 * @return true if set() has been called, or set_error() has been 00329 * called with a value other than 0, otherwise false. 00330 * 00331 * Since 2.0.8 00332 */ 00333 bool is_done() const { 00334 Thread::Mutex::Lock lock{mutex}; 00335 return done; 00336 } 00337 00338 /* Only has effect if --with-glib-memory-slices-compat or 00339 * --with-glib-memory-slices-no-compat option picked */ 00340 CGU_GLIB_MEMORY_SLICES_FUNCS 00341 }; 00342 00343 } // namespace Cgu 00344 00345 #endif