c++-gtk-utils
async_result.h
Go to the documentation of this file.
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