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 * static void all_done() {throw Thread::Exit();} 00114 * 00115 * // ... other calculation implementation methods here 00116 * 00117 * public: 00118 * 00119 * SharedLockPtr<AsyncResult<double>> mean(const std::vector<double>& nums) { 00120 * SharedLockPtr<AsyncResult<double>> res(new AsyncResult<double>); 00121 * jobs.push(Callback::make_ref(&mean_impl, nums, res)); 00122 * return res; 00123 * } 00124 * 00125 * // ... other calculation methods here 00126 * 00127 * Calcs() { 00128 * t = Thread::JoinableHandle(Thread::Thread::start(Callback::make(*this, &Calcs::do_jobs), true), 00129 * Thread::JoinableHandle::join_on_exit); 00130 * if (!t.is_managing()) throw "Thread start error"; 00131 * } 00132 * ~Calcs() { 00133 * jobs.push(Callback::make(&all_done)); 00134 * t.join(); 00135 * } 00136 * }; 00137 * 00138 * int main () { 00139 * 00140 * Calcs calcs; 00141 * auto res1 = calcs.mean(std::vector<double>({1, 2, 8, 0})); 00142 * auto res2 = calcs.mean(std::vector<double>({101, 53.7, 87, 1.2})); 00143 * 00144 * // ... do something else 00145 * 00146 * std::cout << res1->get() << std::endl; 00147 * std::cout << res2->get() << std::endl; 00148 * 00149 * } 00150 * @endcode 00151 * 00152 * AsyncResult objects cannot be copied by value, and as they need to 00153 * be visible both to the set()ing and get()ing threads, it will often 00154 * be easiest to construct them on free store and copy them by smart 00155 * pointer, as in the example above. However, if the library is 00156 * compiled with the --with-glib-memory-slices-compat or 00157 * --with-glib-memory-slices-no-compat configuration options, any 00158 * AsyncResult object constructed on free store will be constructed in 00159 * glib memory slices, which are an efficient small object allocator. 00160 */ 00161 00162 template <class T> class AsyncResult { 00163 T res; 00164 bool done; 00165 int error; 00166 mutable Thread::Mutex mutex; 00167 mutable Thread::Cond cond; 00168 00169 public: 00170 /** 00171 * @exception Thread::MutexError The constructor might throw this 00172 * exception if initialisation of the contained mutex fails. (It is 00173 * often not worth checking for this, as it means either memory is 00174 * exhausted or pthread has run out of other resources to create new 00175 * mutexes.) The constructor will also throw if the default 00176 * constructor of the result type represented by this object throws. 00177 * @exception Thread::CondError The constructor might throw this 00178 * exception if initialisation of the contained condition variable 00179 * fails. (It is often not worth checking for this, as it means 00180 * either memory is exhausted or pthread has run out of other 00181 * resources to create new condition variables.) The constructor will 00182 * also throw if the default constructor of the result type 00183 * represented by this object throws. 00184 * 00185 * Since 2.0.8 00186 */ 00187 AsyncResult(): res(), done(false), error(0) {} 00188 00189 // AsyncResult objects cannot be copied - they are mainly 00190 // intended to pass a result between two known threads 00191 /** 00192 * This class cannot be copied. The copy constructor is deleted. 00193 * 00194 * Since 2.0.8 00195 */ 00196 AsyncResult(const AsyncResult&) = delete; 00197 00198 /** 00199 * This class cannot be copied. The assignment operator is deleted. 00200 * 00201 * Since 2.0.8 00202 */ 00203 AsyncResult& operator=(const AsyncResult&) = delete; 00204 00205 /** 00206 * This method sets the value represented by the AsyncResult object, 00207 * provided that set() has not previously been called and 00208 * set_error() has not previously been called with a value other 00209 * than 0. If set() has previously been called or set_error() 00210 * called with a value other than 0 (so that is_done() will return 00211 * true) this method does nothing. It is thread safe. It is not a 00212 * cancellation point. It will not throw. 00213 * @param val The value which this object is to represent and which 00214 * calls to get() will return. Any threads waiting on get() will 00215 * unblock, and any subsequent calls to is_done() will return true. 00216 * @return true if the call to this method is effective because 00217 * set() has not previously been called and set_error() has not 00218 * previously been called with a value other than 0, otherwise 00219 * false. 00220 * 00221 * Since 2.0.8 00222 */ 00223 bool set(const T& val) { 00224 Thread::Mutex::Lock lock{mutex}; 00225 if (!done) { 00226 res = val; 00227 done = true; 00228 cond.broadcast(); 00229 return true; 00230 } 00231 return false; 00232 } 00233 00234 /** 00235 * This method sets the value represented by the AsyncResult object, 00236 * provided that set() has not previously been called and 00237 * set_error() has not previously been called with a value other 00238 * than 0. If set() has previously been called or set_error() 00239 * called with a value other than 0 (so that is_done() will return 00240 * true) this method does nothing. It is thread safe. It is not a 00241 * cancellation point. It will not throw. 00242 * @param val The value which this object is to represent and which 00243 * calls to get() will return. Any threads waiting on get() will 00244 * unblock, and any subsequent calls to is_done() will return true. 00245 * @return true if the call to this method is effective because 00246 * set() has not previously been called and set_error() has not 00247 * previously been called with a value other than 0, otherwise 00248 * false. 00249 * 00250 * Since 2.0.8 00251 */ 00252 bool set(T&& val) { 00253 Thread::Mutex::Lock lock{mutex}; 00254 if (!done) { 00255 res = std::move(val); 00256 done = true; 00257 cond.broadcast(); 00258 return true; 00259 } 00260 return false; 00261 } 00262 00263 /** 00264 * This method is thread safe. It is a cancellation point if it 00265 * blocks, and is cancellation safe if the stack unwinds on 00266 * cancellation. Any number of threads may call this method and 00267 * block on it. It will not throw. 00268 * @return the value represented by this object as set by a call to 00269 * set(). If no such value has been set (and no error has been set) 00270 * so that is_done() will return false, this method will block until 00271 * a value has been set. 00272 * 00273 * Since 2.0.8 00274 */ 00275 T get() const { 00276 Thread::Mutex::Lock lock{mutex}; 00277 while (!done) cond.wait(mutex); 00278 return res; 00279 } 00280 00281 /** 00282 * This method sets an error if called with a value other than 0, 00283 * provided that set() has not previously been called and this 00284 * method has not previously been called with a value other than 0. 00285 * If set() has been called or this method previously called with a 00286 * value other than 0 (so that is_done() will return true), this 00287 * method does nothing. This method is thread safe. It is not a 00288 * cancellation point. It will not throw. 00289 * @param err The value which subsequent calls to get_error() will 00290 * report. If the value of err is 0, or if this method has been 00291 * called with a value other than 0 or set() has previously been 00292 * called, this method will do nothing. Otherwise, any threads 00293 * waiting on get() will unblock (get() will return a default 00294 * constructed object of the template type), and any subsequent 00295 * calls to is_done() will return true. 00296 * @return true if the call to this method is effective because the 00297 * error value passed is not 0, set() has not previously been called 00298 * and this method has not previously been called with a value other 00299 * than 0, otherwise false. 00300 * 00301 * Since 2.0.8 00302 */ 00303 bool set_error(int err = -1) { 00304 Thread::Mutex::Lock lock{mutex}; 00305 if (err && !done) { 00306 error = err; 00307 done = true; 00308 cond.broadcast(); 00309 return true; 00310 } 00311 return false; 00312 } 00313 00314 /** 00315 * This method is thread safe. It is not a cancellation point. It 00316 * will not throw. 00317 * @return the error value set by a call to set_error(), or 0 if no 00318 * error has been set. 00319 * 00320 * Since 2.0.8 00321 */ 00322 int get_error() const { 00323 Thread::Mutex::Lock lock{mutex}; 00324 return error; 00325 } 00326 00327 /** 00328 * This method is thread safe. It is not a cancellation point. It 00329 * will not throw. 00330 * @return true if set() has been called, or set_error() has been 00331 * called with a value other than 0, otherwise false. 00332 * 00333 * Since 2.0.8 00334 */ 00335 bool is_done() const { 00336 Thread::Mutex::Lock lock{mutex}; 00337 return done; 00338 } 00339 00340 /* Only has effect if --with-glib-memory-slices-compat or 00341 * --with-glib-memory-slices-no-compat option picked */ 00342 CGU_GLIB_MEMORY_SLICES_FUNCS 00343 }; 00344 00345 } // namespace Cgu 00346 00347 #endif