c++-gtk-utils
Public Member Functions | List of all members
Cgu::AsyncResult< T > Class Template Reference

A thread-safe asynchronous result class. More...

#include <c++-gtk-utils/async_result.h>

Public Member Functions

 AsyncResult ()
 
 ~AsyncResult ()
 
bool set (const T &val)
 
get () const
 
bool set_error (int err=-1)
 
int get_error () const
 
bool is_done () const
 

Detailed Description

template<class T>
class Cgu::AsyncResult< T >

A thread-safe asynchronous result class.

See also
AsyncQueueDispatch Thread::Future

Cgu::Thread::Future operates on the principle of there being one worker thread per task. In some cases however, it may be better to have a worker thread, or a limited pool of worker threads, executing a larger number of tasks. This can be implemented by having a worker thread or threads waiting on a Cgu::AsyncQueueDispatch object, onto which other threads push tasks represented by Cgu::Callback::SafeFunctor objects.

Where this model is adopted, when a task completes it may report its results by dispatching a further callback to a glib main loop using Cgu::Callback::post(). However, there will also be cases where, rather than passing a result as an event to a main loop, a thread is to to wait for the task to complete. This class is intended to facilitate that. It operates in a way which is similar to the std::promise class in C++11. The thread which wishes to extract a result can call the get() method, which will block until the worker thread has called the set() method or posted an error.

Here is a compilable example of a calculator class which runs a dedicated thread on which it carries out all its calculations:

#include <vector>
#include <numeric>
#include <ostream>
#include <iostream>
using namespace Cgu;
class Calcs {
void do_jobs() {
for (;;) {
jobs.pop_dispatch(job);
job();
}
}
static void mean_impl(const std::vector<double>& nums,
if (nums.empty()) res->set(0.0);
else res->set(std::accumulate(nums.begin(), nums.end(), 0.0)/nums.size());
}
static void all_done() {throw Thread::Exit();}
// ... other calculation implementation methods here
public:
SharedLockPtr<AsyncResult<double> > mean(const std::vector<double>& nums) {
jobs.push(Callback::make_ref(&mean_impl, nums, res));
return res;
}
// ... other calculation methods here
Calcs() {
Thread::JoinableHandle temp(Thread::Thread::start(Callback::make(*this, &Calcs::do_jobs), true),
if (!temp.is_managing()) throw "Thread start error";
t = temp;
}
~Calcs() {
jobs.push(Callback::make(&all_done));
t.join();
}
};
int main () {
Calcs calcs;
const double ar1[] = {1, 2, 8, 0};
const double ar2[] = {101, 53.7, 87, 1.2};
SharedLockPtr<AsyncResult<double> > res1 = calcs.mean(std::vector<double>(ar1, ar1 + 4));
SharedLockPtr<AsyncResult<double> > res2 = calcs.mean(std::vector<double>(ar2, ar2 + 4));
// ... do something else
std::cout << res1->get() << std::endl;
std::cout << res2->get() << std::endl;
}

AsyncResult objects cannot be copied by value, and as they need to be visible both to the set()ing and get()ing threads, it will often be easiest to construct them on free store and copy them by smart pointer, as in the example above. However, if the library is compiled with the --with-glib-memory-slices-compat or --with-glib-memory-slices-no-compat configuration options, any AsyncResult object constructed on free store will be constructed in glib memory slices, which are an efficient small object allocator.

Constructor & Destructor Documentation

template<class T >
Cgu::AsyncResult< T >::AsyncResult ( )
inline
Exceptions
Thread::MutexErrorThe constructor might throw this exception if initialisation of the contained mutex fails. (It is often not worth checking for this, as it means either memory is exhausted or pthread has run out of other resources to create new mutexes.) The constructor will also throw if the default constructor of the result type represented by this object throws.
Thread::CondErrorThe constructor might throw this exception if initialisation of the contained condition variable fails. (It is often not worth checking for this, as it means either memory is exhausted or pthread has run out of other resources to create new condition variables.) The constructor will also throw if the default constructor of the result type represented by this object throws.

Since 1.2.22

template<class T >
Cgu::AsyncResult< T >::~AsyncResult ( )
inline

Member Function Documentation

template<class T >
T Cgu::AsyncResult< T >::get ( ) const
inline

This method gets the stored value represented by the AsyncResult object. It is thread safe. It is a cancellation point if it blocks, and is cancellation safe if the stack unwinds on cancellation. Any number of threads may call this method and block on it. It will not throw unless the copy constructor of the return value type throws. It is strongly exception safe.

Returns
the value represented by this object as set by a call to set(). If no such value has been set (and no error has been set) so that is_done() will return false, this method will block until either a value or an error has been set. If an error has been set, this method will return a default constructed object of the template type (and the error can be obtained with get_error()).
Note
Question: Couldn't this method return the stored value by reference to const? Answer: It could. However, because of return value optimization, which will be implemented by any compiler capable of compiling this library, no advantage would be gained by doing so when initializing a local variable with the return value of this method (the copy constructor will only be called once whether returning by value or const reference). The advantage of returning by value is that the call to the copy constructor is forced to be within the AsyncResult object's mutex, so different threads' calls to the copy constructor are serialized, and also with blocked cancellation, so this method is cancellation safe. All calls to this method by different threads are therefore isolated and we do not have to worry about the thread safety of direct access to the stored value via its const methods outside the mutex (which would not be thread safe if the stored value has data members declared mutable) nor about the cancellation safety of the copy constructor. Of course, for objects which do not have mutable data, a hit arises by returning by value in cases where it is not intended to initialize a local variable at all nor to cancel a thread: where, say, only const methods are to be called on the return value (which could be done directly if this method returned by const reference). However, that is a design trade-off.

Since 1.2.22

template<class T >
int Cgu::AsyncResult< T >::get_error ( ) const
inline

This method is thread safe. It is not a cancellation point. It will not throw.

Returns
the error value set by a call to set_error(), or 0 if no error has been set.

Since 1.2.22

template<class T >
bool Cgu::AsyncResult< T >::is_done ( ) const
inline

This method is thread safe. It is not a cancellation point. It will not throw.

Returns
true if set() has been called, or set_error() has been called with a value other than 0, otherwise false.

Since 1.2.22

template<class T >
bool Cgu::AsyncResult< T >::set ( const T &  val)
inline

This method sets the value represented by the AsyncResult object, provided that set() has not previously been called and set_error() has not previously been called with a value other than 0. If set() has previously been called or set_error() called with a value other than 0 (so that is_done() will return true) this method does nothing. It is thread safe. It is not a cancellation point. It will not throw unless the assignment operator of the value type throws.

Parameters
valThe value which this object is to represent and which calls to get() will return. Any threads waiting on get() will unblock, and any subsequent calls to is_done() will return true.
Returns
true if the call to this method is effective because set() has not previously been called and set_error() has not previously been called with a value other than 0, otherwise false.

Since 1.2.22

template<class T >
bool Cgu::AsyncResult< T >::set_error ( int  err = -1)
inline

This method sets an error if called with a value other than 0, provided that set() has not previously been called and this method has not previously been called with a value other than 0. If set() has been called or this method previously called with a value other than 0 (so that is_done() will return true), this method does nothing. This method is thread safe. It is not a cancellation point. It will not throw.

Parameters
errThe value which subsequent calls to get_error() will report. If the value of err is 0, or if this method has been called with a value other than 0 or set() has previously been called, this method will do nothing. Otherwise, any threads waiting on get() will unblock (get() will return a default constructed object of the template type), and any subsequent calls to is_done() will return true.
Returns
true if the call to this method is effective because the error value passed is not 0, set() has not previously been called and this method has not previously been called with a value other than 0, otherwise false.

Since 1.2.22


The documentation for this class was generated from the following file: