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  *     // ... 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