/* Copyright (C) 2012 to 2017 Chris Vine

The library comprised in this file or of which this file is part is
distributed by Chris Vine under the GNU Lesser General Public
License as follows:

   This library is free software; you can redistribute it and/or
   modify it under the terms of the GNU Lesser General Public License
   as published by the Free Software Foundation; either version 2.1 of
   the License, or (at your option) any later version.

   This library is distributed in the hope that it will be useful, but
   WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
   Lesser General Public License, version 2.1, for more details.

   You should have received a copy of the GNU Lesser General Public
   License, version 2.1, along with this library (see the file LGPL.TXT
   which came with this source code package in the c++-gtk-utils
   sub-directory); if not, write to the Free Software Foundation, Inc.,
   51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.

However, it is not intended that the object code of a program whose
source code instantiates a template from this file or uses macros or
inline functions (of any length) should by reason only of that
instantiation or use be subject to the restrictions of use in the GNU
Lesser General Public License.  With that in mind, the words "and
macros, inline functions and instantiations of templates (of any
length)" shall be treated as substituted for the words "and small
macros and small inline functions (ten lines or less in length)" in
the fourth paragraph of section 5 of that licence.  This does not
affect any other reason why object code may be subject to the
restrictions in that licence (nor for the avoidance of doubt does it
affect the application of section 2 of that licence to modifications
of the source code in this file).

*/

#ifndef CGU_TASK_MANAGER_H
#define CGU_TASK_MANAGER_H

#include <deque>
#include <utility>     // for std::pair, std::move and std::forward
#include <exception>   // for std::exception
#include <memory>      // for std::unique_ptr
#include <future>      // for std::future and std::packaged_task
#include <type_traits> // for std::remove_reference, std::remove_const, std::enable_if,
                       // std::is_convertible and std::result_of

#include <c++-gtk-utils/callback.h>
#include <c++-gtk-utils/thread.h>
#include <c++-gtk-utils/mutex.h>
#include <c++-gtk-utils/async_queue.h>
#include <c++-gtk-utils/shared_ptr.h>
#include <c++-gtk-utils/async_result.h>
#include <c++-gtk-utils/emitter.h>
#include <c++-gtk-utils/cgu_config.h>

namespace Cgu {

namespace Thread {

struct TaskError: public std::exception {
  virtual const char* what() const throw() {return "TaskError\n";}
};

/**
 * @class Cgu::Thread::TaskManager task_manager.h c++-gtk-utils/task_manager.h
 * @brief A thread-pool class for managing tasks in multi-threaded programs.
 * @sa Cgu::Thread::Future Cgu::AsyncResult Cgu::AsyncQueueDispatch Cgu::AsyncChannel Cgu::Callback::post() Cgu::Thread::TaskManager::IncHandle Cgu::Thread::parallel_for_each() Cgu::Thread::parallel_for_each_partial() Cgu::Thread::parallel_transform() Cgu::Thread::parallel_transform_partial()
 *
 * 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 limited pool of worker threads executing a larger number of
 * tasks.  This class implements this approach via a thread pool.
 *
 * One common approach for thread pools of this kind is to set the
 * maximum number of threads to the number of cores, or some number
 * less than the number of cores, available on the local machine.  How
 * that can be determined is system specific (on linux it can be
 * obtained by, for example, counting the 'processor' fields in
 * /proc/cpuinfo or by using sysconf with the glibc extension for
 * _SC_NPROCESSORS_ONLN).  From version 2.36, glib has a
 * g_get_num_processors() function.  From gcc-4.7, C++11's
 * std::thread::hardware_concurrency() static member function is also
 * available.
 *
 * The most general way of creating a new task is to call
 * TaskManager::add_task() with a callable object (such as a lambda
 * expression or the return value of std::bind) which returns void,
 * although add_task() will also take a Callback::Callback object.
 * Where the task needs to provide a result, two approaches can be
 * adopted.  First, the task callback can have a Cgu::AsyncResult
 * object held by Cgu::SharedLockPtr (or by std::shared_ptr having a
 * thread safe reference count) bound to it, or it can execute a
 * std::packaged_task object from which it can obtain a std::future
 * object.  Alternatively, a task can provide a result asynchronously
 * to a glib main loop by calling Cgu::Callback::post() when it is
 * ready to do so.  The TaskManager::make_task_result(),
 * TaskManager::make_task_packaged(), TaskManager::make_task_when(),
 * TaskManager::make_task_when_full(),
 * TaskManager::make_task_compose(),
 * TaskManager::make_task_packaged_when() and
 * TaskManager::make_task_packaged_compose() convenience wrapper
 * methods are provided which will set this up for you (including
 * constructing appropriate task callbacks).  This would normally be
 * done by passing one of those functions a callable object which
 * returns a value, such as a lambda expression or the return value of
 * std::bind.  Tasks can add other tasks, enabling the composition of
 * an arbitrary number of tasks to obtain a final result.
 *
 * Overloads of TaskManager::make_task_result(),
 * TaskManager::make_task_when() and
 * TaskManager::make_task_when_full() also exist which take a function
 * pointer (or an object reference and member function pointer) to a
 * function which returns a value, with bound arguments, but these are
 * deprecated in the 2.2 series of the library as they offer little
 * advantage over using std::bind.  (Although deprecated, there is no
 * plan to remove these functions as they are there and they work -
 * the deprecation is in effect guidance.)  These deprecated functions
 * can take up to three bound arguments in the case of a non-static
 * member function, and four bound arguments in the case of any other
 * function.  In the case of a non-static member function, the
 * referenced object whose member function is to be called must remain
 * in existence until the task has completed.  The target function
 * passed by pointer (or member function pointer) can take a reference
 * to const argument, as a copy of the object to be passed to the
 * argument is taken to avoid dangling references, but it cannot take
 * a reference to non-const argument.
 * 
 * Copying of the return value of the target function or callable
 * object represented by the task may take place.  When a task
 * completes, the return value will be stored, either in a
 * Cgu::AsyncResult object (if TaskManager::make_task_result() is
 * called) or in the shared state of a std::packaged_task object (if
 * TaskManager::make_task_packaged(),
 * TaskManager::make_task_packaged_when() or
 * TaskManager::make_task_packaged_compose() is called) or for the
 * purposes of executing the 'when' callback in a glib main loop (if
 * TaskManager::make_task_when(), TaskManager::make_task_when_full()
 * or TaskManager::make_task_compose() are called).  This storage will
 * therefore cause the return value type's assignment operator or copy
 * constructor to be called once unless that type has a move
 * assignment operator or move constructor, in which case a move
 * operation will be made where possible.  Note that a 'when' callback
 * takes the stored return value by reference and so without any
 * additional copying upon the 'when' callback being executed in the
 * main loop.
 *
 * TaskManager objects do not provide thread cancellation.  Thread
 * cancellation is incompatible with the task-centred thread pool
 * model.  If task cancellation is wanted, use a Cgu::Thread::Future
 * (or Cgu::Thread::Thread or Cgu::Thread::JoinableHandle) object
 * instead, and have a dedicated thread for the cancelable task.
 *
 * If glib < 2.32 is installed, g_thread_init() must be called before
 * any TaskManager objects are constructed, which in turn means that
 * with glib < 2.32 TaskManager objects may not be constructed as
 * static objects in global namespace (that is, before g_thread_init()
 * has been called in the program).
 *
 * Any exceptions which propagate from a task will be consumed to
 * protect the TaskManager object, and to detect whether this has
 * happened there is a version of the TaskManager::add_task() method
 * which takes a second argument comprising a 'fail' callback.  If an
 * exception propagates from the 'fail' callback that is also consumed
 * and a g_critical() message issued.
 * TaskManager::make_task_when_full() also provides for a 'fail'
 * callback.  TaskManager::make_task_packaged(),
 * TaskManager::make_task_packaged_when() and
 * TaskManager::make_task_packaged_compose() instead store an
 * exception thrown by a task in the shared state of a
 * std::packaged_task object, so that it is assessible from the
 * associated std::future object.
 *
 * Tasks can be aborted by throwing Cgu::Thread::Exit (as well as any
 * other exception).  Where a task is managed by a TaskManager object,
 * throwing Cgu::Thread::Exit will only terminate the task and not the
 * thread on which it is running (and will cause the 'fail' callback
 * to be executed, if there is one).
 *
 * Any 'fail' callback passed to TaskManager::add_task() or
 * TaskManager::make_task_when_full() must be fully bound.  Whilst a
 * task can pass error status to the 'fail' callback via shared data
 * bound to both the task and the 'fail' callback (held by, say, a
 * SharedLockPtr object), or a global error stack, 'fail' callbacks
 * are generally best reserved for use with entirely unexpected
 * exceptions, where the most reasonable course is to perform some
 * orderly logging and shutdown.  For handlable exceptions, in an
 * asynchronous environment the best course is often to catch them and
 * deal with them in the task itself and (where
 * TaskManager::make_task_when_full(), TaskManager::make_task_when()
 * or TaskManager::make_task_compose() is employed) return a value of
 * the task function's return type indicating no result.
 * Alternatively, as mentioned above,
 * TaskManager::make_task_packaged(),
 * TaskManager::make_task_packaged_when() and
 * TaskManager::make_task_packaged_compose() store an exception thrown
 * by a task in the shared state of a std::packaged_task object
 *
 * TaskManager objects have no copy constructor or copy assignment
 * operator, as copying them would have no obvious semantic meaning.
 * Whilst swapping or moving TaskManager objects would be meaningful,
 * this is not implemented either because it would require an
 * additional internal lock to be thread safe, and the circumstances
 * in which moving or swapping would be useful are limited.  Where a
 * move option is wanted, a TaskManager object can be constructed on
 * free store and held by std::unique_ptr.
 *
 * Here is a compilable example of the calculator class referred to in
 * the documentation on the AsyncResult but which uses a TaskManager
 * object so that the calculator class can run more than one thread to
 * service its calculations, using TaskManager::make_task_result():
 *
 * @code 
 *   #include <vector>
 *   #include <numeric>
 *   #include <ostream>
 *   #include <iostream>
 *
 *   #include <glib.h>
 *
 *   #include <c++-gtk-utils/task_manager.h>
 *   #include <c++-gtk-utils/async_result.h>
 *   #include <c++-gtk-utils/shared_ptr.h>
 *
 *   using namespace Cgu;
 *
 *   class Calcs {
 *     Thread::TaskManager tm;
 *   public:
 *     SharedLockPtr<AsyncResult<double>> mean(const std::vector<double>& nums) {
 *       return tm.make_task_result([=]() -> double {
 *           if (nums.empty()) return 0.0;
 *	     return std::accumulate(nums.begin(), nums.end(), 0.0)/nums.size();
 *         });
 *     }
 *
 *     // ... other calculation methods here
 *   };
 *
 *   int main () {
 *
 *     g_thread_init(0);
 *     Calcs calcs;
 *     auto res1 = calcs.mean({1, 2, 8, 0});
 *     auto res2 = calcs.mean({101, 53.7, 87, 1.2});
 *
 *     // ... do something else
 *     std::cout << res1->get() << std::endl;
 *     std::cout << res2->get() << std::endl;
 *
 *   }
 * @endcode
 *
 * The same could be done using TaskManager::make_task_packaged() as follows:
 *
 * @code 
 *   #include <vector>
 *   #include <numeric>
 *   #include <ostream>
 *   #include <iostream>
 *   #include <future>
 *
 *   #include <glib.h>
 *
 *   #include <c++-gtk-utils/task_manager.h>
 *   #include <c++-gtk-utils/async_result.h>
 *   #include <c++-gtk-utils/shared_ptr.h>
 *
 *   using namespace Cgu;
 *
 *   class Calcs {
 *     Thread::TaskManager tm;
 *   public:
 *     std::future<double> mean(const std::vector<double>& nums) {
 *       return tm.make_task_packaged([=]() -> double {
 *           if (nums.empty()) return 0.0;
 *	     return std::accumulate(nums.begin(), nums.end(), 0.0)/nums.size();
 *         });
 *     }
 *
 *     // ... other calculation methods here
 *   };
 *
 *   int main () {
 *
 *     g_thread_init(0);
 *     Calcs calcs;
 *     auto res1 = calcs.mean({1, 2, 8, 0});
 *     auto res2 = calcs.mean({101, 53.7, 87, 1.2});
 *
 *     // ... do something else
 *     std::cout << res1.get() << std::endl;
 *     std::cout << res2.get() << std::endl;
 *
 *   }
 * @endcode
 *
 * Here is a reimplementation, using TaskManager::make_task_when(), of
 * the example using a get_primes() function given in the
 * documentation for Cgu::Thread::Future:
 * @code 
 *   std::vector<long> get_primes(int n); // calculates the first n primes
 *
 *   // get the first 1,000 primes
 *   using namespace Cgu;
 *
 *   Thread::TaskManager tm;
 *   tm.make_task_when([] (const std::vector<long>& result) {
 *                       for (const auto& elt: result) {std::cout << elt << std::endl;}
 *                     },
 *                     0,                    // default main loop context
 *                     [] () {return get_primes(1000);});
 * @endcode
 *
 * Where a task running on a TaskManager object is to block, the
 * TaskManager::IncHandle scoped handle class can be used to increment
 * the maximum number of threads running on the object's thread pool
 * temporarily while blocking takes place, so as to enable another
 * thread to keep a core active.  This can be useful where a task is
 * to 'join' on another task when composing tasks: and it is usually
 * essential to increment the maximum thread count temporarily where a
 * task is to block on one of its sub-tasks, to avoid any possibility
 * of deadlock through thread starvation (thread starvation occurs
 * where all threads on a thread pool are occupied by tasks blocking
 * on sub-tasks which have still to run on the thread pool, and which
 * cannot run because the maximum thread count has been reached).
 * Here is a compilable example:
 *
 * @code
 *   #include <iostream>
 *   #include <ostream>
 *
 *   #include <glib.h>
 *
 *   #include <c++-gtk-utils/task_manager.h>
 *
 *   using namespace Cgu;
 *
 *   // simulate a blocking operation, say from a server, with g_usleep()
 *   int mult(int x, int y) {
 *     g_usleep(100000);
 *     return x * y;
 *   }
 *
 *   int main(int argc, char* argv[]) {
 *
 *     g_thread_init(0);
 *     Thread::TaskManager tm{1}; // only one thread available unless blocking!
 *     GMainLoop* loop = g_main_loop_new(0, true);
 *
 *     tm.make_task_when(
 *       [loop] (const int& res) {
 *         std::cout << res << std::endl;
 *         g_main_loop_quit(loop);
 *       },
 *       0, // default main loop
 *       [&tm] () -> int {
 *         // this task multiplies 'a' by 2 and 'b' by 3, and adds the products 
 *         int a = 10;
 *         int b = 12;
 *
 *         // increment maximum thread count before launching sub-task and
 *         // then blocking
 *         Thread::TaskManager::IncHandle h{tm};
 *         // start a sub-task
 *         auto sub = tm.make_task_result([a, &tm] () -> int {
 *           // increment maximum thread count again before blocking in
 *           // this task (pretend that some other task in the program
 *           // may also want to run while both the parent task and this
 *           // task block on mult())
 *           Thread::TaskManager::IncHandle h{tm};
 *           return mult(a, 2);
 *         });
 *
 *         int res = mult(b, 3)
 *         return sub->get() + res;
 *       }
 *     );
 *  
 *     g_main_loop_run(loop);
 *   }
 * @endcode
 *
 * An alternative to using TaskManager::IncHandle for sub-tasks is to
 * run the sub-tasks on their own threads via Thread::Future or
 * std::async().
 *
 * Rate limiting
 * -------------
 *
 * Resources are not infinite and there is a hard limit to the number
 * of tasks that a TaskManager object may have queued for execution at
 * any one time.  From version 2.2.14 the limit to the number of
 * running and queued tasks may be obtained by calling the
 * TaskManager::get_max_tasks() method (note that particularly on
 * 32-bit systems it will in practice be impossible to reach this
 * limit because of memory exhaustion: the value returned by that
 * method represents the limit enforced by TaskManager irrespective of
 * the actual available memory at the system or process level and any
 * intervention of std::bad_alloc exceptions).  If that limit is
 * exceeded, the TaskManager::add_task() and TaskManager::make_task_*
 * methods will throw std::length_error.
 *
 * In practice however, on most systems such a large number of queued
 * tasks (normally around 4,294,967,295 for 64-bit systems) is likely
 * to be unfeasible and program logic constraints will be exceeded
 * long before the limit is reached, even if available memory is not.
 * In some usages therefore, some form of rate limiting may be needed
 * to prevent a work-producing thread overwhelming a TaskManager
 * object by continuously adding more tasks for execution than the
 * object's throughput capacity is capable of dealing with, so pushing
 * the number of unexecuted tasks to an excessive level.
 *
 * TaskManager objects provide no built-in rate limiting (other than
 * throwing the std::length_error exception referred to above).  This
 * is because there is no one-size-fits-all way of doing so.  One
 * common approach is to apply throttling to threads which add tasks
 * by enforcing a wait in their thread of execution when the level of
 * queued tasks reaches too high a level, so hindering their ability
 * to add new ones.  However this is counter productive where it is a
 * task running on the TaskManager object which is adding the new
 * tasks, particularly with a TaskManager object having only a few
 * threads running in its pool.  Another approach is to throw an
 * exception when adding tasks which exceed a user-selectable level
 * which is much lower than the value returned by
 * TaskManager::get_max_tasks(), but this would make it more difficult
 * to use the Cgu::Thread::parallel_for_each() and
 * Cgu::Thread::parallel_transform() functions provided by this
 * library.
 *
 * The best approach is for user code to provide its own rate limiting
 * in cases where the way that that code is organised means that it
 * could produce an excessive number of accumulating unexecuted tasks,
 * possibly by applying delays when unexecuted tasks rise in number
 * excessively, using timeouts with a glib main loop.  This may be
 * checked for by having code call the TaskManager::get_tasks() method
 * before adding a significant batch of new tasks in order to test
 * queue size, and if necessary postpone adding the new tasks until
 * the size of the already accumulated tasks has reduced.
 *
 * The Cgu::AsyncChannel class has emplace() and push() methods which
 * will block when a channel is full.  That class can therefore be
 * used for rate limiting function objects pushed onto the channel in
 * cases where that is an appropriate alternative solution to
 * TaskManager.
 */

#ifndef DOXYGEN_PARSING
// TODO: this is a work-around for gcc < 4.7, which has a bug which
// requires a function whose return value is determined by decltype,
// such as make_task_result(Func&&), to be inline.  At a suitable
// API/ABI break when gcc requirements are updated, this should be
// moved to task_manager.tpp.
namespace TaskManagerHelper2 {

template <class Ret, class FType>
struct FunctorResultWrapper {
  static void exec(FType& f,
		   const SharedLockPtr<AsyncResult<Ret>>& ret) {
    ret->set(f());
  }
  static void do_fail(const SharedLockPtr<AsyncResult<Ret>>& ret) {
    ret->set_error(); // won't throw
  }
};

/* 
 * The FunctorResultExec class is a specialised class which is
 * necessary because the 'functor' member needs to be declared mutable
 * so that it can bind to the reference to non-const argument of
 * FunctorResultWrapper::exec(), and thus so that a mutable lambda can
 * be executed by that function.  Because it is so specialised, it is
 * not suitable for inclusion in the generic interfaces provided in
 * callback.h.  (Except in this specialised usage, it can also be
 * dangerous, as it allows a member of the callback object to be
 * mutated: normally this would be undesirable.)  An alternative would
 * have been to put the 'functor' member in a wrapper struct like
 * MemfunWhenWrapperArgs or FunWhenWrapperArgs, but if 'functor' were
 * an lvalue that would mean it being copied twice.  This is the most
 * efficient implementation.
 */
template <class Ret, class FType>
class FunctorResultExec: public Cgu::Callback::Callback {
  mutable FType functor;
  SharedLockPtr<AsyncResult<Ret>> ret;
public:
  void dispatch() const {FunctorResultWrapper<Ret, FType>::exec(functor, ret);}
  // we don't need to templatize 'ret_' for perfect forwarding - it is
  // always passed as a lvalue
  template <class FunctorArg>
  FunctorResultExec(FunctorArg&& functor_,
		    const SharedLockPtr<AsyncResult<Ret>>& ret_): functor(std::forward<FunctorArg>(functor_)),
								  ret(ret_) {}
};

} // namespace TaskManagerHelper2
#endif // DOXYGEN_PARSING


class TaskManager {
 public:
  enum StopMode {wait_for_running, wait_for_all}; 
  class IncHandle;
 private:
  typedef std::pair<std::unique_ptr<const Callback::Callback>,
                    std::unique_ptr<const Callback::Callback>> QueueItemType;

  struct RefImpl; // reference counted implementation class
  // it is fine holding RefImpl by plain pointer and not by
  // IntrusivePtr: it is the only data member this class has, so it
  // can safely manage that member in its own destructor and other
  // methods
  RefImpl* ref_impl;

  void set_max_threads_impl(unsigned int max, Mutex::TrackLock& lock);
  static unsigned int init_max_tasks();
 public:
/**
 * This class cannot be copied.  The copy constructor is deleted.
 */
  TaskManager(const TaskManager&) = delete;

/**
 * This class cannot be copied.  The assignment operator is deleted.
 */
  TaskManager& operator=(const TaskManager&) = delete;

 /**
  * Gets the maximum number of threads which the TaskManager object is
  * currently set to run in the thread pool. This value is established
  * initially by the 'max' argument passed to the TaskManager
  * constructor and can subequently be changed by calling
  * set_max_threads() or change_max_threads().  The default value is 8.
  * This method will not throw and is thread safe.  However, if a
  * blocking task might use the TaskManager::IncHandle class (or
  * increase and then decrease the number by hand by calling
  * change_max_threads()), this method will not usually be useful.
  * @return The maximum number of threads.
  *
  * Since 2.0.12
  */
  unsigned int get_max_threads() const;

 /**
  * Gets the minimum number of threads which the TaskManager object
  * will run in the thread pool (these threads will last until
  * stop_all() is called or the TaskManager object is destroyed).
  * This value is established by the 'min' argument passed to the
  * TaskManager constructor and cannot subequently be changed.  The
  * default is 0.  This method will not throw and is thread safe.
  * @return The minimum number of threads.
  *
  * Since 2.0.12
  */
  unsigned int get_min_threads() const;

 /**
  * Gets the number of threads which the TaskManager object is
  * currently running in the thread pool, including those blocking
  * waiting for a task.  This value could be greater than the number
  * returned by get_max_threads() if change_max_threads() has recently
  * been called with a negative number but not enough tasks have since
  * completed to reduce the number of running threads to the new value
  * set.  This method will not throw and is thread safe.
  * @return The number of threads running in the thread pool,
  * including those blocking waiting for a task.
  *
  * Since 2.0.12
  */
  unsigned int get_used_threads() const;

 /**
  * @deprecated
  *
  * DEPRECATED.  Use change_max_threads() instead.  This method will
  * interfere with the intended operation of the
  * ThreadManager::IncHandle class if one task constructs a IncHandle
  * object and another calls this method.
  *
  * Sets the maximum number of threads which the TaskManager object
  * will currently run in the thread pool.  If this is less than the
  * current number of running threads, the number of threads actually
  * running will only be reduced as tasks complete, or as idle
  * timeouts expire.  This method does nothing if stop_all() has
  * previously been called.  This method is thread safe.
  * @param max The maximum number of threads which the TaskManager
  * object will currently run in the thread pool.  This method will
  * not set the maximum value of threads to a value less than that
  * returned by get_min_threads(), nor to a value less than 1.
  * @exception std::bad_alloc If this call is passed a value for 'max'
  * which increases the maximum number of threads from its previous
  * setting and tasks are currently queued for execution, new threads
  * will be started for the queued tasks, so this exception may be
  * thrown on starting the new threads if memory is exhausted and the
  * system throws in that case.  (On systems with
  * over-commit/lazy-commit combined with virtual memory (swap), it is
  * rarely useful to check for memory exhaustion).
  * @exception Cgu::Thread::TaskError If this call is passed a value
  * for 'max' which increases the maximum number of threads from its
  * previous setting and tasks are currently queued for execution, new
  * threads will be started for the queued tasks, so this exception
  * may be thrown on starting the new threads if a thread fails to
  * start correctly (this would mean that memory is exhausted, the
  * pthread thread limit has been reached or pthread has run out of
  * other resources to start new threads).
  *
  * Since 2.0.12
  */
  void set_max_threads(unsigned int max);

 /**
  * This will increase, or if 'delta' is negative reduce, the maximum
  * number of threads which the TaskManager object will currently run
  * in the thread pool by the value of 'delta'.  The purpose of this
  * is to enable a task to increment the maximum thread number where
  * it is about to enter a call which may block for some time, with a
  * view to decrementing it later when it has finished making blocking
  * calls, so as to enable another thread to keep a core active.  If
  * 'delta' is negative and results in a max_threads value of less
  * than the current number of running threads, the number of threads
  * actually running will only be reduced as tasks complete, or as
  * idle timeouts expire.  This method does nothing if stop_all() has
  * previously been called.  This method is thread safe.  Since
  * version 2.2.1, the scoped handle class TaskManager::IncHandle is
  * available which calls this method.
  * @param delta The change (positive or negative) to the maximum
  * number of threads which the TaskManager object will currently run
  * in the thread pool.  This method will not set the maximum value of
  * threads to a value less than that returned by get_min_threads(),
  * nor to a value less than 1.
  * @exception std::bad_alloc If this call is passed a positive value
  * and tasks are currently queued for execution, a new thread or
  * threads will be started for the queued tasks, so this exception
  * may be thrown on starting a new thread if memory is exhausted and
  * the system throws in that case.  (On systems with
  * over-commit/lazy-commit combined with virtual memory (swap), it is
  * rarely useful to check for memory exhaustion).
  * @exception Cgu::Thread::TaskError If this call is passed a
  * positive value and tasks are currently queued for execution, a new
  * thread or threads will be started for the queued tasks, so this
  * exception may be thrown on starting a new thread if it fails to
  * start correctly (this would mean that memory is exhausted, the
  * pthread thread limit has been reached or pthread has run out of
  * other resources to start new threads).
  *
  * Since 2.0.14
  */
  void change_max_threads(int delta);

 /**
  * Gets the number of tasks which the TaskManager object is at
  * present either running in the thread pool or has queued for
  * execution.  This value will be less than the number returned by
  * get_used_threads() if threads in the thread pool are currently
  * waiting to receive tasks for execution.  This method will not
  * throw and is thread safe.
  * @return The number of tasks either running or queued for
  * execution.
  *
  * Since 2.0.12
  */
  unsigned int get_tasks() const;

 /**
  * Gets the limit to the sum of the number of tasks which a
  * TaskManager object may have running in the thread pool or queued
  * for execution at any one time.  On a 32-bit system, reaching this
  * limit will normally cause the amount of memory which any process
  * may allocate to be exceeded so the limit will in practice never be
  * met (the add_task() or make_task_* methods will throw a
  * std::bad_alloc exception before then).  On a 64-bit system this
  * limit will normally be the same as UINT_MAX (4,294,967,295 for a
  * 32-bit unsigned int) which although likely to be unfeasibly large
  * could in theory be reached with a system which can make around
  * 70GB of memory available to the process for the TaskManager
  * object.  The add_task() and make_task_* methods will throw
  * std::length_error if an attempt is made to exceed this limit and
  * std::bad_alloc has not got there first.
  *
  * This method is thread safe.
  *
  * @return The maximum number of tasks which a TaskManager object may
  * have either running or queued for execution at any one time.
  * @exception std::bad_alloc This exception may be thrown the first
  * time this method is called.  Any subsequent calls will not throw
  * if an earlier one did not (this method calculates the limit once
  * only and then caches it using static local initialization).
  *
  * Since 2.0.31 and 2.2.14
  */
  static unsigned int get_max_tasks() {
    // having max_tasks as a local static rather than a namespace
    // static means that we cannot suffer from the static
    // initialization order fiasco.  Static local initialization is
    // also guaranteed to be thread safe in C++11 on.  Because this
    // function just calls up a local static, we might as well make it
    // inline.
    static unsigned int max_tasks = init_max_tasks();
    return max_tasks;
  }

 /**
  * Gets the length of time in milliseconds that threads greater in
  * number than the minimum and not executing any tasks will remain in
  * existence waiting for new tasks.  This value is established
  * initially by the 'idle' argument passed to the TaskManager
  * constructor and can subequently be changed by calling
  * set_idle_time().  The default value is 10000 (10 seconds).  This
  * method will not throw and is thread safe.
  * @return The idle time in milliseconds.
  *
  * Since 2.0.12
  */
  unsigned int get_idle_time() const;

 /**
  * Sets the length of time in milliseconds that threads greater in
  * number than the minimum and not executing any tasks will remain in
  * existence waiting for new tasks.  This will only have effect for
  * threads in the pool which begin waiting for new tasks after this
  * method is called.  This method will not throw and is thread safe.
  * @param idle The length of the idle time in milliseconds during
  * which threads will remain waiting for new tasks.
  *
  * Since 2.0.12
  */
  void set_idle_time(unsigned int idle);

 /**
  * Gets the current blocking setting, which determines whether calls
  * to stop_all() and the destructor will block waiting for all
  * remaining tasks to complete.  This value is established initially
  * by the 'blocking' argument passed to the TaskManager constructor
  * and can subequently be changed by calling set_blocking().  This
  * method will not throw and is thread safe.
  * @return The current blocking setting.
  *
  * Since 2.0.12
  */
  bool get_blocking() const;

 /**
  * Sets the current blocking setting, which determines whether calls
  * to stop_all() and the destructor will block waiting for all
  * remaining tasks to complete.  This method cannot be called after
  * stop_all() has been called (if that is attempted,
  * Cgu::Thread::TaskError will be thrown).  It is thread safe.
  * @param blocking The new blocking setting.
  * @exception Cgu::Thread::TaskError This exception will be thrown if
  * stop_all() has previously been called.
  *
  * Since 2.0.12
  */
  void set_blocking(bool blocking);

 /**
  * Gets the current StopMode setting (either
  * Cgu::Thread::TaskManager::wait_for_running or
  * Cgu::Thread::TaskManager::wait_for_all) executed when running
  * stop_all() or when the destructor is called.  See the
  * documentation on stop_all() for an explanation of the setting.
  * This value is established initially by the 'mode' argument passed
  * to the TaskManager constructor and can subequently be changed by
  * calling set_stop_mode().  This method will not throw and is thread
  * safe.
  * @return The current StopMode setting.
  *
  * Since 2.0.12
  */
  StopMode get_stop_mode() const;

 /**
  * Sets the current StopMode setting (either
  * Cgu::Thread::TaskManager::wait_for_running or
  * Cgu::Thread::TaskManager::wait_for_all) executed when running
  * stop_all() or when the destructor is called.  See the
  * documentation on stop_all() for an explanation of the setting.
  * This method will not throw and is thread safe.
  * @param mode The new StopMode setting.
  *
  * Since 2.0.12
  */
  void set_stop_mode(StopMode mode);

 /**
  * This will cause the TaskManager object to stop running tasks.  The
  * precise effect depends on the current StopMode and blocking
  * settings.  If StopMode is set to
  * Cgu::Thread::TaskManager::wait_for_running, all queued tasks which
  * are not yet running on a thread will be dispensed with, but any
  * already running will be left to complete normally.  If StopMode is
  * set to Cgu::Thread::TaskManager::wait_for_all, both already
  * running tasks and all tasks already queued will be permitted to
  * execute and complete normally.  If the blocking setting is set to
  * true, this method will wait until all the tasks still to execute
  * have finished before returning, and if false it will return
  * straight away.
  *
  * The StopMode setting should not be set to
  * Cgu::Thread::TaskManager::wait_for_running if, when this method is
  * called, another thread may be waiting on the get() or move_get()
  * method of a Cgu::AsyncResult object returned by
  * Cgu::Thread::TaskManager::make_task_result(), as otherwise that
  * wait may never end - choose the
  * Cgu::Thread::TaskManager::wait_for_all setting instead in such
  * cases.  This restriction does not apply to a thread waiting on a
  * std::future object returned by
  * Cgu::Thread::TaskManager::make_task_packaged(): instead
  * std::future::get() will unblock and throw an exception upon the
  * associated std::packaged_task object being destroyed as unexecuted
  * queued tasks are dispensed with.
  *
  * After this method has been called, any attempt to add further
  * tasks with the add_task() method will fail, and add_task() will
  * throw Cgu::Thread::TaskError.
  *
  * This method is thread safe (any thread may call it) unless the
  * blocking setting is true, in which case no task running on the
  * TaskManager object may call this method.
  *
  * @exception std::bad_alloc This exception will be thrown if memory
  * is exhausted and the system throws in that case.  (On systems with
  * over-commit/lazy-commit combined with virtual memory (swap), it is
  * rarely useful to check for memory exhaustion).
  * @exception Cgu::Thread::TaskError This exception will be thrown if
  * stop_all() has previously been called, unless that previous call
  * threw std::bad_alloc: if std::bad_alloc is thrown, this method may
  * be called again to stop all threads, once the memory deficiency is
  * dealt with, but no other methods of the TaskManager object should
  * be called.
  *
  * Since 2.0.12
  */
  void stop_all();

 /**
  * This method adds a new task.  If one or more threads in the pool
  * are currently blocking and waiting for a task, then the task will
  * begin executing immediately in one of the threads.  If not, and
  * the value returned by get_used_threads() is less than the value
  * returned by get_max_threads(), a new thread will start and the
  * task will execute immediately in the new thread.  Otherwise, the
  * task will be queued for execution as soon as a thread becomes
  * available.  Tasks will be executed in the order in which they are
  * added to the ThreadManager object.  This method is thread safe
  * (any thread may call it, including any task running on the
  * TaskManager object).
  *
  * A task may terminate itself prematurely by throwing
  * Cgu::Thread::Exit.  In addition, the implementation of TaskManager
  * will consume any other exception escaping from the task callback
  * and safely terminate the task concerned in order to protect the
  * integrity of the TaskManager object.  Where detecting any of these
  * outcomes is important (usually it won't be), the two argument
  * version of this method is available so that a 'fail' callback can
  * be executed in these circumstances.
  *
  * @param task A callback representing the new task, as constructed
  * by the Callback::lambda(), Callback::make() or
  * Callback::make_ref() factory functions.  Ownership is taken of
  * this callback, and it will be disposed of when it has been
  * finished with.  If an exception propagates from the task, the
  * exception will be consumed and (if the thrown object's type is not
  * Cgu::Thread::Exit) a g_critical() warning will be issued.  The
  * destructors of any bound arguments in the callback must not throw.
  * @exception std::bad_alloc This exception will be thrown if memory
  * is exhausted and the system throws in that case.  (On systems with
  * over-commit/lazy-commit combined with virtual memory (swap), it is
  * rarely useful to check for memory exhaustion).  See also the
  * documentation for the get_max_tasks() method about the possibility
  * of std::length_error being thrown.  If std::bad_alloc or
  * std::length_error is thrown, the task will not start and the
  * 'task' callback will be disposed of.
  * @exception Cgu::Thread::TaskError This exception will be thrown if
  * stop_all() has previously been called.  It will also be thrown if
  * this method tries but fails to start a new thread, or if
  * is_error() would return true because this class's internal thread
  * pool loop implementation has thrown std::bad_alloc or a thread has
  * previously failed to start correctly.  If this exception is
  * thrown, the task will not start and the 'task' callback will be
  * disposed of.
  * 
  * Since 2.0.12
  */
  void add_task(const Callback::Callback* task) {
    add_task(std::unique_ptr<const Callback::Callback>(task),
	     std::unique_ptr<const Callback::Callback>());
  }

 /**
  * This method adds a new task.  If one or more threads in the pool
  * are currently blocking and waiting for a task, then the task will
  * begin executing immediately in one of the threads.  If not, and
  * the value returned by get_used_threads() is less than the value
  * returned by get_max_threads(), a new thread will start and the
  * task will execute immediately in the new thread.  Otherwise, the
  * task will be queued for execution as soon as a thread becomes
  * available.  Tasks will be executed in the order in which they are
  * added to the ThreadManager object.  This method is thread safe
  * (any thread may call it, including any task running on the
  * TaskManager object).
  *
  * A task may terminate itself prematurely by throwing
  * Cgu::Thread::Exit.  In addition, the implementation of TaskManager
  * will consume any other exception escaping from the task callback
  * and safely terminate the task concerned in order to protect the
  * integrity of the TaskManager object.  Where detecting any of these
  * outcomes is important (usually it won't be), a callback can be
  * passed to the 'fail' argument which will execute if, and only if,
  * either Cgu::Thread::Exit is thrown or some other exception has
  * propagated from the task.  This 'fail' callback is different from
  * the 'fail' callback of Cgu::Thread::Future objects (programming
  * for many tasks to a lesser number of threads requires different
  * approaches from programming for one thread per task), and it
  * executes in the task thread rather than executing in a glib main
  * loop (however, the 'fail' callback can of course call
  * Cgu::Callback::post() to execute another callback in a main loop,
  * if that is what is wanted).
  *
  * @param task A callback representing the new task, as constructed
  * by the Callback::lambda(), Callback::make() or
  * Callback::make_ref() factory functions.  If an exception
  * propagates from the task, the exception will be consumed and the
  * 'fail' callback will execute.
  * @param fail A callback (as constructed by the Callback::lambda(),
  * Callback::make() or Callback::make_ref() factory functions) which
  * will be executed if the function or callable object executed by
  * the 'task' callback exits by throwing Thread::Exit or some other
  * exception.  If an exception propagates from the 'fail' callback,
  * this will be consumed to protect the TaskManager object, and a
  * g_critical() warning will be issued.
  * @exception std::bad_alloc This exception will be thrown if memory
  * is exhausted and the system throws in that case.  (On systems with
  * over-commit/lazy-commit combined with virtual memory (swap), it is
  * rarely useful to check for memory exhaustion).  See also the
  * documentation for the get_max_tasks() method about the possibility
  * of std::length_error being thrown.  If std::bad_alloc or
  * std::length_error is thrown, the task will not start (which also
  * means that the 'fail' callback will not execute).
  * @exception Cgu::Thread::TaskError This exception will be thrown if
  * stop_all() has previously been called.  It will also be thrown if
  * this method tries but fails to start a new thread, or if
  * is_error() would return true because this class's internal thread
  * pool loop implementation has thrown std::bad_alloc or a thread has
  * previously failed to start correctly.  If this exception is
  * thrown, the task will not start (which also means that the 'fail'
  * callback will not execute).
  *
  * Since 2.0.12
  */
  void add_task(std::unique_ptr<const Callback::Callback> task,
		std::unique_ptr<const Callback::Callback> fail);

 /**
  * This method adds a new task.  If one or more threads in the pool
  * are currently blocking and waiting for a task, then the task will
  * begin executing immediately in one of the threads.  If not, and
  * the value returned by get_used_threads() is less than the value
  * returned by get_max_threads(), a new thread will start and the
  * task will execute immediately in the new thread.  Otherwise, the
  * task will be queued for execution as soon as a thread becomes
  * available.  Tasks will be executed in the order in which they are
  * added to the ThreadManager object.  This method is thread safe
  * (any thread may call it, including any task running on the
  * TaskManager object).
  *
  * A task may terminate itself prematurely by throwing
  * Cgu::Thread::Exit.  In addition, the implementation of TaskManager
  * will consume any other exception escaping from the task callback
  * and safely terminate the task concerned in order to protect the
  * integrity of the TaskManager object.  Where detecting any of these
  * outcomes is important (usually it won't be), the two argument
  * version of this method is available so that a 'fail' callback can
  * be executed in these circumstances.
  *
  * @param task A callable object representing the new task, such as
  * formed by a lambda expression or the result of std::bind.  It must
  * be fully bound (that is, it must take no arguments when called).
  * If an exception propagates from the task, the exception will be
  * consumed and (if the thrown object's type is not
  * Cgu::Thread::Exit) a g_critical() warning will be issued.  The
  * destructors of any bound values must not throw.
  * @exception std::bad_alloc This exception will be thrown if memory
  * is exhausted and the system throws in that case.  (On systems with
  * over-commit/lazy-commit combined with virtual memory (swap), it is
  * rarely useful to check for memory exhaustion).  See also the
  * documentation for the get_max_tasks() method about the possibility
  * of std::length_error being thrown.  If std::bad_alloc or
  * std::length_error is thrown, the task will not start.
  * @exception Cgu::Thread::TaskError This exception will be thrown if
  * stop_all() has previously been called.  It will also be thrown if
  * this method tries but fails to start a new thread, or if
  * is_error() would return true because this class's internal thread
  * pool loop implementation has thrown std::bad_alloc or a thread has
  * previously failed to start correctly.  If this exception is
  * thrown, the task will not start.
  * @note An exception might also be thrown if the copy or move
  * constructor of the callable object throws.  If such an exception
  * is thrown, the task will not start.
  * 
  * Since 2.1.0
  */
  // we need to use enable_if so that where this function is passed a
  // pointer to non-const Callback::Callback, or some other
  // convertible pointer, this templated overload is dropped from the
  // overload set, in order to support the Callback::Callback
  // overloads of this function.  This overload calls into the version
  // of this function taking a pointer to const Callback::Callback in
  // order to perform type erasure.
  template <class Task,
	    class = typename std::enable_if<!std::is_convertible<typename std::remove_reference<Task>::type,
	    							 const Callback::Callback*>::value>::type>
  void add_task(Task&& task) {
    add_task(std::unique_ptr<const Callback::Callback>(Callback::lambda<>(std::forward<Task>(task))),
	     std::unique_ptr<const Callback::Callback>());
 }

 /**
  * This method adds a new task.  If one or more threads in the pool
  * are currently blocking and waiting for a task, then the task will
  * begin executing immediately in one of the threads.  If not, and
  * the value returned by get_used_threads() is less than the value
  * returned by get_max_threads(), a new thread will start and the
  * task will execute immediately in the new thread.  Otherwise, the
  * task will be queued for execution as soon as a thread becomes
  * available.  Tasks will be executed in the order in which they are
  * added to the ThreadManager object.  This method is thread safe
  * (any thread may call it, including any task running on the
  * TaskManager object).
  *
  * A task may terminate itself prematurely by throwing
  * Cgu::Thread::Exit.  In addition, the implementation of TaskManager
  * will consume any other exception escaping from the task callback
  * and safely terminate the task concerned in order to protect the
  * integrity of the TaskManager object.  Where detecting any of these
  * outcomes is important (usually it won't be), a callback can be
  * passed to the 'fail' argument which will execute if, and only if,
  * either Cgu::Thread::Exit is thrown or some other exception has
  * propagated from the task.  This 'fail' callback is different from
  * the 'fail' callback of Cgu::Thread::Future objects (programming
  * for many tasks to a lesser number of threads requires different
  * approaches from programming for one thread per task), and it
  * executes in the task thread rather than executing in a glib main
  * loop (however, the 'fail' callback can of course call
  * Cgu::Callback::post() to execute another callback in a main loop,
  * if that is what is wanted).
  *
  * @param task A callable object representing the new task, such as
  * formed by a lambda expression or the result of std::bind.  It must
  * be fully bound (that is, it must take no arguments when called).
  * The destructors of any bound values must not throw.  If an exception
  * propagates from the task, the exception will be consumed and the
  * 'fail' callback will execute.
  * @param fail A callable object (such as formed by a lambda
  * expression or the result of std::bind) which will be executed if
  * the callable object represented by the 'task' callback exits by
  * throwing Thread::Exit or some other exception.  It must be fully
  * bound (that is, it must take no arguments when called).  The
  * destructors of any bound values must not throw.  If an exception
  * propagates from the 'fail' callback, this will be consumed to
  * protect the TaskManager object, and a g_critical() warning will be
  * issued.
  * @exception std::bad_alloc This exception will be thrown if memory
  * is exhausted and the system throws in that case.  (On systems with
  * over-commit/lazy-commit combined with virtual memory (swap), it is
  * rarely useful to check for memory exhaustion).  See also the
  * documentation for the get_max_tasks() method about the possibility
  * of std::length_error being thrown.  If std::bad_alloc or
  * std::length_error is thrown, the task will not start (which also
  * means that the 'fail' callback will not execute).
  * @exception Cgu::Thread::TaskError This exception will be thrown if
  * stop_all() has previously been called.  It will also be thrown if
  * this method tries but fails to start a new thread, or if
  * is_error() would return true because this class's internal thread
  * pool loop implementation has thrown std::bad_alloc or a thread has
  * previously failed to start correctly.  If this exception is
  * thrown, the task will not start (which also means that the 'fail'
  * callback will not execute).
  * @note An exception might also be thrown if the copy or move
  * constructor of the 'task' or 'fail' callable objects throws.  If
  * such an exception is thrown, the task will not start (which also
  * means that the 'fail' callback will not execute)
  *
  * Since 2.1.0
  */
  // we need to use enable_if so that where this function is passed
  // unique_ptr's holding non-const Callback::Callback objects, or
  // some other convertible object, this templated overload is dropped
  // from the overload set, in order to support the unique_ptr
  // overloads of this function.  This overload calls into the version
  // of this function taking Callback objects by unique_ptr in order
  // to perform type erasure.
  template <class Task, class Fail,
            class = typename std::enable_if<!std::is_convertible<Task, std::unique_ptr<const Callback::Callback>>::value
	    				    && !std::is_convertible<Fail, std::unique_ptr<const Callback::Callback>>::value>::type>
  void add_task(Task&& task, Fail&& fail) {
    std::unique_ptr<const Callback::Callback> task_cb(
      Callback::lambda<>(std::forward<Task>(task))
    );
    std::unique_ptr<const Callback::Callback> fail_cb(
      Callback::lambda<>(std::forward<Fail>(fail))
    );
    add_task(std::move(task_cb), std::move(fail_cb));
 }

 /**
  * This will return true if a thread required by the thread pool has
  * failed to start correctly because of memory exhaustion or because
  * pthread has run out of other resources to start new threads, or
  * because an internal operation has thrown std::bad_alloc.  (On
  * systems with over-commit/lazy-commit combined with virtual memory
  * (swap), it is rarely useful to check for memory exhaustion, and
  * even more so where glib is used, as that terminates a program if
  * memory cannot be obtained from the operating system, but there may
  * be some specialized cases where the return value of this method is
  * useful - this class does not use any glib functions which might
  * cause such termination.)  This method will not throw and is thread
  * safe.
  * 
  * Since 2.0.12
  */
  bool is_error() const;

 /**
  * @deprecated
  *
  * DEPRECATED.  Use the versions of make_task_result() which take
  * callable objects.
  *
  * This is a wrapper which takes a member function pointer to a
  * member function which returns a value, together with arguments,
  * and constructs a TaskManager task which will execute that function
  * by calling add_task() with an appropriate callback object, and
  * returns a Cgu::AsyncResult object (held by Cgu::SharedLockPtr)
  * which will provide the value that the function returns.  Apart
  * from the absence of a 'one thread per task' model, this method
  * therefore provides a similar interface to the one provided by
  * Cgu::Thread::Future.  It is thread safe: any thread may call this
  * method, including another task running on the TaskManager object,
  * but see the introductory remarks about the use of the
  * TaskManager::IncHandle scoped handle class where a task running on
  * a TaskManager object is to block on one of its sub-tasks.  See
  * also the documentation on add_task() for further information about
  * how task execution works.
  *
  * This method can take up to three bound arguments for the target
  * member function.
  *
  * If the function passed to this method exits by throwing
  * Thread::Exit or some other exception, then the exception will be
  * consumed and the returned Cgu::AsyncResult object's get() or
  * move_get() method will unblock and its get_error() method will
  * return -1.
  *
  * @param t The object whose member function passed to this method is
  * to execute as a task.
  * @param func The member function to be executed as a task.
  * @param args The arguments to be passed to that member function.
  * @exception std::bad_alloc This exception will be thrown if memory
  * is exhausted and the system throws in that case.  (On systems with
  * over-commit/lazy-commit combined with virtual memory (swap), it is
  * rarely useful to check for memory exhaustion).  See also the
  * documentation for the get_max_tasks() method about the possibility
  * of std::length_error being thrown.  If std::bad_alloc or
  * std::length_error is thrown, the task will not start.
  * @exception Cgu::Thread::TaskError This exception will be thrown if
  * stop_all() has previously been called.  It will also be thrown if
  * the call to add_task() made by this method tries but fails to
  * start a new thread, or if is_error() would return true because
  * this class's internal thread pool loop implementation has thrown
  * std::bad_alloc or a thread has previously failed to start
  * correctly.  If this exception is thrown, the task will not start.
  * @note This method will also throw if the copy or move constructor
  * of a bound argument throws.  If such an exception is thrown, the
  * task will not start.
  *
  * Since 2.0.13
  */

  template <class Ret, class... Params, class... Args, class T>
  Cgu::SharedLockPtr<Cgu::AsyncResult<Ret>> make_task_result(T& t,
							     Ret (T::*func)(Params...),
							     Args&&... args);

 /**
  * @deprecated
  *
  * DEPRECATED.  Use the versions of make_task_when_full() which take
  * callable objects.
  *
  * This is a wrapper which takes a member function pointer to a
  * member function which returns a value, together with arguments,
  * and constructs a TaskManager task which will execute that function
  * by calling add_task() with an appropriate callback object, and
  * causes the 'when' callback passed as an argument to this method to
  * be executed by a glib main loop if and when the task finishes
  * correctly - the 'when' callback is passed the member function's
  * return value when it is invoked.  It is thread safe (any thread
  * may call this method, including another task running on the
  * TaskManager object).  Apart from the absence of a 'one thread per
  * task' model, this method therefore provides a similar interface to
  * the one provided by Cgu::Thread::Future.  See the documentation on
  * add_task() for further information about how task execution works.
  *
  * This method can take up to three bound arguments for the target
  * member function.
  *
  * Note that unlike add_task(), but like the 'fail' callback of
  * Cgu::Thread::Future objects, if a fail callback is provided to
  * this method and it executes, it will execute in the glib main loop
  * whose GMainContext object is passed to the 'context' argument of
  * this method.
  *
  * Note also that if releasers are provided for the 'when' or 'fail'
  * callbacks, these are passed by pointer and not by reference (this
  * is so that a NULL pointer can indicate that no releaser is to be
  * provided).  If provided, a releaser will enable automatic
  * disconnection of the 'when' or 'fail' callback, if the object
  * having the callback function as a member is destroyed. For this to
  * be race free, the lifetime of that object must be controlled by
  * the thread in whose main loop the 'when' or 'fail' callback will
  * execute.
  *
  * The make_task_when() method is similar to this method but provides
  * an abbreviated set of paramaters suitable for most cases.  This
  * method is for use where releasers or a 'fail' callback are
  * required.
  *
  * @param when A callback which will be executed if and when the
  * function passed to this method finishes correctly. The callback is
  * passed that function's return value when it is invoked.  If an
  * exception propagates from the 'when' callback, this will be
  * consumed and a g_critical() warning will be issued.  The callback
  * will execute in the glib main loop whose GMainContext object is
  * passed to the 'context' argument of this method.
  * @param when_releaser A pointer to a Releaser object for automatic
  * disconnection of the 'when' callback before it executes in a main
  * loop (mainly relevant if the callback represents a non-static
  * member function of an object which may be destroyed before the
  * callback executes).  A value of 0/NULL/nullptr indicates no
  * releaser.
  * @param fail A callback which will be executed if the 'when'
  * callback does not execute.  This would happen if the function
  * passed to this method exits by throwing Thread::Exit or some other
  * exception, if the copy constructor of a non-reference argument of
  * that function throws, if the move constructor (or if it has none,
  * the copy constructor) of that function's return value throws or if
  * the 'when' callback does not execute because the internal
  * implementation of this wrapper throws std::bad_alloc (which will
  * not happen if the library has been installed using the
  * \--with-glib-memory-slices-no-compat configuration option: instead
  * glib will terminate the program if it is unable to obtain memory
  * from the operating system).  If an exception propagates from the
  * 'fail' callback, this will be consumed and a g_critical() warning
  * will be issued.  The callback will execute in the glib main loop
  * whose GMainContext object is passed to the 'context' argument of
  * this method.  An empty std::unique_ptr object indicates no 'fail'
  * callback.
  * @param fail_releaser A pointer to a Releaser object for automatic
  * disconnection of the 'fail' callback before it executes in a main
  * loop (mainly relevant if the callback represents a non-static
  * member function of an object which may be destroyed before the
  * callback executes).  A value of 0/NULL/nullptr indicates no
  * releaser.
  * @param priority The priority to be given in the main loop to the
  * 'when' callback or any 'fail' callback. In ascending order of
  * priorities, priorities are G_PRIORITY_LOW,
  * G_PRIORITY_DEFAULT_IDLE, G_PRIORITY_HIGH_IDLE, G_PRIORITY_DEFAULT
  * and G_PRIORITY_HIGH. This determines the order in which the
  * callback will appear in the event list in the main loop, not the
  * priority which the OS will adopt.
  * @param context The glib main context of the main loop in which the
  * 'when' callback or any 'fail' callback is to be executed.  A value
  * 0/NULL/nullptr will cause the callback to be executed in the main
  * program loop.
  * @param t The object whose member function passed to this method is
  * to execute as a task.
  * @param func The member function to be executed as a task.  If an
  * exception propagates from the task, the exception will be consumed
  * and the 'fail' callback will execute.
  * @param args The arguments to be passed to that member function.
  * @exception std::bad_alloc This exception will be thrown if memory
  * is exhausted and the system throws in that case.  (On systems with
  * over-commit/lazy-commit combined with virtual memory (swap), it is
  * rarely useful to check for memory exhaustion).  See also the
  * documentation for the get_max_tasks() method about the possibility
  * of std::length_error being thrown.  If std::bad_alloc or
  * std::length_error is thrown, the task will not start (which also
  * means that the 'when' and 'fail' callbacks will not execute).
  * @exception Cgu::Thread::TaskError This exception will be thrown if
  * stop_all() has previously been called.  It will also be thrown if
  * the call to add_task() made by this method tries but fails to
  * start a new thread, or if is_error() would return true because
  * this class's internal thread pool loop implementation has thrown
  * std::bad_alloc or a thread has previously failed to start
  * correctly.  If this exception is thrown, the task will not start
  * (which also means that the 'when' and 'fail' callbacks will not
  * execute).
  * @note 1. This method will also throw if the copy or move
  * constructor of a bound argument throws.  If such an exception is
  * thrown, the task will not start (which also means that the 'when'
  * and 'fail' callbacks will not execute).
  * @note 2. If a 'when_releaser' or a 'fail_releaser' object is
  * provided, it is in theory possible (if memory is exhausted and the
  * system throws in that case) that an internal SafeEmitterArg object
  * will throw std::bad_alloc when emitting/executing the 'when' or
  * 'fail' callback in the glib main loop, with the result that the
  * relevant callback will not execute (instead the exception will be
  * consumed and a g_critical() warning will be issued).  This is
  * rarely of any relevance because glib will abort the program if it
  * is itself unable to obtain memory from the operating system.
  * However, where it is relevant, design the program so that it is
  * not necessary to provide a releaser object.
  *
  * Since 2.0.13
  */
  template <class Ret, class... Params, class... Args, class T>
  void make_task_when_full(std::unique_ptr<const Cgu::Callback::CallbackArg<const Ret&>> when,
			   Cgu::Releaser* when_releaser,
			   std::unique_ptr<const Cgu::Callback::Callback> fail,
			   Cgu::Releaser* fail_releaser,
			   gint priority,
			   GMainContext* context,
			   T& t,
			   Ret (T::*func)(Params...),
			   Args&&... args);

 /**
  * @deprecated
  *
  * DEPRECATED.  Use the versions of make_task_when() which take
  * callable objects.
  *
  * This is an abbreviated version of make_task_when_full(), which is
  * for use when it is known that invocation of the member function
  * passed to this method, the copy constructors of any non-reference
  * arguments of that function and the move constructor (or if it has
  * none, the copy constructor) of that function's return value do not
  * throw anything other than std::bad_alloc, and the user is not
  * interested in std::bad_alloc and does not need a Cgu::Releaser
  * object for the 'when' callback (which is likely to cover the
  * majority of uses, particularly when composing tasks using glib
  * because glib terminates the program if it is unable to obtain
  * memory).
  *
  * This method can take up to three bound arguments for the target
  * member function.
  *
  * Like make_task_when_full(), this method is a wrapper which takes a
  * member function pointer to a member function which returns a
  * value, together with arguments, and constructs a TaskManager task
  * which will execute that function by calling add_task() with an
  * appropriate callback object, and causes the 'when' callback passed
  * as an argument to this method to be executed by a glib main loop
  * if and when the task finishes correctly - the 'when' callback is
  * passed the member function's return value when it is invoked.  It
  * is thread safe (any thread may call this method, including another
  * task running on the TaskManager object).  Apart from the absence
  * of a 'one thread per task' model, this method therefore provides a
  * similar interface to the one provided by Cgu::Thread::Future.  See
  * the documentation on add_task() for further information about how
  * task execution works.
  *
  * The 'when' callback will execute with G_PRIORITY_DEFAULT priority
  * in the main loop.
  *
  * @param when A callback which will be executed if and when the
  * function passed to this method finishes correctly. The callback is
  * passed that function's return value when it is invoked.  If an
  * exception propagates from the 'when' callback, this will be
  * consumed and a g_critical() warning will be issued.  The callback
  * will execute in the glib main loop whose GMainContext object is
  * passed to the 'context' argument of this method.
  * @param context The glib main context of the main loop in which the
  * 'when' callback is to be executed.  A value 0/NULL/nullptr will
  * cause the callback to be executed in the main program loop.
  * @param t The object whose member function passed to this method is
  * to execute as a task.
  * @param func The member function to be executed as a task.  If an
  * exception propagates from the task, the exception will be consumed
  * and (if the thrown object's type is not Cgu::Thread::Exit) a
  * g_critical() warning will be issued.
  * @param args The arguments to be passed to that member function.
  * @exception std::bad_alloc This exception will be thrown if memory
  * is exhausted and the system throws in that case.  (On systems with
  * over-commit/lazy-commit combined with virtual memory (swap), it is
  * rarely useful to check for memory exhaustion).  See also the
  * documentation for the get_max_tasks() method about the possibility
  * of std::length_error being thrown.  If std::bad_alloc or
  * std::length_error is thrown, the task will not start (which also
  * means that the 'when' callback will not execute).
  * @exception Cgu::Thread::TaskError This exception will be thrown if
  * stop_all() has previously been called.  It will also be thrown if
  * the call to add_task() made by this method tries but fails to
  * start a new thread, or if is_error() would return true because
  * this class's internal thread pool loop implementation has thrown
  * std::bad_alloc or a thread has previously failed to start
  * correctly.  If this exception is thrown, the task will not start
  * (which also means that the 'when' callback will not execute).
  * @note 1. This method will also throw if the copy or move
  * constructor of a bound argument throws.  If such an exception is
  * thrown, the task will not start (which also means that the 'when'
  * callback will not execute).
  * @note 2. As mentioned in describing 'func' above, if 'func' exits
  * by throwing an exception the exception will be consumed and (if
  * the thrown object's type is not Cgu::Thread::Exit) a g_critical()
  * warning will be issued.  The same will occur if the copy
  * constructor of a non-reference argument of 'func' throws when
  * invoking 'func' or the move constructor (or if it has none, the
  * copy constructor) of the return value of 'func' throws, or if the
  * internal implementation of this wrapper throws std::bad_alloc on
  * executing 'func'.
  *
  * Since 2.0.13
  */
  template <class Ret, class... Params, class... Args, class T>
  void make_task_when(std::unique_ptr<const Cgu::Callback::CallbackArg<const Ret&>> when,
		      GMainContext* context,
		      T& t,
		      Ret (T::*func)(Params...),
		      Args&&... args) {
    static_assert(sizeof...(Args) < 4,
		  "No greater than three bound arguments can be passed to "
		  "TaskManager::make_task_when() taking a member function.");

    make_task_when_full(std::move(when),
			0,
			std::unique_ptr<const Cgu::Callback::Callback>(),
			0,
			G_PRIORITY_DEFAULT,
			context,
			t,
			func,
			std::forward<Args>(args)...);
  }

 /**
  * @deprecated
  *
  * DEPRECATED.  Use the versions of make_task_result() which take
  * callable objects.
  *
  * This is a wrapper which takes a member function pointer to a
  * member function which returns a value, together with arguments,
  * and constructs a TaskManager task which will execute that function
  * by calling add_task() with an appropriate callback object, and
  * returns a Cgu::AsyncResult object (held by Cgu::SharedLockPtr)
  * which will provide the value that the function returns.  Apart
  * from the absence of a 'one thread per task' model, this method
  * therefore provides a similar interface to the one provided by
  * Cgu::Thread::Future.  It is thread safe: any thread may call this
  * method, including another task running on the TaskManager object,
  * but see the introductory remarks about the use of the
  * TaskManager::IncHandle scoped handle class where a task running on
  * a TaskManager object is to block on one of its sub-tasks.  See
  * also the documentation on add_task() for further information about
  * how task execution works.
  *
  * This method can take up to three bound arguments for the target
  * member function.
  *
  * If the function passed to this method exits by throwing
  * Thread::Exit or some other exception, then the exception will be
  * consumed and the returned Cgu::AsyncResult object's get() or
  * move_get() method will unblock and its get_error() method will
  * return -1.
  *
  * @param t The object whose member function passed to this method is
  * to execute as a task.
  * @param func The member function to be executed as a task.
  * @param args The arguments to be passed to that member function.
  * @exception std::bad_alloc This exception will be thrown if memory
  * is exhausted and the system throws in that case.  (On systems with
  * over-commit/lazy-commit combined with virtual memory (swap), it is
  * rarely useful to check for memory exhaustion).  See also the
  * documentation for the get_max_tasks() method about the possibility
  * of std::length_error being thrown.  If std::bad_alloc or
  * std::length_error is thrown, the task will not start.
  * @exception Cgu::Thread::TaskError This exception will be thrown if
  * stop_all() has previously been called.  It will also be thrown if
  * the call to add_task() made by this method tries but fails to
  * start a new thread, or if is_error() would return true because
  * this class's internal thread pool loop implementation has thrown
  * std::bad_alloc or a thread has previously failed to start
  * correctly.  If this exception is thrown, the task will not start.
  * @note This method will also throw if the copy or move constructor
  * of a bound argument throws.  If such an exception is thrown, the
  * task will not start.
  *
  * Since 2.0.13
  */

  template <class Ret, class... Params, class... Args, class T>
  Cgu::SharedLockPtr<Cgu::AsyncResult<Ret>> make_task_result(const T& t,
							     Ret (T::*func)(Params...) const,
							     Args&&... args);

 /**
  * @deprecated
  *
  * DEPRECATED.  Use the versions of make_task_when_full() which take
  * callable objects.
  *
  * This is a wrapper which takes a member function pointer to a
  * member function which returns a value, together with arguments,
  * and constructs a TaskManager task which will execute that function
  * by calling add_task() with an appropriate callback object, and
  * causes the 'when' callback passed as an argument to this method to
  * be executed by a glib main loop if and when the task finishes
  * correctly - the 'when' callback is passed the member function's
  * return value when it is invoked.  It is thread safe (any thread
  * may call this method, including another task running on the
  * TaskManager object).  Apart from the absence of a 'one thread per
  * task' model, this method therefore provides a similar interface to
  * the one provided by Cgu::Thread::Future.  See the documentation on
  * add_task() for further information about how task execution works.
  *
  * This method can take up to three bound arguments for the target
  * member function.
  *
  * Note that unlike add_task(), but like the 'fail' callback of
  * Cgu::Thread::Future objects, if a fail callback is provided to
  * this method and it executes, it will execute in the glib main loop
  * whose GMainContext object is passed to the 'context' argument of
  * this method.
  *
  * Note also that if releasers are provided for the 'when' or 'fail'
  * callbacks, these are passed by pointer and not by reference (this
  * is so that a NULL pointer can indicate that no releaser is to be
  * provided).  If provided, a releaser will enable automatic
  * disconnection of the 'when' or 'fail' callback, if the object
  * having the callback function as a member is destroyed. For this to
  * be race free, the lifetime of that object must be controlled by
  * the thread in whose main loop the 'when' or 'fail' callback will
  * execute.
  *
  * The make_task_when() method is similar to this method but provides
  * an abbreviated set of paramaters suitable for most cases.  This
  * method is for use where releasers or a 'fail' callback are
  * required.
  *
  * @param when A callback which will be executed if and when the
  * function passed to this method finishes correctly. The callback is
  * passed that function's return value when it is invoked.  If an
  * exception propagates from the 'when' callback, this will be
  * consumed and a g_critical() warning will be issued.  The callback
  * will execute in the glib main loop whose GMainContext object is
  * passed to the 'context' argument of this method.
  * @param when_releaser A pointer to a Releaser object for automatic
  * disconnection of the 'when' callback before it executes in a main
  * loop (mainly relevant if the callback represents a non-static
  * member function of an object which may be destroyed before the
  * callback executes).  A value of 0/NULL/nullptr indicates no
  * releaser.
  * @param fail A callback which will be executed if the 'when'
  * callback does not execute.  This would happen if the function
  * passed to this method exits by throwing Thread::Exit or some other
  * exception, if the copy constructor of a non-reference argument of
  * that function throws, if the move constructor (or if it has none,
  * the copy constructor) of that function's return value throws or if
  * the 'when' callback does not execute because the internal
  * implementation of this wrapper throws std::bad_alloc (which will
  * not happen if the library has been installed using the
  * \--with-glib-memory-slices-no-compat configuration option: instead
  * glib will terminate the program if it is unable to obtain memory
  * from the operating system).  If an exception propagates from the
  * 'fail' callback, this will be consumed and a g_critical() warning
  * will be issued.  The callback will execute in the glib main loop
  * whose GMainContext object is passed to the 'context' argument of
  * this method.  An empty std::unique_ptr object indicates no 'fail'
  * callback.
  * @param fail_releaser A pointer to a Releaser object for automatic
  * disconnection of the 'fail' callback before it executes in a main
  * loop (mainly relevant if the callback represents a non-static
  * member function of an object which may be destroyed before the
  * callback executes).  A value of 0/NULL/nullptr indicates no
  * releaser.
  * @param priority The priority to be given in the main loop to the
  * 'when' callback or any 'fail' callback. In ascending order of
  * priorities, priorities are G_PRIORITY_LOW,
  * G_PRIORITY_DEFAULT_IDLE, G_PRIORITY_HIGH_IDLE, G_PRIORITY_DEFAULT
  * and G_PRIORITY_HIGH. This determines the order in which the
  * callback will appear in the event list in the main loop, not the
  * priority which the OS will adopt.
  * @param context The glib main context of the main loop in which the
  * 'when' callback or any 'fail' callback is to be executed.  A value
  * 0/NULL/nullptr will cause the callback to be executed in the main
  * program loop.
  * @param t The object whose member function passed to this method is
  * to execute as a task.
  * @param func The member function to be executed as a task.  If an
  * exception propagates from the task, the exception will be consumed
  * and the 'fail' callback will execute.
  * @param args The arguments to be passed to that member function.
  * @exception std::bad_alloc This exception will be thrown if memory
  * is exhausted and the system throws in that case.  (On systems with
  * over-commit/lazy-commit combined with virtual memory (swap), it is
  * rarely useful to check for memory exhaustion).  See also the
  * documentation for the get_max_tasks() method about the possibility
  * of std::length_error being thrown.  If std::bad_alloc or
  * std::length_error is thrown, the task will not start (which also
  * means that the 'when' and 'fail' callbacks will not execute).
  * @exception Cgu::Thread::TaskError This exception will be thrown if
  * stop_all() has previously been called.  It will also be thrown if
  * the call to add_task() made by this method tries but fails to
  * start a new thread, or if is_error() would return true because
  * this class's internal thread pool loop implementation has thrown
  * std::bad_alloc or a thread has previously failed to start
  * correctly.  If this exception is thrown, the task will not start
  * (which also means that the 'when' and 'fail' callbacks will not
  * execute).
  * @note 1. This method will also throw if the copy or move
  * constructor of a bound argument throws.  If such an exception is
  * thrown, the task will not start (which also means that the 'when'
  * and 'fail' callbacks will not execute).
  * @note 2. If a 'when_releaser' or a 'fail_releaser' object is
  * provided, it is in theory possible (if memory is exhausted and the
  * system throws in that case) that an internal SafeEmitterArg object
  * will throw std::bad_alloc when emitting/executing the 'when' or
  * 'fail' callback in the glib main loop, with the result that the
  * relevant callback will not execute (instead the exception will be
  * consumed and a g_critical() warning will be issued).  This is
  * rarely of any relevance because glib will abort the program if it
  * is itself unable to obtain memory from the operating system.
  * However, where it is relevant, design the program so that it is
  * not necessary to provide a releaser object.
  *
  * Since 2.0.13
  */
  template <class Ret, class... Params, class... Args, class T>
  void make_task_when_full(std::unique_ptr<const Cgu::Callback::CallbackArg<const Ret&>> when,
			   Cgu::Releaser* when_releaser,
			   std::unique_ptr<const Cgu::Callback::Callback> fail,
			   Cgu::Releaser* fail_releaser,
			   gint priority,
			   GMainContext* context,
			   const T& t,
			   Ret (T::*func)(Params...) const,
			   Args&&... args);

 /**
  * @deprecated
  *
  * DEPRECATED.  Use the versions of make_task_when() which take
  * callable objects.
  *
  * This is an abbreviated version of make_task_when_full(), which is
  * for use when it is known that invocation of the member function
  * passed to this method, the copy constructors of any non-reference
  * arguments of that function and the move constructor (or if it has
  * none, the copy constructor) of that function's return value do not
  * throw anything other than std::bad_alloc, and the user is not
  * interested in std::bad_alloc and does not need a Cgu::Releaser
  * object for the 'when' callback (which is likely to cover the
  * majority of uses, particularly when composing tasks using glib
  * because glib terminates the program if it is unable to obtain
  * memory).
  *
  * This method can take up to three bound arguments for the target
  * member function.
  *
  * Like make_task_when_full(), this method is a wrapper which takes a
  * member function pointer to a member function which returns a
  * value, together with arguments, and constructs a TaskManager task
  * which will execute that function by calling add_task() with an
  * appropriate callback object, and causes the 'when' callback passed
  * as an argument to this method to be executed by a glib main loop
  * if and when the task finishes correctly - the 'when' callback is
  * passed the member function's return value when it is invoked.  It
  * is thread safe (any thread may call this method, including another
  * task running on the TaskManager object).  Apart from the absence
  * of a 'one thread per task' model, this method therefore provides a
  * similar interface to the one provided by Cgu::Thread::Future.  See
  * the documentation on add_task() for further information about how
  * task execution works.
  *
  * The 'when' callback will execute with G_PRIORITY_DEFAULT priority
  * in the main loop.
  *
  * @param when A callback which will be executed if and when the
  * function passed to this method finishes correctly. The callback is
  * passed that function's return value when it is invoked.  If an
  * exception propagates from the 'when' callback, this will be
  * consumed and a g_critical() warning will be issued.  The callback
  * will execute in the glib main loop whose GMainContext object is
  * passed to the 'context' argument of this method.
  * @param context The glib main context of the main loop in which the
  * 'when' callback is to be executed.  A value 0/NULL/nullptr will
  * cause the callback to be executed in the main program loop.
  * @param t The object whose member function passed to this method is
  * to execute as a task.
  * @param func The member function to be executed as a task.  If an
  * exception propagates from the task, the exception will be consumed
  * and (if the thrown object's type is not Cgu::Thread::Exit) a
  * g_critical() warning will be issued.
  * @param args The arguments to be passed to that member function.
  * @exception std::bad_alloc This exception will be thrown if memory
  * is exhausted and the system throws in that case.  (On systems with
  * over-commit/lazy-commit combined with virtual memory (swap), it is
  * rarely useful to check for memory exhaustion).  See also the
  * documentation for the get_max_tasks() method about the possibility
  * of std::length_error being thrown.  If std::bad_alloc or
  * std::length_error is thrown, the task will not start (which also
  * means that the 'when' callback will not execute).
  * @exception Cgu::Thread::TaskError This exception will be thrown if
  * stop_all() has previously been called.  It will also be thrown if
  * the call to add_task() made by this method tries but fails to
  * start a new thread, or if is_error() would return true because
  * this class's internal thread pool loop implementation has thrown
  * std::bad_alloc or a thread has previously failed to start
  * correctly.  If this exception is thrown, the task will not start
  * (which also means that the 'when' callback will not execute).
  * @note 1. This method will also throw if the copy or move
  * constructor of a bound argument throws.  If such an exception is
  * thrown, the task will not start (which also means that the 'when'
  * callback will not execute).
  * @note 2. As mentioned in describing 'func' above, if 'func' exits
  * by throwing an exception the exception will be consumed and (if
  * the thrown object's type is not Cgu::Thread::Exit) a g_critical()
  * warning will be issued.  The same will occur if the copy
  * constructor of a non-reference argument of 'func' throws when
  * invoking 'func' or the move constructor (or if it has none, the
  * copy constructor) of the return value of 'func' throws, or if the
  * internal implementation of this wrapper throws std::bad_alloc on
  * executing 'func'.
  *
  * Since 2.0.13
  */
  template <class Ret, class... Params, class... Args, class T>
  void make_task_when(std::unique_ptr<const Cgu::Callback::CallbackArg<const Ret&>> when,
		      GMainContext* context,
		      const T& t,
		      Ret (T::*func)(Params...) const,
		      Args&&... args) {
    static_assert(sizeof...(Args) < 4,
		  "No greater than three bound arguments can be passed to "
		  "TaskManager::make_task_when() taking a member function.");

    make_task_when_full(std::move(when),
			0,
			std::unique_ptr<const Cgu::Callback::Callback>(),
			0,
			G_PRIORITY_DEFAULT,
			context,
			t,
			func,
			std::forward<Args>(args)...);
  }

 /**
  * @deprecated
  *
  * DEPRECATED.  Use the versions of make_task_result() which take
  * callable objects.
  *
  * This is a wrapper which takes a pointer to a function which
  * returns a value, together with arguments, and constructs a
  * TaskManager task which will execute that function by calling
  * add_task() with an appropriate callback object, and returns a
  * Cgu::AsyncResult object (held by Cgu::SharedLockPtr) which will
  * provide the value that the function returns.  Apart from the
  * absence of a 'one thread per task' model, this method therefore
  * provides a similar interface to the one provided by
  * Cgu::Thread::Future.  It is thread safe: any thread may call this
  * method, including another task running on the TaskManager object,
  * but see the introductory remarks about the use of the
  * TaskManager::IncHandle scoped handle class where a task running on
  * a TaskManager object is to block on one of its sub-tasks.  See
  * also the documentation on add_task() for further information about
  * how task execution works.
  *
  * This method can take up to four bound arguments for the target
  * function.
  *
  * If the function passed to this method exits by throwing
  * Thread::Exit or some other exception, then the exception will be
  * consumed and the returned Cgu::AsyncResult object's get() or
  * move_get() method will unblock and its get_error() method will
  * return -1.
  *
  * @param func The function to be executed as a task.
  * @param args The arguments to be passed to that function.
  * @exception std::bad_alloc This exception will be thrown if memory
  * is exhausted and the system throws in that case.  (On systems with
  * over-commit/lazy-commit combined with virtual memory (swap), it is
  * rarely useful to check for memory exhaustion).  See also the
  * documentation for the get_max_tasks() method about the possibility
  * of std::length_error being thrown.  If std::bad_alloc or
  * std::length_error is thrown, the task will not start.
  * @exception Cgu::Thread::TaskError This exception will be thrown if
  * stop_all() has previously been called.  It will also be thrown if
  * the call to add_task() made by this method tries but fails to
  * start a new thread, or if is_error() would return true because
  * this class's internal thread pool loop implementation has thrown
  * std::bad_alloc or a thread has previously failed to start
  * correctly.  If this exception is thrown, the task will not start.
  * @note This method will also throw if the copy or move constructor
  * of a bound argument throws.  If such an exception is thrown, the
  * task will not start.
  *
  * Since 2.0.13
  */
  template <class Ret, class... Params, class... Args>
  Cgu::SharedLockPtr<Cgu::AsyncResult<Ret>> make_task_result(Ret (*func)(Params...),
							     Args&&... args);

 /**
  * @deprecated
  *
  * DEPRECATED.  Use the versions of make_task_when_full() which take
  * callable objects.
  *
  * This is a wrapper which takes a pointer to a function which
  * returns a value, together with arguments, and constructs a
  * TaskManager task which will execute that function by calling
  * add_task() with an appropriate callback object, and causes the
  * 'when' callback passed as an argument to this method to be
  * executed by a glib main loop if and when the task finishes
  * correctly - the 'when' callback is passed the function's return
  * value when it is invoked.  It is thread safe (any thread may call
  * this method, including another task running on the TaskManager
  * object).  Apart from the absence of a 'one thread per task' model,
  * this method therefore provides a similar interface to the one
  * provided by Cgu::Thread::Future.  See the documentation on
  * add_task() for further information about how task execution works.
  *
  * This method can take up to four bound arguments for the target
  * function.
  *
  * Note that unlike add_task(), but like the 'fail' callback of
  * Cgu::Thread::Future objects, if a fail callback is provided to
  * this method and it executes, it will execute in the glib main loop
  * whose GMainContext object is passed to the 'context' argument of
  * this method.
  *
  * Note also that if releasers are provided for the 'when' or 'fail'
  * callbacks, these are passed by pointer and not by reference (this
  * is so that a NULL pointer can indicate that no releaser is to be
  * provided).  If provided, a releaser will enable automatic
  * disconnection of the 'when' or 'fail' callback, if the object of
  * which the releaser is a member is destroyed. For this to be race
  * free, the lifetime of that object must be controlled by the thread
  * in whose main loop the 'when' or 'fail' callback will execute.
  *
  * The make_task_when() method is similar to this method but provides
  * an abbreviated set of paramaters suitable for most cases.  This
  * method is for use where releasers or a 'fail' callback are
  * required.
  *
  * @param when A callback which will be executed if and when the
  * function passed to this method finishes correctly. The callback is
  * passed that function's return value when it is invoked.  If an
  * exception propagates from the 'when' callback, this will be
  * consumed and a g_critical() warning will be issued.  The callback
  * will execute in the glib main loop whose GMainContext object is
  * passed to the 'context' argument of this method.
  * @param when_releaser A pointer to a Releaser object for automatic
  * disconnection of the 'when' callback before it executes in a main
  * loop (mainly relevant if the callback represents a non-static
  * member function of an object which may be destroyed before the
  * callback executes).  A value of 0/NULL/nullptr indicates no
  * releaser.
  * @param fail A callback which will be executed if the 'when'
  * callback does not execute.  This would happen if the function
  * passed to this method exits by throwing Thread::Exit or some other
  * exception, if the copy constructor of a non-reference argument of
  * that function throws, if the move constructor (or if it has none,
  * the copy constructor) of that function's return value throws or if
  * the 'when' callback does not execute because the internal
  * implementation of this wrapper throws std::bad_alloc (which will
  * not happen if the library has been installed using the
  * \--with-glib-memory-slices-no-compat configuration option: instead
  * glib will terminate the program if it is unable to obtain memory
  * from the operating system).  If an exception propagates from the
  * 'fail' callback, this will be consumed and a g_critical() warning
  * will be issued.  The callback will execute in the glib main loop
  * whose GMainContext object is passed to the 'context' argument of
  * this method.  An empty std::unique_ptr object indicates no 'fail'
  * callback.
  * @param fail_releaser A pointer to a Releaser object for automatic
  * disconnection of the 'fail' callback before it executes in a main
  * loop (mainly relevant if the callback represents a non-static
  * member function of an object which may be destroyed before the
  * callback executes).  A value of 0/NULL/nullptr indicates no
  * releaser.
  * @param priority The priority to be given in the main loop to the
  * 'when' callback or any 'fail' callback. In ascending order of
  * priorities, priorities are G_PRIORITY_LOW,
  * G_PRIORITY_DEFAULT_IDLE, G_PRIORITY_HIGH_IDLE, G_PRIORITY_DEFAULT
  * and G_PRIORITY_HIGH. This determines the order in which the
  * callback will appear in the event list in the main loop, not the
  * priority which the OS will adopt.
  * @param context The glib main context of the main loop in which the
  * 'when' callback or any 'fail' callback is to be executed.  A value
  * 0/NULL/nullptr will cause the callback to be executed in the main
  * program loop.
  * @param func The function to be executed as a task.  If an
  * exception propagates from the task, the exception will be consumed
  * and the 'fail' callback will execute.
  * @param args The arguments to be passed to that function.
  * @exception std::bad_alloc This exception will be thrown if memory
  * is exhausted and the system throws in that case.  (On systems with
  * over-commit/lazy-commit combined with virtual memory (swap), it is
  * rarely useful to check for memory exhaustion).  See also the
  * documentation for the get_max_tasks() method about the possibility
  * of std::length_error being thrown.  If std::bad_alloc or
  * std::length_error is thrown, the task will not start (which also
  * means that the 'when' and 'fail' callbacks will not execute).
  * @exception Cgu::Thread::TaskError This exception will be thrown if
  * stop_all() has previously been called.  It will also be thrown if
  * the call to add_task() made by this method tries but fails to
  * start a new thread, or if is_error() would return true because
  * this class's internal thread pool loop implementation has thrown
  * std::bad_alloc or a thread has previously failed to start
  * correctly.  If this exception is thrown, the task will not start
  * (which also means that the 'when' and 'fail' callbacks will not
  * execute).
  * @note 1. This method will also throw if the copy or move
  * constructor of a bound argument throws.  If such an exception is
  * thrown, the task will not start (which also means that the 'when'
  * and 'fail' callbacks will not execute).
  * @note 2. If a 'when_releaser' or a 'fail_releaser' object is
  * provided, it is in theory possible (if memory is exhausted and the
  * system throws in that case) that an internal SafeEmitterArg object
  * will throw std::bad_alloc when emitting/executing the 'when' or
  * 'fail' callback in the glib main loop, with the result that the
  * relevant callback will not execute (instead the exception will be
  * consumed and a g_critical() warning will be issued).  This is
  * rarely of any relevance because glib will abort the program if it
  * is itself unable to obtain memory from the operating system.
  * However, where it is relevant, design the program so that it is
  * not necessary to provide a releaser object.
  *
  * Since 2.0.13
  */
  template <class Ret, class... Params, class... Args>
  void make_task_when_full(std::unique_ptr<const Cgu::Callback::CallbackArg<const Ret&>> when,
			   Cgu::Releaser* when_releaser,
			   std::unique_ptr<const Cgu::Callback::Callback> fail,
			   Cgu::Releaser* fail_releaser,
			   gint priority,
			   GMainContext* context,
			   Ret (*func)(Params...),
			   Args&&... args);

 /**
  * @deprecated
  *
  * DEPRECATED.  Use the versions of make_task_when() which take
  * callable objects.
  *
  * This is an abbreviated version of make_task_when_full(), which is
  * for use when it is known that invocation of the function passed to
  * this method, the copy constructors of any non-reference arguments
  * of that function and the move constructor (or if it has none, the
  * copy constructor) of that function's return value do not throw
  * anything other than std::bad_alloc, and the user is not interested
  * in std::bad_alloc and does not need a Cgu::Releaser object for the
  * 'when' callback (which is likely to cover the majority of uses,
  * particularly when composing tasks using glib because glib
  * terminates the program if it is unable to obtain memory).
  *
  * This method can take up to four bound arguments for the target
  * function.
  *
  * Like make_task_when_full(), this method is a wrapper which takes a
  * pointer to a function which returns a value, together with
  * arguments, and constructs a TaskManager task which will execute
  * that function by calling add_task() with an appropriate callback
  * object, and causes the 'when' callback passed as an argument to
  * this method to be executed by a glib main loop if and when the
  * task finishes correctly - the 'when' callback is passed the
  * function's return value when it is invoked.  It is thread safe
  * (any thread may call this method, including another task running
  * on the TaskManager object).  Apart from the absence of a 'one
  * thread per task' model, this method therefore provides a similar
  * interface to the one provided by Cgu::Thread::Future.  See the
  * documentation on add_task() for further information about how task
  * execution works.
  *
  * The 'when' callback will execute with G_PRIORITY_DEFAULT priority
  * in the main loop.
  *
  * @param when A callback which will be executed if and when the
  * function passed to this method finishes correctly. The callback is
  * passed that function's return value when it is invoked.  If an
  * exception propagates from the 'when' callback, this will be
  * consumed and a g_critical() warning will be issued.  The callback
  * will execute in the glib main loop whose GMainContext object is
  * passed to the 'context' argument of this method.
  * @param context The glib main context of the main loop in which the
  * 'when' callback is to be executed.  A value 0/NULL/nullptr will
  * cause the callback to be executed in the main program loop.
  * @param func The function to be executed as a task.  If an
  * exception propagates from the task, the exception will be consumed
  * and (if the thrown object's type is not Cgu::Thread::Exit) a
  * g_critical() warning will be issued.
  * @param args The arguments to be passed to that function.
  * @exception std::bad_alloc This exception will be thrown if memory
  * is exhausted and the system throws in that case.  (On systems with
  * over-commit/lazy-commit combined with virtual memory (swap), it is
  * rarely useful to check for memory exhaustion).  See also the
  * documentation for the get_max_tasks() method about the possibility
  * of std::length_error being thrown.  If std::bad_alloc or
  * std::length_error is thrown, the task will not start (which also
  * means that the 'when' callback will not execute).
  * @exception Cgu::Thread::TaskError This exception will be thrown if
  * stop_all() has previously been called.  It will also be thrown if
  * the call to add_task() made by this method tries but fails to
  * start a new thread, or if is_error() would return true because
  * this class's internal thread pool loop implementation has thrown
  * std::bad_alloc or a thread has previously failed to start
  * correctly.  If this exception is thrown, the task will not start
  * (which also means that the 'when' callback will not execute).
  * @note 1. This method will also throw if the copy or move
  * constructor of a bound argument throws.  If such an exception is
  * thrown, the task will not start (which also means that the 'when'
  * callback will not execute).
  * @note 2. As mentioned in describing 'func' above, if 'func' exits
  * by throwing an exception the exception will be consumed and (if
  * the thrown object's type is not Cgu::Thread::Exit) a g_critical()
  * warning will be issued.  The same will occur if the copy
  * constructor of a non-reference argument of 'func' throws when
  * invoking 'func' or the move constructor (or if it has none, the
  * copy constructor) of the return value of 'func' throws, or if the
  * internal implementation of this wrapper throws std::bad_alloc on
  * executing 'func'.
  *
  * Since 2.0.13
  */
  template <class Ret, class... Params, class... Args>
  void make_task_when(std::unique_ptr<const Cgu::Callback::CallbackArg<const Ret&>> when,
		      GMainContext* context,
		      Ret (*func)(Params...),
		      Args&&... args) {
    static_assert(sizeof...(Args) < 5,
		  "No greater than four bound arguments can be passed to "
		  "TaskManager::make_task_when() taking a function.");

    make_task_when_full(std::move(when),
			0,
			std::unique_ptr<const Cgu::Callback::Callback>(),
			0,
			G_PRIORITY_DEFAULT,
			context,
			func,
			std::forward<Args>(args)...);
  }

 /**
  * This is a wrapper which takes a callable object which returns a
  * value (such as a std::function object, a lambda or the return
  * value of std::bind), and constructs a TaskManager task which will
  * execute that object by calling add_task() with an appropriate
  * callback object, and returns a Cgu::AsyncResult object (held by
  * Cgu::SharedLockPtr) which will provide the value that it returns.
  * Apart from the absence of a 'one thread per task' model, this
  * method therefore provides a similar interface to the one provided
  * by Cgu::Thread::Future.  It is thread safe: any thread may call
  * this method, including another task running on the TaskManager
  * object, but see the introductory remarks about the use of the
  * TaskManager::IncHandle scoped handle class where a task running on
  * a TaskManager object is to block on one of its sub-tasks.  See
  * also the documentation on add_task() for further information about
  * how task execution works.
  *
  * If the callable object passed to this method exits by throwing
  * Thread::Exit or some other exception, then the exception will be
  * consumed and the returned Cgu::AsyncResult object's get() or
  * move_get() method will unblock and its get_error() method will
  * return -1.
  *
  * @param f The callable object to be executed as a task, such as
  * formed by a lambda expression or the result of std::bind.  It
  * should return a value (it cannot return void).
  * @exception std::bad_alloc This exception will be thrown if memory
  * is exhausted and the system throws in that case.  (On systems with
  * over-commit/lazy-commit combined with virtual memory (swap), it is
  * rarely useful to check for memory exhaustion).  See also the
  * documentation for the get_max_tasks() method about the possibility
  * of std::length_error being thrown.  If std::bad_alloc or
  * std::length_error is thrown, the task will not start.
  * @exception Cgu::Thread::TaskError This exception will be thrown if
  * stop_all() has previously been called.  It will also be thrown if
  * the call to add_task() made by this method tries but fails to
  * start a new thread, or if is_error() would return true because
  * this class's internal thread pool loop implementation has thrown
  * std::bad_alloc or a thread has previously failed to start
  * correctly.  If this exception is thrown, the task will not start.
  * @note 1. This method will also throw if the copy or move
  * constructor of the callable object throws.  If such an exception
  * is thrown, the task will not start.
  * @note 2. If the callable object passed as an argument has both
  * const and non-const operator()() methods, the non-const version
  * will be called even if the callable object passed is a const
  * object.
  *
  * Since 2.0.14
  */
  // we don't need this version of make_task_result() for syntactic
  // reasons - the version taking a single template parameter will do
  // by itself syntactically because it can use decltype.  However, we
  // include this version in order to be API compatible with
  // c++-gtk-utils < 2.0.14, which required the return type to be
  // specified when this method is passed something other than a
  // std::function object.  SFINAE will take care of the rest, except
  // with a corner case where all of the following apply: (i) a
  // function object is passed whose operator()() method returns a
  // copy of the function object (or another function object of the
  // same type), (ii) the function object is passed to this method as
  // a rvalue and not a lvalue, and (iii) the user specifically states
  // the return type when instantiating this template function.  This
  // would give rise to an ambiguity, but its happening is extremely
  // unlikely, and cannot happen with a lambda or the return value of
  // std::bind, because those types are only known to the compiler,
  // and cannot happen with other objects if the user lets template
  // deduction take its course.
  template <class Ret, class Func>
  Cgu::SharedLockPtr<Cgu::AsyncResult<Ret>> make_task_result(Func&& f);

  // we don't want to document this function: it provides the type
  // deduction of the return value of the passed functor (it deals
  // with cases where this is not specified expressly).
#ifndef DOXYGEN_PARSING
  template <class Func>
  auto make_task_result(Func&& f) -> Cgu::SharedLockPtr<Cgu::AsyncResult<decltype(f())>> {

    // TODO: this is a work-around for gcc < 4.7, which has a bug
    // which requires a function whose return value is determined by
    // decltype, such as make_task_result(Func&&), to be inline.  At a
    // suitable API/ABI break when gcc requirements are updated, this
    // should be moved to task_manager.tpp.

    // there are two types related to the functor to be executed by
    // the task.  'Func' is the transient type provided by argument
    // deduction for forwarding, and will vary depending on whether
    // the functor object is a lvalue (which will deduce it as a
    // reference type) or rvalue (which will not).  'FType' is the
    // type to be held by the callback object generated in this
    // function, and is never a reference type.  It is also never
    // const, because the FType member is marked mutable in the
    // callback object so that it can execute mutable lambdas (or
    // other functors with a non-const operator()() method).
    typedef typename std::remove_const<typename std::remove_reference<Func>::type>::type FType;
    // this method will fail to compile if Ret is a reference type:
    // that is a feature, not a bug, as a function returning a
    // reference lacks referential transparency, is unlikely to be
    // thread-safe and is unsuitable for use as a task function
    typedef decltype(f()) Ret;
    typedef std::unique_ptr<const Callback::Callback> CbPtr;

    SharedLockPtr<AsyncResult<Ret>> ret{new AsyncResult<Ret>};
    CbPtr exec_cb(new TaskManagerHelper2::FunctorResultExec<Ret, FType>(std::forward<Func>(f), ret));
    CbPtr do_fail_cb(Callback::make_ref(&TaskManagerHelper2::FunctorResultWrapper<Ret, FType>::do_fail,
					ret));
    add_task(std::move(exec_cb), std::move(do_fail_cb));

    return ret;
  }
#endif

 /**
  * This is a wrapper which takes a callable object which returns a
  * value (such as a std::function object, a lambda or the return
  * value of std::bind), and constructs a TaskManager task which will
  * execute that object by calling add_task() with an appropriate
  * callback object, and causes the 'when' callback passed as an
  * argument to this method to be executed by a glib main loop if and
  * when the task finishes correctly - the 'when' callback is passed
  * the callable object's return value when it is invoked.  It is
  * thread safe (any thread may call this method, including another
  * task running on the TaskManager object).  Apart from the absence
  * of a 'one thread per task' model, this method therefore provides a
  * similar interface to the one provided by Cgu::Thread::Future.  See
  * the documentation on add_task() for further information about how
  * task execution works.
  *
  * Note that unlike add_task(), but like the 'fail' callback of
  * Cgu::Thread::Future objects, if a fail callback is provided to
  * this method and it executes, it will execute in the glib main loop
  * whose GMainContext object is passed to the 'context' argument of
  * this method.
  *
  * Note also that if releasers are provided for the 'when' or 'fail'
  * callbacks, these are passed by pointer and not by reference (this
  * is so that a NULL pointer can indicate that no releaser is to be
  * provided).  If provided, a releaser will enable automatic
  * disconnection of the 'when' or 'fail' callback, if the object of
  * which the releaser is a member is destroyed. For this to be race
  * free, the lifetime of that object must be controlled by the thread
  * in whose main loop the 'when' or 'fail' callback will execute.
  *
  * The make_task_when() method is similar to this method but provides
  * an abbreviated set of parameters suitable for most cases.  This
  * method is for use where releasers or a 'fail' callback are
  * required.
  *
  * @param when A callback which will be executed if and when the
  * callable object passed as 'func' to this method finishes
  * correctly. The callback is passed that object's return value when
  * it is invoked.  If an exception propagates from the 'when'
  * callback, this will be consumed and a g_critical() warning will be
  * issued.  The callback will execute in the glib main loop whose
  * GMainContext object is passed to the 'context' argument of this
  * method.
  * @param when_releaser A pointer to a Releaser object for automatic
  * disconnection of the 'when' callback before it executes in a main
  * loop (mainly relevant if the callback represents a non-static
  * member function of an object which may be destroyed before the
  * callback executes).  A value of 0/NULL/nullptr indicates no
  * releaser.
  * @param fail A callback which will be executed if the 'when'
  * callback does not execute.  This would happen if the callable
  * object passed as 'func' to this method exits by throwing
  * Thread::Exit or some other exception, if the move constructor (or
  * if it has none, the copy constructor) of that object's return
  * value throws or if the 'when' callback does not execute because
  * the internal implementation of this wrapper throws std::bad_alloc
  * (which will not happen if the library has been installed using the
  * \--with-glib-memory-slices-no-compat configuration option: instead
  * glib will terminate the program if it is unable to obtain memory
  * from the operating system).  If an exception propagates from the
  * 'fail' callback, this will be consumed and a g_critical() warning
  * will be issued.  The callback will execute in the glib main loop
  * whose GMainContext object is passed to the 'context' argument of
  * this method.  An empty std::unique_ptr object indicates no 'fail'
  * callback.
  * @param fail_releaser A pointer to a Releaser object for automatic
  * disconnection of the 'fail' callback before it executes in a main
  * loop (mainly relevant if the callback represents a non-static
  * member function of an object which may be destroyed before the
  * callback executes).  A value of 0/NULL/nullptr indicates no
  * releaser.
  * @param priority The priority to be given in the main loop to the
  * 'when' callback or any 'fail' callback. In ascending order of
  * priorities, priorities are G_PRIORITY_LOW,
  * G_PRIORITY_DEFAULT_IDLE, G_PRIORITY_HIGH_IDLE, G_PRIORITY_DEFAULT
  * and G_PRIORITY_HIGH. This determines the order in which the
  * callback will appear in the event list in the main loop, not the
  * priority which the OS will adopt.
  * @param context The glib main context of the main loop in which the
  * 'when' callback or any 'fail' callback is to be executed.  A value
  * 0/NULL/nullptr will cause the callback to be executed in the main
  * program loop.
  * @param func The callable object to be executed as a task, such as
  * formed by a lambda expression or the result of std::bind.  It
  * should return a value (it cannot return void).  It must be fully
  * bound (that is, it must take no arguments when called).  If an
  * exception propagates from the task, the exception will be consumed
  * and the 'fail' callback will execute.
  * @exception std::bad_alloc This exception will be thrown if memory
  * is exhausted and the system throws in that case.  (On systems with
  * over-commit/lazy-commit combined with virtual memory (swap), it is
  * rarely useful to check for memory exhaustion).  See also the
  * documentation for the get_max_tasks() method about the possibility
  * of std::length_error being thrown.  If std::bad_alloc or
  * std::length_error is thrown, the task will not start (which also
  * means that the 'when' and 'fail' callbacks will not execute).
  * @exception Cgu::Thread::TaskError This exception will be thrown if
  * stop_all() has previously been called.  It will also be thrown if
  * the call to add_task() made by this method tries but fails to
  * start a new thread, or if is_error() would return true because
  * this class's internal thread pool loop implementation has thrown
  * std::bad_alloc or a thread has previously failed to start
  * correctly.  If this exception is thrown, the task will not start
  * (which also means that the 'when' and 'fail' callbacks will not
  * execute).
  * @note 1. This method will also throw if the copy or move
  * constructor of the callable object throws.  If such an exception
  * is thrown, the task will not start (which also means that the
  * 'when' and 'fail' callbacks will not execute).
  * @note 2. If the callable object passed as an argument has both
  * const and non-const operator()() methods, the non-const version
  * will be called even if the callable object passed is a const
  * object.
  * @note 3. If a 'when_releaser' or a 'fail_releaser' object is
  * provided, it is in theory possible (if memory is exhausted and the
  * system throws in that case) that an internal SafeEmitterArg object
  * will throw std::bad_alloc when emitting/executing the 'when' or
  * 'fail' callback in the glib main loop, with the result that the
  * relevant callback will not execute (instead the exception will be
  * consumed and a g_critical() warning will be issued).  This is
  * rarely of any relevance because glib will abort the program if it
  * is itself unable to obtain memory from the operating system.
  * However, where it is relevant, design the program so that it is
  * not necessary to provide a releaser object.
  *
  * Since 2.0.14
  */
  template <class Ret, class Func>
  void make_task_when_full(std::unique_ptr<const Cgu::Callback::CallbackArg<const Ret&>> when,
			   Cgu::Releaser* when_releaser,
			   std::unique_ptr<const Cgu::Callback::Callback> fail,
			   Cgu::Releaser* fail_releaser,
			   gint priority,
			   GMainContext* context,
			   Func&& func);

 /**
  * This is a wrapper which takes a callable object which returns a
  * value as its 'func' argument (such as a std::function object, a
  * lambda or the return value of std::bind), and constructs a
  * TaskManager task which will execute that object by calling
  * add_task() with an appropriate callback object, and causes the
  * 'when' callback passed as an argument to this method to be
  * executed by a glib main loop if and when the task finishes
  * correctly - the 'when' callback is passed the return value of the
  * 'func' argument when it is invoked.  It is thread safe (any thread
  * may call this method, including another task running on the
  * TaskManager object).  Apart from the absence of a 'one thread per
  * task' model, this method therefore provides a similar interface to
  * the one provided by Cgu::Thread::Future.  See the documentation on
  * add_task() for further information about how task execution works.
  *
  * Note that unlike add_task(), but like the 'fail' callback of
  * Cgu::Thread::Future objects, if a fail callback is provided to
  * this method and it executes, it will execute in the glib main loop
  * whose GMainContext object is passed to the 'context' argument of
  * this method.
  *
  * Note also that if releasers are provided for the 'when' or 'fail'
  * callbacks, these are passed by pointer and not by reference (this
  * is so that a NULL pointer can indicate that no releaser is to be
  * provided).  If provided, a releaser will enable automatic
  * disconnection of the 'when' or 'fail' callback, if the object of
  * which the releaser is a member is destroyed. For this to be race
  * free, the lifetime of that object must be controlled by the thread
  * in whose main loop the 'when' or 'fail' callback will execute.
  *
  * The make_task_when() method is similar to this method but provides
  * an abbreviated set of parameters suitable for most cases.  This
  * method is for use where releasers or a 'fail' callback are
  * required.
  *
  * @param when A callable object (such as formed by a lambda
  * expression or the result of std::bind) which will be executed if
  * and when the 'func' object passed to this method finishes
  * correctly.  The 'when' callback is passed that objects's return
  * value when invoked, and should take a single unbound argument,
  * namely a reference to const of the type of that return value.  If
  * an exception propagates from the 'when' callback, this will be
  * consumed and a g_critical() warning will be issued.  The callback
  * will execute in the glib main loop whose GMainContext object is
  * passed to the 'context' argument of this method.
  * @param when_releaser A pointer to a Releaser object for automatic
  * disconnection of the 'when' callback before it executes in a main
  * loop (mainly relevant if the callback calls a non-static member
  * function of an object which may be destroyed before the callback
  * executes).  A value of 0/NULL/nullptr indicates no releaser.
  * @param fail A callable object (such as formed by a lambda
  * expression or the result of std::bind) which will be executed if
  * the 'when' callback does not execute.  This would happen if the
  * callable object passed as 'func' to this method exits by throwing
  * Thread::Exit or some other exception, if the move constructor (or
  * if it has none, the copy constructor) of that object's return
  * value throws or if the 'when' callback does not execute because
  * the internal implementation of this wrapper throws std::bad_alloc
  * (which will not happen if the library has been installed using the
  * \--with-glib-memory-slices-no-compat configuration option: instead
  * glib will terminate the program if it is unable to obtain memory
  * from the operating system).  The callable object must be fully
  * bound (that is, it must take no arguments when called).  If an
  * exception propagates from the 'fail' callback, this will be
  * consumed and a g_critical() warning will be issued.  The callback
  * will execute in the glib main loop whose GMainContext object is
  * passed to the 'context' argument of this method.  If no 'fail'
  * callback is wanted, pass a lambda which does nothing.
  * @param fail_releaser A pointer to a Releaser object for automatic
  * disconnection of the 'fail' callback before it executes in a main
  * loop (mainly relevant if the callback calls a non-static member
  * function of an object which may be destroyed before the callback
  * executes).  A value of 0/NULL/nullptr indicates no releaser.
  * @param priority The priority to be given in the main loop to the
  * 'when' callback or any 'fail' callback. In ascending order of
  * priorities, priorities are G_PRIORITY_LOW,
  * G_PRIORITY_DEFAULT_IDLE, G_PRIORITY_HIGH_IDLE, G_PRIORITY_DEFAULT
  * and G_PRIORITY_HIGH. This determines the order in which the
  * callback will appear in the event list in the main loop, not the
  * priority which the OS will adopt.
  * @param context The glib main context of the main loop in which the
  * 'when' callback or any 'fail' callback is to be executed.  A value
  * 0/NULL/nullptr will cause the callback to be executed in the main
  * program loop.
  * @param func The callable object to be executed as a task, such as
  * formed by a lambda expression or the result of std::bind.  It
  * should return a value (it cannot return void).  It must be fully
  * bound (that is, it must take no arguments when called).  If an
  * exception propagates from the task, the exception will be consumed
  * and the 'fail' callback will execute.
  * @exception std::bad_alloc This exception will be thrown if memory
  * is exhausted and the system throws in that case.  (On systems with
  * over-commit/lazy-commit combined with virtual memory (swap), it is
  * rarely useful to check for memory exhaustion).  See also the
  * documentation for the get_max_tasks() method about the possibility
  * of std::length_error being thrown.  If std::bad_alloc or
  * std::length_error is thrown, the task will not start (which also
  * means that the 'when' and 'fail' callbacks will not execute).
  * @exception Cgu::Thread::TaskError This exception will be thrown if
  * stop_all() has previously been called.  It will also be thrown if
  * the call to add_task() made by this method tries but fails to
  * start a new thread, or if is_error() would return true because
  * this class's internal thread pool loop implementation has thrown
  * std::bad_alloc or a thread has previously failed to start
  * correctly.  If this exception is thrown, the task will not start
  * (which also means that the 'when' and 'fail' callbacks will not
  * execute).
  * @note 1. This method will also throw if the copy or move
  * constructor of the 'func', 'when' or 'fail' callable objects
  * throws.  If such an exception is thrown, the task will not start
  * (which also means that the 'when' and 'fail' callbacks will not
  * execute).
  * @note 2. If any of the callable objects passed to this method have
  * both const and non-const operator()() methods, the non-const
  * version will be called even if the callable object passed is a
  * const object.
  * @note 3. If a 'when_releaser' or a 'fail_releaser' object is
  * provided, it is in theory possible (if memory is exhausted and the
  * system throws in that case) that an internal SafeEmitterArg object
  * will throw std::bad_alloc when emitting/executing the 'when' or
  * 'fail' callback in the glib main loop, with the result that the
  * relevant callback will not execute (instead the exception will be
  * consumed and a g_critical() warning will be issued).  This is
  * rarely of any relevance because glib will abort the program if it
  * is itself unable to obtain memory from the operating system.
  * However, where it is relevant, design the program so that it is
  * not necessary to provide a releaser object.
  *
  * Since 2.1.0
  */
  // we need to use enable_if so that where this function is passed
  // unique_ptr's holding non-const Callback::CallbackArg objects, or
  // some other convertible object, this templated overload is dropped
  // from the overload set, in order to support the unique_ptr
  // overloads of this function.  This overload calls into the version
  // of this function taking CallbackArg objects by unique_ptr in
  // order to perform type erasure.
  template <class When, class Fail, class Func,
            class = typename std::enable_if<!std::is_convertible<When, std::unique_ptr<const Callback::CallbackArg<const typename std::result_of<Func()>::type&>>>::value
  	    				    && !std::is_convertible<Fail, std::unique_ptr<const Callback::Callback>>::value>::type>
  void make_task_when_full(When&& when,
  			   Cgu::Releaser* when_releaser,
  			   Fail&& fail,
  			   Cgu::Releaser* fail_releaser,
  			   gint priority,
  			   GMainContext* context,
  			   Func&& func) {
    // this method will fail to compile if Ret is a reference type:
    // that is a feature, not a bug, as a function returning a
    // reference lacks referential transparency, is unlikely to be
    // thread-safe and is unsuitable for use as a task function
    typedef decltype(func()) Ret;
    std::unique_ptr<const Callback::CallbackArg<const Ret&>> when_ptr(
      Callback::lambda<const Ret&>(std::forward<When>(when))
    );
    std::unique_ptr<const Callback::Callback> fail_ptr(
      Callback::lambda<>(std::forward<Fail>(fail))
    );
    make_task_when_full(std::move(when_ptr),
  			when_releaser,
  			std::move(fail_ptr),
  			fail_releaser,
  			priority,
  			context,
  			std::forward<Func>(func));
  }

 /**
  * This is an abbreviated version of make_task_when_full(), which is
  * for use when it is known that invocation of the callable object
  * passed to this method and the move constructor (or if it has none,
  * the copy constructor) of that object's return value do not throw
  * anything other than std::bad_alloc, and the user is not interested
  * in std::bad_alloc and does not need a Cgu::Releaser object for the
  * 'when' callback (which is likely to cover the majority of uses,
  * particularly when composing tasks using glib because glib
  * terminates the program if it is unable to obtain memory).
  *
  * Like make_task_when_full(), this method is a wrapper which takes a
  * callable object which returns a value, and constructs a
  * TaskManager task which will execute that object by calling
  * add_task() with an appropriate callback object, and causes the
  * 'when' callback passed as an argument to this method to be
  * executed by a glib main loop if and when the task finishes
  * correctly - the 'when' callback is passed the callable object's
  * return value when it is invoked.  It is thread safe (any thread
  * may call this method, including another task running on the
  * TaskManager object).  Apart from the absence of a 'one thread per
  * task' model, this method therefore provides a similar interface to
  * the one provided by Cgu::Thread::Future.  See the documentation on
  * add_task() for further information about how task execution works.
  *
  * The 'when' callback will execute with G_PRIORITY_DEFAULT priority
  * in the main loop.
  *
  * There is a similar make_task_compose() function which has the
  * callable object to be executed as a task as its first argument and
  * the 'when' callback as its last argument, in order to aid task
  * composition.
  *
  * @param when A callback which will be executed if and when the
  * callable object passed to this method finishes correctly. The
  * callback is passed that object's return value when it is invoked.
  * If an exception propagates from the 'when' callback, this will be
  * consumed and a g_critical() warning will be issued.  The callback
  * will execute in the glib main loop whose GMainContext object is
  * passed to the 'context' argument of this method.
  * @param context The glib main context of the main loop in which the
  * 'when' callback is to be executed.  A value 0/NULL/nullptr will
  * cause the callback to be executed in the main program loop.
  * @param f The callable object to be executed as a task, such as
  * formed by a lambda expression or the result of std::bind.  It
  * should return a value (it cannot return void).  It must be fully
  * bound (that is, it must take no arguments when called).  If an
  * exception propagates from the task, the exception will be consumed
  * and (if the thrown object's type is not Cgu::Thread::Exit) a
  * g_critical() warning will be issued.
  * @exception std::bad_alloc This exception will be thrown if memory
  * is exhausted and the system throws in that case.  (On systems with
  * over-commit/lazy-commit combined with virtual memory (swap), it is
  * rarely useful to check for memory exhaustion).  See also the
  * documentation for the get_max_tasks() method about the possibility
  * of std::length_error being thrown.  If std::bad_alloc or
  * std::length_error is thrown, the task will not start (which also
  * means that the 'when' callback will not execute).
  * @exception Cgu::Thread::TaskError This exception will be thrown if
  * stop_all() has previously been called.  It will also be thrown if
  * the call to add_task() made by this method tries but fails to
  * start a new thread, or if is_error() would return true because
  * this class's internal thread pool loop implementation has thrown
  * std::bad_alloc or a thread has previously failed to start
  * correctly.  If this exception is thrown, the task will not start
  * (which also means that the 'when' callback will not execute).
  * @note 1. This method will also throw if the copy or move
  * constructor of the callable object throws.  If such an exception
  * is thrown, the task will not start (which also means that the
  * 'when' callback will not execute).
  * @note 2. If the callable object passed as an argument has both
  * const and non-const operator()() methods, the non-const version
  * will be called even if the callable object passed is a const
  * object.
  * @note 3. As mentioned in describing 'f' above, if 'f' exits by
  * throwing an exception the exception will be consumed and (if the
  * thrown object's type is not Cgu::Thread::Exit) a g_critical()
  * warning will be issued.  The same will occur if the move
  * constructor (or if it has none, the copy constructor) of the
  * return value of 'f' throws or if the internal implementation of
  * this wrapper throws std::bad_alloc on executing 'f'.
  *
  * Since 2.0.14
  */
  template <class Ret, class Func>
  void make_task_when(std::unique_ptr<const Cgu::Callback::CallbackArg<const Ret&>> when,
		      GMainContext* context,
		      Func&& f) {
    make_task_when_full(std::move(when),
			0,
			std::unique_ptr<const Cgu::Callback::Callback>(),
			0,
			G_PRIORITY_DEFAULT,
			context,
                        std::forward<Func>(f));
  }

 /**
  * This is an abbreviated version of make_task_when_full(), which is
  * for use when it is known that invocation of the callable object
  * passed to the 'func' argument of this method and the move
  * constructor (or if it has none, the copy constructor) of that
  * object's return value do not throw anything other than
  * std::bad_alloc, and the user is not interested in std::bad_alloc
  * and does not need a Cgu::Releaser object for the 'when' callback
  * (which is likely to cover the majority of uses, particularly when
  * composing tasks using glib because glib terminates the program if
  * it is unable to obtain memory).
  *
  * Like make_task_when_full(), this method is a wrapper which takes a
  * callable object which returns a value as its 'func' argument, and
  * constructs a TaskManager task which will execute that object by
  * calling add_task() with an appropriate callback object, and causes
  * the 'when' callback passed as an argument to this method to be
  * executed by a glib main loop if and when the task finishes
  * correctly - the 'when' callback is passed the return value of the
  * 'func' argument when it is invoked.  It is thread safe (any thread
  * may call this method, including another task running on the
  * TaskManager object).  Apart from the absence of a 'one thread per
  * task' model, this method therefore provides a similar interface to
  * the one provided by Cgu::Thread::Future.  See the documentation on
  * add_task() for further information about how task execution works.
  *
  * The 'when' callback will execute with G_PRIORITY_DEFAULT priority
  * in the main loop.
  *
  * There is a similar make_task_compose() function which has the
  * callable object to be executed as a task as its first argument and
  * the 'when' callback as its last argument, in order to aid task
  * composition.
  *
  * @param when A callable object (such as formed by a lambda
  * expression or the result of std::bind) which will be executed if
  * and when the 'func' object passed to this method finishes
  * correctly.  The 'when' callback is passed that objects's return
  * value when invoked, and should take a single unbound argument,
  * namely a reference to const of the type of that return value.  If
  * an exception propagates from the 'when' callback, this will be
  * consumed and a g_critical() warning will be issued.  The callback
  * will execute in the glib main loop whose GMainContext object is
  * passed to the 'context' argument of this method.
  * @param context The glib main context of the main loop in which the
  * 'when' callback is to be executed.  A value 0/NULL/nullptr will
  * cause the callback to be executed in the main program loop.
  * @param func The callable object to be executed as a task, such as
  * formed by a lambda expression or the result of std::bind.  It
  * should return a value (it cannot return void).  It must be fully
  * bound (that is, it must take no arguments when called).  If an
  * exception propagates from the task, the exception will be consumed
  * and (if the thrown object's type is not Cgu::Thread::Exit) a
  * g_critical() warning will be issued.
  * @exception std::bad_alloc This exception will be thrown if memory
  * is exhausted and the system throws in that case.  (On systems with
  * over-commit/lazy-commit combined with virtual memory (swap), it is
  * rarely useful to check for memory exhaustion).  See also the
  * documentation for the get_max_tasks() method about the possibility
  * of std::length_error being thrown.  If std::bad_alloc or
  * std::length_error is thrown, the task will not start (which also
  * means that the 'when' callback will not execute).
  * @exception Cgu::Thread::TaskError This exception will be thrown if
  * stop_all() has previously been called.  It will also be thrown if
  * the call to add_task() made by this method tries but fails to
  * start a new thread, or if is_error() would return true because
  * this class's internal thread pool loop implementation has thrown
  * std::bad_alloc or a thread has previously failed to start
  * correctly.  If this exception is thrown, the task will not start
  * (which also means that the 'when' callback will not execute).
  * @note 1. This method will also throw if the copy or move
  * constructor of the 'func' or 'when' callable objects throws.  If
  * such an exception is thrown, the task will not start (which also
  * means that the 'when' callback will not execute).
  * @note 2. If any of the callable objects passed to this method have
  * both const and non-const operator()() methods, the non-const
  * version will be called even if the callable object passed is a
  * const object.
  * @note 3. As mentioned in describing 'func' above, if 'func' exits
  * by throwing an exception the exception will be consumed and (if
  * the thrown object's type is not Cgu::Thread::Exit) a g_critical()
  * warning will be issued.  The same will occur if the move
  * constructor (or if it has none, the copy constructor) of the
  * return value of 'func' throws or if the internal implementation of
  * this wrapper throws std::bad_alloc on executing 'func'.
  *
  * Since 2.1.0
  */
  // we need to use enable_if so that where this function is passed a
  // unique_ptr holding a non-const Callback::CallbackArg object, or
  // some other convertible object, this templated overload is dropped
  // from the overload set, in order to support the unique_ptr
  // overloads of this function.  This overload calls into the version
  // of this function taking a CallbackArg object by unique_ptr in
  // order to perform type erasure.
  template <class When, class Func,
            class = typename std::enable_if<!std::is_convertible<When, std::unique_ptr<const Callback::CallbackArg<const typename std::result_of<Func()>::type&>>>::value>::type>
  void make_task_when(When&& when,
		      GMainContext* context,
		      Func&& func) {
    // this method will fail to compile if Ret is a reference type:
    // that is a feature, not a bug, as a function returning a
    // reference lacks referential transparency, is unlikely to be
    // thread-safe and is unsuitable for use as a task function
    typedef decltype(func()) Ret;
    std::unique_ptr<const Callback::CallbackArg<const Ret&>> when_ptr(
      Callback::lambda<const Ret&>(std::forward<When>(when))
    );
    make_task_when_full(std::move(when_ptr),
			0,
			std::unique_ptr<const Cgu::Callback::Callback>(),
			0,
			G_PRIORITY_DEFAULT,
			context,
                        std::forward<Func>(func));
  }

 /**
  * This is an abbreviated version of make_task_when_full(), which is
  * for use when it is known that invocation of the callable object
  * passed to this method and the move constructor (or if it has none,
  * the copy constructor) of that object's return value do not throw
  * anything other than std::bad_alloc, and the user is not interested
  * in std::bad_alloc and does not need a Cgu::Releaser object for the
  * 'when' callback (which is likely to cover the majority of uses,
  * particularly when composing tasks using glib because glib
  * terminates the program if it is unable to obtain memory).
  *
  * This method does the same as the version of make_task_when()
  * taking a callable object, except that this method takes that
  * object as its first argument and the 'when' callback as its last
  * argument in order to aid task composition, and in particular so
  * tasks compose in user code in a visually ordered manner.
  *
  * More particularly, like make_task_when_full(), this method is a
  * wrapper which takes a callable object which returns a value, and
  * constructs a TaskManager task which will execute that object by
  * calling add_task() with an appropriate callback object, and causes
  * the 'when' callback passed as an argument to this method to be
  * executed by a glib main loop if and when the task finishes
  * correctly - the 'when' callback is passed the callable object's
  * return value when it is invoked.  It is thread safe (any thread
  * may call this method, including another task running on the
  * TaskManager object).  Apart from the absence of a 'one thread per
  * task' model, this method therefore provides a similar interface to
  * the one provided by Cgu::Thread::Future.  See the documentation on
  * add_task() for further information about how task execution works.
  *
  * The 'when' callback will execute with G_PRIORITY_DEFAULT priority
  * in the main loop.
  *
  * @param f The callable object to be executed as a task, such as
  * formed by a lambda expression or the result of std::bind.  It
  * should return a value (it cannot return void).  It must be fully
  * bound (that is, it must take no arguments when called).  If an
  * exception propagates from the task, the exception will be consumed
  * and (if the thrown object's type is not Cgu::Thread::Exit) a
  * g_critical() warning will be issued.
  * @param context The glib main context of the main loop in which the
  * 'when' callback is to be executed.  A value 0/NULL/nullptr will
  * cause the callback to be executed in the main program loop.
  * @param when A callback which will be executed if and when the
  * callable object passed to this method finishes correctly. The
  * callback is passed that object's return value when it is invoked.
  * If an exception propagates from the 'when' callback, this will be
  * consumed and a g_critical() warning will be issued.  The callback
  * will execute in the glib main loop whose GMainContext object is
  * passed to the 'context' argument of this method.
  * @exception std::bad_alloc This exception will be thrown if memory
  * is exhausted and the system throws in that case.  (On systems with
  * over-commit/lazy-commit combined with virtual memory (swap), it is
  * rarely useful to check for memory exhaustion).  See also the
  * documentation for the get_max_tasks() method about the possibility
  * of std::length_error being thrown.  If std::bad_alloc or
  * std::length_error is thrown, the task will not start (which also
  * means that the 'when' callback will not execute).
  * @exception Cgu::Thread::TaskError This exception will be thrown if
  * stop_all() has previously been called.  It will also be thrown if
  * the call to add_task() made by this method tries but fails to
  * start a new thread, or if is_error() would return true because
  * this class's internal thread pool loop implementation has thrown
  * std::bad_alloc or a thread has previously failed to start
  * correctly.  If this exception is thrown, the task will not start
  * (which also means that the 'when' callback will not execute).
  * @note 1. This method will also throw if the copy or move
  * constructor of the callable object throws.  If such an exception
  * is thrown, the task will not start (which also means that the
  * 'when' callback will not execute).
  * @note 2. If the callable object passed as an argument has both
  * const and non-const operator()() methods, the non-const version
  * will be called even if the callable object passed is a const
  * object.
  * @note 3. As mentioned in describing 'f' above, if 'f' exits by
  * throwing an exception the exception will be consumed and (if the
  * thrown object's type is not Cgu::Thread::Exit) a g_critical()
  * warning will be issued.  The same will occur if the move
  * constructor (or if it has none, the copy constructor) of the
  * return value of 'f' throws or if the internal implementation of
  * this wrapper throws std::bad_alloc on executing 'f'.
  *
  * Since 2.0.14
  */
  template <class Ret, class Func>
  void make_task_compose(Func&& f,
			 GMainContext* context,
			 std::unique_ptr<const Cgu::Callback::CallbackArg<const Ret&>> when) {
    make_task_when_full(std::move(when),
			0,
			std::unique_ptr<const Cgu::Callback::Callback>(),
			0,
			G_PRIORITY_DEFAULT,
			context,
                        std::forward<Func>(f));
  }

 /**
  * This is an abbreviated version of make_task_when_full(), which is
  * for use when it is known that invocation of the callable object
  * passed to the 'func' argument of this method and the move
  * constructor (or if it has none, the copy constructor) of that
  * object's return value do not throw anything other than
  * std::bad_alloc, and the user is not interested in std::bad_alloc
  * and does not need a Cgu::Releaser object for the 'when' callback
  * (which is likely to cover the majority of uses, particularly when
  * composing tasks using glib because glib terminates the program if
  * it is unable to obtain memory).
  *
  * This method does the same as make_task_when(), except that this
  * method takes the callable object to be executed as a task as its
  * first argument and the 'when' callback as its last argument in
  * order to aid task composition, and in particular so tasks compose
  * in user code in a visually ordered manner.
  *
  * More particularly, like make_task_when_full(), this method is a
  * wrapper which takes a callable object which returns a value as its
  * 'func' argument, and constructs a TaskManager task which will
  * execute that object by calling add_task() with an appropriate
  * callback object, and causes the 'when' callback passed as an
  * argument to this method to be executed by a glib main loop if and
  * when the task finishes correctly - the 'when' callback is passed
  * the return value of the 'func' argument when it is invoked.  It is
  * thread safe (any thread may call this method, including another
  * task running on the TaskManager object).  Apart from the absence
  * of a 'one thread per task' model, this method therefore provides a
  * similar interface to the one provided by Cgu::Thread::Future.  See
  * the documentation on add_task() for further information about how
  * task execution works.
  *
  * The 'when' callback will execute with G_PRIORITY_DEFAULT priority
  * in the main loop.
  *
  * @param func The callable object to be executed as a task, such as
  * formed by a lambda expression or the result of std::bind.  It
  * should return a value (it cannot return void).  It must be fully
  * bound (that is, it must take no arguments when called).  If an
  * exception propagates from the task, the exception will be consumed
  * and (if the thrown object's type is not Cgu::Thread::Exit) a
  * g_critical() warning will be issued.
  * @param context The glib main context of the main loop in which the
  * 'when' callback is to be executed.  A value 0/NULL/nullptr will
  * cause the callback to be executed in the main program loop.
  * @param when A callable object (such as formed by a lambda
  * expression or the result of std::bind) which will be executed if
  * and when the 'func' object passed to this method finishes
  * correctly.  The 'when' callback is passed that objects's return
  * value when invoked, and should take a single unbound argument,
  * namely a reference to const of the type of that return value.  If
  * an exception propagates from the 'when' callback, this will be
  * consumed and a g_critical() warning will be issued.  The callback
  * will execute in the glib main loop whose GMainContext object is
  * passed to the 'context' argument of this method.
  * @exception std::bad_alloc This exception will be thrown if memory
  * is exhausted and the system throws in that case.  (On systems with
  * over-commit/lazy-commit combined with virtual memory (swap), it is
  * rarely useful to check for memory exhaustion).  See also the
  * documentation for the get_max_tasks() method about the possibility
  * of std::length_error being thrown.  If std::bad_alloc or
  * std::length_error is thrown, the task will not start (which also
  * means that the 'when' callback will not execute).
  * @exception Cgu::Thread::TaskError This exception will be thrown if
  * stop_all() has previously been called.  It will also be thrown if
  * the call to add_task() made by this method tries but fails to
  * start a new thread, or if is_error() would return true because
  * this class's internal thread pool loop implementation has thrown
  * std::bad_alloc or a thread has previously failed to start
  * correctly.  If this exception is thrown, the task will not start
  * (which also means that the 'when' callback will not execute).
  * @note 1. This method will also throw if the copy or move
  * constructor of the 'func' or 'when' callable objects throws.  If
  * such an exception is thrown, the task will not start (which also
  * means that the 'when' callback will not execute).
  * @note 2. If any of the callable objects passed to this method have
  * both const and non-const operator()() methods, the non-const
  * version will be called even if the callable object passed is a
  * const object.
  * @note 3. As mentioned in describing 'func' above, if 'func' exits
  * by throwing an exception the exception will be consumed and (if
  * the thrown object's type is not Cgu::Thread::Exit) a g_critical()
  * warning will be issued.  The same will occur if the move
  * constructor (or if it has none, the copy constructor) of the
  * return value of 'func' throws or if the internal implementation of
  * this wrapper throws std::bad_alloc on executing 'func'.
  *
  * Since 2.1.0
  */
  // we need to use enable_if so that where this function is passed a
  // unique_ptr holding a non-const Callback::CallbackArg object, or
  // some other convertible object, this templated overload is dropped
  // from the overload set, in order to support the unique_ptr
  // overloads of this function.  This overload calls into the version
  // of this function taking a CallbackArg object by unique_ptr in
  // order to perform type erasure.
  template <class Func, class When,
            class = typename std::enable_if<!std::is_convertible<When, std::unique_ptr<const Callback::CallbackArg<const typename std::result_of<Func()>::type&>>>::value>::type>
  void make_task_compose(Func&& func,
			 GMainContext* context,
			 When&& when) {
    // this method will fail to compile if Ret is a reference type:
    // that is a feature, not a bug, as a function returning a
    // reference lacks referential transparency, is unlikely to be
    // thread-safe and is unsuitable for use as a task function
    typedef decltype(func()) Ret;
    std::unique_ptr<const Callback::CallbackArg<const Ret&>> when_ptr(
      Callback::lambda<const Ret&>(std::forward<When>(when))
    );
    make_task_when_full(std::move(when_ptr),
			0,
			std::unique_ptr<const Cgu::Callback::Callback>(),
			0,
			G_PRIORITY_DEFAULT,
			context,
                        std::forward<Func>(func));
  }

 /**
  * This is a wrapper which takes a callable object (such as a
  * std::function object, a lambda or the return value of std::bind),
  * and constructs a TaskManager task which will execute that object
  * by calling add_task() with an appropriate callback representing a
  * std::packaged_task object, and returns a std::future object which
  * will provide the value (if any) that the callable object returns.
  * It is thread safe: any thread may call this method, including
  * another task running on the TaskManager object, but see the
  * introductory remarks about the use of the TaskManager::IncHandle
  * scoped handle class where a task running on a TaskManager object
  * is to block on one of its sub-tasks.  See also the documentation
  * on add_task() for further information about how task execution
  * works.
  *
  * If the callable object passed to this method exits by throwing an
  * exception (including Thread::Exit), that exception is stored in
  * the shared state of std::packaged_task, and will be rethrown when
  * get() is called on the returned std::future object.  The callable
  * object may return void, in which case the std::future<void> object
  * returned by this method would be used for the purpose only of
  * obtaining an exception, or of "joining" on the completed task.
  *
  * One of the (perhaps odd) side effects of the specification of
  * std::async() in C++11/14 is that the question whether a
  * std::future object's destructor blocks until completion of its
  * associated task depends on whether it was constructed by a call to
  * std::async().  As the return value of this method is obtained from
  * std::packaged_task and not by a call to std::async(), its
  * destructor will not block.  If the returned std::future object is
  * allowed to go out of scope before the task completes and without
  * moving to another std::future object, the task will run to
  * completion (unless it throws a prior exception), but its return
  * value will be discarded.
  *
  * @param f The callable object, such as formed by a lambda
  * expression or the result of std::bind, to be executed via
  * std::packaged_task by the TaskManager object.
  * @return A std::future object representing the shared state of a
  * std::packaged_task object constructed to execute the task, from
  * which the result of the task can be obtained.
  * @exception std::bad_alloc This exception will be thrown if memory
  * is exhausted and the system throws in that case.  (On systems with
  * over-commit/lazy-commit combined with virtual memory (swap), it is
  * rarely useful to check for memory exhaustion).  See also the
  * documentation for the get_max_tasks() method about the possibility
  * of std::length_error being thrown.  If std::bad_alloc or
  * std::length_error is thrown, the task will not start.
  * @exception Cgu::Thread::TaskError This exception will be thrown if
  * stop_all() has previously been called.  It will also be thrown if
  * the call to add_task() made by this method tries but fails to
  * start a new thread, or if is_error() would return true because
  * this class's internal thread pool loop implementation has thrown
  * std::bad_alloc or a thread has previously failed to start
  * correctly.  If this exception is thrown, the task will not start.
  * @note This method will also throw if the copy or move
  * constructor of the callable object throws, or the move constructor
  * of std::packaged_task throws.  If such an exception is thrown, the
  * task will not start.
  *
  * Since 2.2.4
  */
  template <class Func>
  auto make_task_packaged(Func&& f) -> std::future<decltype(f())> {

    // TODO: this is a work-around for gcc < 4.7, which has a bug
    // which requires a function whose return value is determined by
    // decltype, such as make_task_packaged(Func&&), to be inline.  At
    // a suitable API/ABI break when gcc requirements are updated,
    // this should be moved to task_manager.tpp.

    // this method will fail to compile if Ret is a reference type:
    // that is a feature, not a bug, as a function returning a
    // reference lacks referential transparency, is unlikely to be
    // thread-safe and is unsuitable for use as a task function
    typedef decltype(f()) Ret;
  
    std::packaged_task<Ret()> task{std::forward<Func>(f)};
    std::future<Ret> ret{task.get_future()};
    add_task(std::move(task));

    return ret;
  }

 /**
  * This method is similar to make_task_when_full(): it takes a
  * callable object as its 'func' argument (such as a std::function
  * object, a lambda or the return value of std::bind), and constructs
  * a TaskManager task which will execute that object by calling
  * add_task() with an appropriate callback object, and causes the
  * 'when' callback passed as an argument to this method to be
  * executed by a glib main loop when the task finishes.  However,
  * unlike make_task_when_full(), it constructs a std::packaged_task
  * object to execute the task, and instead of passing the return
  * value of 'func' directly as an argument to the 'when' callback, it
  * passes a non-const reference to a std::future object of the return
  * type: so if 'func' returns a std::string object, the 'when'
  * callback should take a 'std::future<std::string>&' argument.  The
  * main purpose of this is to store any exception thrown by 'func'
  * (or by the move or copy constructor of its return value) in the
  * std::packaged_task object's shared state, so that it can be
  * obtained by the 'when' callback by calling std::future::get().  In
  * addition, subject to note 3 below this method's task wrapper will
  * never be in the position of consuming an internal std::bad_alloc
  * exception (although this method might throw std::bad_alloc).
  * Amongst other things, this dispenses with the need for a 'fail'
  * callback (at a very slight cost to efficiency compared with
  * make_task_when_full()).
  *
  * The callable object comprising the 'func' argument may return
  * void, if the purpose of the 'when' callback is only to deal in the
  * glib main loop with any exception thrown by 'func', or to do
  * something in the glib main loop dependent on the task having
  * finished but not on a value returned by it.
  *
  * A call to std::future::get() on the passed std::future object will
  * never block, because the 'when' callback is only invoked after the
  * task has finished (successfully or by throwing an exception).
  *
  * This method is thread safe (any thread may call it, including
  * another task running on the TaskManager object).  See the
  * documentation on add_task() for further information about how task
  * execution works.
  *
  * Note that if a releaser is provided for the 'when' callback, this
  * is passed by pointer and not by reference (this is so that a NULL
  * pointer can indicate that no releaser is to be provided).  If
  * provided, a releaser will enable automatic disconnection of the
  * 'when' callback if the object of which the releaser is a member is
  * destroyed. For this to be race free, the lifetime of that object
  * must be controlled by the thread in whose main loop the 'when'
  * callback will execute.
  *
  * There is an overload of this method which takes only three
  * arguments - the 'when' callback, a context argument and the
  * callable object to be run as a task, which is analogous to
  * make_task_when().  It is shorthand for cases where the 'when'
  * callback is to be executed without a releaser and with
  * G_PRIORITY_DEFAULT priority.
  *
  * @param when A callable object (such as formed by a lambda
  * expression or the result of std::bind) which will be executed when
  * the 'func' object passed to this method finishes, either by
  * executing normally or by throwing an exception.  The 'when'
  * callback should take a single unbound argument, namely a non-const
  * reference to a std::future object for the type of the return value
  * of the 'func' object.  If an exception propagates from the 'when'
  * callback, this will be consumed and a g_critical() warning will be
  * issued.  The callback will execute in the glib main loop whose
  * GMainContext object is passed to the 'context' argument of this
  * method.
  * @param when_releaser A pointer to a Releaser object for automatic
  * disconnection of the 'when' callback before it executes in a main
  * loop (mainly relevant if the callback calls a non-static member
  * function of an object which may be destroyed before the callback
  * executes).  A value of 0/NULL/nullptr indicates no releaser.
  * @param priority The priority to be given in the main loop to the
  * 'when' callback. In ascending order of priorities, priorities are
  * G_PRIORITY_LOW, G_PRIORITY_DEFAULT_IDLE, G_PRIORITY_HIGH_IDLE,
  * G_PRIORITY_DEFAULT and G_PRIORITY_HIGH. This determines the order
  * in which the callback will appear in the event list in the main
  * loop, not the priority which the OS will adopt.
  * @param context The glib main context of the main loop in which the
  * 'when' callback is to be executed.  A value 0/NULL/nullptr will
  * cause the callback to be executed in the main program loop.
  * @param func The callable object to be executed as a task, such as
  * formed by a lambda expression or the result of std::bind.  It must
  * be fully bound (that is, it must take no arguments when called).
  * If an exception propagates from the task, the exception will be
  * stored in the shared state of a std::packaged_task object and made
  * available to the 'when' callback via std::future::get().
  * @exception std::bad_alloc This exception will be thrown if memory
  * is exhausted and the system throws in that case.  (On systems with
  * over-commit/lazy-commit combined with virtual memory (swap), it is
  * rarely useful to check for memory exhaustion).  See also the
  * documentation for the get_max_tasks() method about the possibility
  * of std::length_error being thrown.  If std::bad_alloc or
  * std::length_error is thrown, the task will not start (which also
  * means that the 'when' callback will not execute).
  * @exception Cgu::Thread::TaskError This exception will be thrown if
  * stop_all() has previously been called.  It will also be thrown if
  * the call to add_task() made by this method tries but fails to
  * start a new thread, or if is_error() would return true because
  * this class's internal thread pool loop implementation has thrown
  * std::bad_alloc or a thread has previously failed to start
  * correctly.  If this exception is thrown, the task will not start
  * (which also means that the 'when' callback will not execute).
  * @note 1. This method will also throw if the copy or move
  * constructor of the 'func' or 'when' callable objects throws.  If
  * such an exception is thrown, the task will not start (which also
  * means that the 'when' callback will not execute).
  * @note 2. If any of the callable objects passed to this method have
  * both const and non-const operator()() methods, the non-const
  * version will be called even if the callable object passed is a
  * const object.
  * @note 3. If a 'when_releaser' object is provided, it is in theory
  * possible (if memory is exhausted and the system throws in that
  * case) that an internal SafeEmitterArg object will throw
  * std::bad_alloc when emitting/executing the 'when' callback in the
  * glib main loop, with the result that the relevant callback will
  * not execute (instead the exception will be consumed and a
  * g_critical() warning will be issued).  This is rarely of any
  * relevance because glib will abort the program if it is itself
  * unable to obtain memory from the operating system.  However, where
  * it is relevant, design the program so that it is not necessary to
  * provide a releaser object.
  *
  * Since 2.2.5
  */
  template <class When, class Func>
  void make_task_packaged_when(When&& when,
			       Cgu::Releaser* when_releaser,
			       gint priority,
			       GMainContext* context,
			       Func&& func);

 /**
  * This overload of make_task_packaged_when() taking three arguments
  * is an abbreviated version of the one taking five arguments.  It is
  * for use where the 'when' callback is to be executed without a
  * releaser and with G_PRIORITY_DEFAULT priority.
  *
  * It is similar to make_task_when(): it takes a callable object as
  * its 'func' argument (such as a std::function object, a lambda or
  * the return value of std::bind), and constructs a TaskManager task
  * which will execute that object by calling add_task() with an
  * appropriate callback object, and causes the 'when' callback passed
  * as an argument to this method to be executed by a glib main loop
  * when the task finishes.  However, unlike make_task_when(), it
  * constructs a std::packaged_task object to execute the task, and
  * instead of passing the return value of 'func' directly as an
  * argument to the 'when' callback, it passes a non-const reference
  * to a std::future object of the return type: so if 'func' returns a
  * std::string object, the 'when' callback should take a
  * 'std::future<std::string>&' argument.  The main purpose of this is
  * to store any exception thrown by 'func' (or by the move or copy
  * constructor of its return value) in the std::packaged_task
  * object's shared state, so that it can be obtained by the 'when'
  * callback by calling std::future::get().  In addition, this
  * method's task wrapper will never be in the position of consuming
  * an internal std::bad_alloc exception (although this method might
  * throw std::bad_alloc).
  *
  * The callable object comprising the 'func' argument may return
  * void, if the purpose of the 'when' callback is only to deal in the
  * glib main loop with any exception thrown by 'func', or to do
  * something in the glib main loop dependent on the task having
  * finished but not on a value returned by it.
  *
  * A call to std::future::get() on the passed std::future object will
  * never block, because the 'when' callback is only invoked after the
  * task has finished (successfully or by throwing an exception).
  *
  * This method is thread safe (any thread may call it, including
  * another task running on the TaskManager object).  See the
  * documentation on add_task() for further information about how task
  * execution works.
  *
  * @param when A callable object (such as formed by a lambda
  * expression or the result of std::bind) which will be executed when
  * the 'func' object passed to this method finishes, either by
  * executing normally or by throwing an exception.  The 'when'
  * callback should take a single unbound argument, namely a non-const
  * reference to a std::future object for the type of the return value
  * of the 'func' object.  If an exception propagates from the 'when'
  * callback, this will be consumed and a g_critical() warning will be
  * issued.  The callback will execute in the glib main loop whose
  * GMainContext object is passed to the 'context' argument of this
  * method.
  * @param context The glib main context of the main loop in which the
  * 'when' callback is to be executed.  A value 0/NULL/nullptr will
  * cause the callback to be executed in the main program loop.
  * @param func The callable object to be executed as a task, such as
  * formed by a lambda expression or the result of std::bind.  It must
  * be fully bound (that is, it must take no arguments when called).
  * If an exception propagates from the task, the exception will be
  * stored in the shared state of a std::packaged_task object and made
  * available to the 'when' callback via std::future::get().
  * @exception std::bad_alloc This exception will be thrown if memory
  * is exhausted and the system throws in that case.  (On systems with
  * over-commit/lazy-commit combined with virtual memory (swap), it is
  * rarely useful to check for memory exhaustion).  See also the
  * documentation for the get_max_tasks() method about the possibility
  * of std::length_error being thrown.  If std::bad_alloc or
  * std::length_error is thrown, the task will not start (which also
  * means that the 'when' callback will not execute).
  * @exception Cgu::Thread::TaskError This exception will be thrown if
  * stop_all() has previously been called.  It will also be thrown if
  * the call to add_task() made by this method tries but fails to
  * start a new thread, or if is_error() would return true because
  * this class's internal thread pool loop implementation has thrown
  * std::bad_alloc or a thread has previously failed to start
  * correctly.  If this exception is thrown, the task will not start
  * (which also means that the 'when' callback will not execute).
  * @note 1. This method will also throw if the copy or move
  * constructor of the 'func' or 'when' callable objects throws.  If
  * such an exception is thrown, the task will not start (which also
  * means that the 'when' callback will not execute).
  * @note 2. If any of the callable objects passed to this method have
  * both const and non-const operator()() methods, the non-const
  * version will be called even if the callable object passed is a
  * const object.
  *
  * Since 2.2.5
  */
  template <class When, class Func>
  void make_task_packaged_when(When&& when,
			       GMainContext* context,
			       Func&& func) {
    make_task_packaged_when(std::forward<When>(when),
			    0,
			    G_PRIORITY_DEFAULT,
			    context,
			    std::forward<Func>(func));
  }

 /**
  * This is the same as the version of make_task_packaged_when()
  * taking three arguments, except that it takes the callable object
  * to be executed as a task as its first argument and the 'when'
  * callback as its last argument in order to aid task composition,
  * and in particular so tasks compose in user code in a visually
  * ordered manner.  It is for use where the 'when' callback is to be
  * executed without a releaser and with G_PRIORITY_DEFAULT priority.
  *
  * It is similar to make_task_compose(): it takes a callable object
  * as its 'func' argument (such as a std::function object, a lambda
  * or the return value of std::bind), and constructs a TaskManager
  * task which will execute that object by calling add_task() with an
  * appropriate callback object, and causes the 'when' callback passed
  * as an argument to this method to be executed by a glib main loop
  * when the task finishes.  However, unlike make_task_compose(), it
  * constructs a std::packaged_task object to execute the task, and
  * instead of passing the return value of 'func' directly as an
  * argument to the 'when' callback, it passes a non-const reference
  * to a std::future object of the return type: so if 'func' returns a
  * std::string object, the 'when' callback should take a
  * 'std::future<std::string>&' argument.  The main purpose of this is
  * to store any exception thrown by 'func' (or by the move or copy
  * constructor of its return value) in the std::packaged_task
  * object's shared state, so that it can be obtained by the 'when'
  * callback by calling std::future::get().  In addition, this
  * method's task wrapper will never be in the position of consuming
  * an internal std::bad_alloc exception (although this method might
  * throw std::bad_alloc).
  *
  * The callable object comprising the 'func' argument may return
  * void, if the purpose of the 'when' callback is only to deal in the
  * glib main loop with any exception thrown by 'func', or to do
  * something in the glib main loop dependent on the task having
  * finished but not on a value returned by it.
  *
  * A call to std::future::get() on the passed std::future object will
  * never block, because the 'when' callback is only invoked after the
  * task has finished (successfully or by throwing an exception).
  *
  * This method is thread safe (any thread may call it, including
  * another task running on the TaskManager object).  See the
  * documentation on add_task() for further information about how task
  * execution works.
  *
  * @param func The callable object to be executed as a task, such as
  * formed by a lambda expression or the result of std::bind.  It must
  * be fully bound (that is, it must take no arguments when called).
  * If an exception propagates from the task, the exception will be
  * stored in the shared state of a std::packaged_task object and made
  * available to the 'when' callback via std::future::get().
  * @param context The glib main context of the main loop in which the
  * 'when' callback is to be executed.  A value 0/NULL/nullptr will
  * cause the callback to be executed in the main program loop.
  * @param when A callable object (such as formed by a lambda
  * expression or the result of std::bind) which will be executed when
  * the 'func' object passed to this method finishes, either by
  * executing normally or by throwing an exception.  The 'when'
  * callback should take a single unbound argument, namely a non-const
  * reference to a std::future object for the type of the return value
  * of the 'func' object.  If an exception propagates from the 'when'
  * callback, this will be consumed and a g_critical() warning will be
  * issued.  The callback will execute in the glib main loop whose
  * GMainContext object is passed to the 'context' argument of this
  * method.
  * @exception std::bad_alloc This exception will be thrown if memory
  * is exhausted and the system throws in that case.  (On systems with
  * over-commit/lazy-commit combined with virtual memory (swap), it is
  * rarely useful to check for memory exhaustion).  See also the
  * documentation for the get_max_tasks() method about the possibility
  * of std::length_error being thrown.  If std::bad_alloc or
  * std::length_error is thrown, the task will not start (which also
  * means that the 'when' callback will not execute).
  * @exception Cgu::Thread::TaskError This exception will be thrown if
  * stop_all() has previously been called.  It will also be thrown if
  * the call to add_task() made by this method tries but fails to
  * start a new thread, or if is_error() would return true because
  * this class's internal thread pool loop implementation has thrown
  * std::bad_alloc or a thread has previously failed to start
  * correctly.  If this exception is thrown, the task will not start
  * (which also means that the 'when' callback will not execute).
  * @note 1. This method will also throw if the copy or move
  * constructor of the 'func' or 'when' callable objects throws.  If
  * such an exception is thrown, the task will not start (which also
  * means that the 'when' callback will not execute).
  * @note 2. If any of the callable objects passed to this method have
  * both const and non-const operator()() methods, the non-const
  * version will be called even if the callable object passed is a
  * const object.
  *
  * Since 2.2.5
  */
  template <class When, class Func>
  void make_task_packaged_compose(Func&& func,
				  GMainContext* context,
				  When&& when) {
    make_task_packaged_when(std::forward<When>(when),
			    0,
			    G_PRIORITY_DEFAULT,
			    context,
			    std::forward<Func>(func));
  }

 /**
  * If the specified minimum number of threads is greater than 0, this
  * constructor will start the required minimum number of threads.  If
  * glib < 2.32 is installed, g_thread_init() must be called before
  * any TaskManager objects are constructed
  * @param max The maximum number of threads which the TaskManager
  * object will run in the thread pool.  If the value passed as this
  * argument is less than the value passed as 'min', the maximum
  * number of threads will be set to 'min'.  A value of 0 is not
  * valid, and if this is passed the number will be set to the greater
  * of 1 and 'min'.
  * @param min The minimum number of threads which the TaskManager
  * object will run in the thread pool.  In cases where it is
  * important that, if starting a new thread in the pool were to fail,
  * any tasks which were queued for execution before the failure
  * occurred will still run to completion (say, because the failure
  * exception is to be caught and 'blocking' is true), a 'min' value
  * of at least 1 will ensure that a thread remains available in the
  * pool for that purpose.  (See the Note below for more about this.)
  * @param idle The length of time in milliseconds that threads
  * greater in number than 'min' and not executing any tasks will
  * remain in existence.  The default is 10000 (10 seconds).
  * @param blocking If true, calls to stop_all() and the destructor
  * will not return until the tasks remaining to be executed have
  * finished (what is meant by "the tasks remaining to be executed"
  * depends on the StopMode setting, for which see the documentation
  * on the stop_all() method).  If false, stop_all() and the
  * destructor will return straight away (which in terms of the
  * TaskManager class implementation is safe for the reasons explained
  * in the documentation on the destructor).
  * @param mode The StopMode setting (either
  * Cgu::Thread::TaskManager::wait_for_running or
  * Cgu::Thread::TaskManager::wait_for_all) executed when running
  * stop_all() or when the destructor is called.  See the
  * documentation on stop_all() for an explanation of the setting.
  * @exception std::bad_alloc This exception might be thrown if memory
  * is exhausted and the system throws in that case.
  * @exception Cgu::Thread::TaskError This exception will be thrown if
  * starting the specified minimum number of threads fails.
  * @exception Cgu::Thread::MutexError This exception might be thrown
  * 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.)
  * @exception Cgu::Thread::CondError This exception might be thrown
  * 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.)
  * @note If 'min' is 0, even though tasks are outstanding in the pool
  * there could be 0 threads left running in the pool to complete them
  * if all of the following improbable events occur together: (i) a
  * thread has increased the maximum number of threads set for the
  * pool via change_max_threads() or set_max_threads() at a time when
  * tasks are queued for execution, so that an attempt is made to
  * start new threads, (ii) a different thread has concurrently
  * attempted, using one of those methods, to reduce the maximum
  * number of threads set for the pool by an amount equal to or
  * greater than the original maximum thread number prevailing before
  * these two concurrent operations, (iii) concurrently with those two
  * events a number of tasks equal to the original maximum thread
  * number referred to in ii above happen to have finished, and (iv)
  * the call to change_max_threads() or set_max_threads() referred to
  * at i above throws an exception and @a all of the threads attempted
  * to be started there fail to start.  If usage of a TaskManager
  * object could result in all these things occurring, setting its
  * 'min' value to 1 will ensure that there is always at least one
  * thread available in the pool which can complete any outstanding
  * tasks which had been added to the object before the exception was
  * thrown.
  *
  * Since 2.0.12
  */
  TaskManager(unsigned int max = 8, unsigned int min = 0,
              unsigned int idle = 10000, bool blocking = true,
	      StopMode mode = TaskManager::wait_for_all);

 /**
  * The destructor will call stop_all(), unless that method has
  * previously been called explicitly without throwing std::bad_alloc.
  * If the blocking setting is true, the destructor will not return
  * until the tasks remaining to be executed have finished (what is
  * meant by "the tasks remaining to be executed" depends on the
  * StopMode setting, for which see the documentation on the
  * stop_all() method.)  If the blocking setting is false, the
  * destructor will return straight away: this is safe, because
  * TaskManager's internals for running tasks have been implemented
  * using reference counting and will not be deleted until all threads
  * running on the TaskManager object have finished, although the
  * remaining tasks should not attempt to call any of TaskManager's
  * methods once the TaskManager object itself has been destroyed.
  *
  * The destructor is thread safe (any thread can destroy a
  * TaskManager object) unless the blocking setting is true, in which
  * case no task running on the TaskManager object may destroy the
  * TaskManager object.  Subject to that, it is not an error for a
  * thread to destroy a TaskManager object and so invoke this
  * destructor while another thread is already blocking in (if the
  * blocking setting is true) or already out of (if the blocking
  * setting is false) a call to stop_all() and remaining tasks are
  * executing: if blocking, both calls (to stop_all() and to this
  * destructor) would safely block together.  Any given thread can
  * similarly safely follow a non-blocking call to stop_all() by a
  * non-blocking call to this destructor even though remaining tasks
  * are executing.  However, it is an error for a thread to call
  * stop_all() after another thread has begun destruction of the
  * TaskManager object (that is, after this destructor has been
  * entered): there would then be an unresolvable race with the
  * destructor.
  *
  * The destructor will not throw.
  *
  * If stop_all() has not previously been called explicitly and throws
  * std::bad_alloc() when called in this destructor, the exception
  * will be caught and consumed, but then the destructor will not
  * block even if the blocking setting is true, and if the minimum
  * number of threads is not 0 some threads might remain running
  * during the entire program duration (albeit safely).  Where the
  * throwing of std::bad_alloc is a meaningful event (usually it
  * isn't) and needs to be guarded against, call stop_all() explicitly
  * before this destructor is entered, or use a minimum thread value
  * of 0 and allow for the case of the destructor not blocking.
  *
  * Since 2.0.12
  */
  ~TaskManager();

/* Only has effect if --with-glib-memory-slices-compat or
 * --with-glib-memory-slices-no-compat option picked */
  CGU_GLIB_MEMORY_SLICES_FUNCS
};

 /**
  * @class TaskManager::IncHandle task_manager.h c++-gtk-utils/task_manager.h
  * @brief A scoped handle for exception safe incrementing of the
  * maximum number of threads that a TaskManager object will run.
  * @sa Thread::TaskManager
  *
  * This class is for use where a task running on a TaskManager object
  * is about to make a blocking call.  It enables the task to
  * increment in an exception safe way the maximum number of tasks
  * which the TaskManager object will currently run in its thread pool
  * to enable another thread to keep a core active, so that the number
  * is automatically decremented again when the
  * ThreadManager::IncHandle object has gone out of scope after the
  * task has finished making blocking calls or something has thrown.
  *
  * The documentation on Thread::TaskManager gives an example of its
  * use.
  *
  * This class is available since version 2.2.1 of the library.
  */
class TaskManager::IncHandle {
  TaskManager& tm;
public:
 /**
  * This class cannot be copied.  The copy constructor is deleted.
  *
  * Since 2.0.18/2.2.1
  */
  IncHandle(const TaskManager::IncHandle&) = delete;

 /**
  * This class cannot be copied.  The assignment operator is deleted.
  *
  * Since 2.0.18/2.2.1
  */
  TaskManager::IncHandle& operator=(const TaskManager::IncHandle&) = delete;

 /**
  * This class requires initialisation with a TaskManager object.  The
  * default constructor is deleted.
  *
  * Since 2.0.18/2.2.1
  */
  IncHandle() = delete;

 /**
  * This constructor calls TaskManager::change_max_threads() to
  * increment the maximum number of threads a TaskManager object will
  * currently run in its thread pool.
  * @param tm_ The TaskManager object whose maximum thread limit is to
  * be incremented.
  * @exception std::bad_alloc If tasks are currently queued for
  * execution, a new thread will be started, so this exception may be
  * thrown on starting the thread if memory is exhausted and the
  * system throws in that case.  (On systems with
  * over-commit/lazy-commit combined with virtual memory (swap), it is
  * rarely useful to check for memory exhaustion).
  * @exception Cgu::Thread::TaskError If tasks are currently queued
  * for execution, a new thread will be started, so this exception may
  * be thrown on starting the thread if it fails to start correctly
  * (this would mean that memory is exhausted, the pthread thread
  * limit has been reached or pthread has run out of other resources
  * to start new threads).
  *
  * Since 2.0.18/2.2.1
  */
  explicit IncHandle(TaskManager& tm_): tm(tm_) {
    tm_.change_max_threads(1);
  }

 /**
  * This destructor calls TaskManager::change_max_threads() to
  * decrement the maximum number of threads a TaskManager object will
  * currently run in its thread pool.  It will not throw.
  *
  * Since 2.0.18/2.2.1
  */
  ~IncHandle() {tm.change_max_threads(-1);}
};

} // namespace Thread

} // namespace Cgu

#include <c++-gtk-utils/task_manager.tpp>

#endif
