/* Copyright (C) 2006 to 2014 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).

*/

/**
 * @file async_queue.h
 * @brief This file provides thread-safe asynchronous queue classes.
 *
 * AsyncQueue is a class which provides some of the functionality of a
 * std::queue object (but note that the AsyncQueue::pop(value_type&
 * obj) and AsyncQueue::move_pop(value_type& obj) methods provide the
 * popped element by reference - see the comments on that method for
 * the reason), except that it has mutex locking of the data container
 * so as to permit pushing and popping from different threads.  It is
 * therefore useful for passing data between threads, perhaps in
 * response to a signal being emitted from a Notifier object.  Data
 * can be pushed or pulled by move constructor/move assignment
 * operator or by means of a std::unique_ptr object, SharedLockPtr
 * object or an IntrusivePtr object referencing data derived from
 * IntrusiveLockCounter.
 *
 * AsyncQueueDispatch is a class which has blocking pop() and
 * move_pop() methods, which allows it to be waited on by a dedicated
 * event/message dispatching thread for incoming work (represented by
 * the data pushed onto the queue).  In the same way, it can be used
 * to implement thread pools, by having threads in the pool waiting on
 * the queue.
 *
 * By default the queues use a std::list object as their container
 * because when adding an item to the queue all allocation can take
 * place outside the queue object's mutex.  However, for types which
 * have low overhead copy or move constructors, this can be changed
 * to, say, a std::deque object by specifying it as the second
 * template parameter.
 */

#ifndef CGU_ASYNC_QUEUE_H
#define CGU_ASYNC_QUEUE_H

#include <queue>
#include <list>
#include <exception>
#include <utility>    // for std::move and std::forward
#include <algorithm>  // for std::swap
#include <time.h>

#include <pthread.h>

#include <c++-gtk-utils/mutex.h>
#include <c++-gtk-utils/thread.h>
#include <c++-gtk-utils/cgu_config.h>

#ifdef CGU_USE_SCHED_YIELD
#include <sched.h>
#else
#include <unistd.h>
#endif

namespace Cgu {

/**
 * @class AsyncQueuePopError async_queue.h c++-gtk-utils/async_queue.h
 * @brief An exception thrown if calling pop() on a AsyncQueue or
 * AsyncQueueDispatch object fails because the queue is empty.
 * @sa AsyncQueue AsyncQueueDispatch
 */

struct AsyncQueuePopError: public std::exception {
  virtual const char* what() const throw() {return "AsyncQueuePopError: popping from empty AsyncQueue object\n";}
};


/**
 * @class AsyncQueue async_queue.h c++-gtk-utils/async_queue.h
 * @brief A thread-safe asynchronous queue.
 * @sa AsyncQueueDispatch AsyncResult
 *
 * AsyncQueue is a class which provides some of the functionality of a
 * std::queue object (but note that the AsyncQueue::pop(value_type&
 * obj) and AsyncQueue::move_pop(value_type& obj) methods provide the
 * popped element by reference - see the comments on that method for
 * the reason), except that it has mutex locking of the data container
 * so as to permit pushing and popping from different threads.  It is
 * therefore useful for passing data between threads, perhaps in
 * response to a signal being emitted from a Notifier object.  Data
 * can be pushed or pulled by move constructor/move assignment
 * operator or by means of a std::unique_ptr object, SharedLockPtr
 * object or an IntrusivePtr object referencing data derived from
 * IntrusiveLockCounter.
 *
 * By default the queue uses a std::list object as its container
 * because when adding an item to the queue all allocation can take
 * place outside the queue object's mutex.  However, for types which
 * have low overhead copy or move constructors, this can be changed
 * to, say, a std::deque object by specifying it as the second
 * template parameter.
 *
 * If the library is installed using the
 * \--with-glib-memory-slices-compat or
 * \--with-glib-memory-slices-no-compat configuration options, any
 * AsyncQueue objects constructed on free store will be constructed in
 * glib memory slices.  This does not affect the queue container
 * itself: to change the allocator of the C++ container, a custom
 * allocator type can be provided when the AsyncQueue object is
 * instantiated offering the standard allocator interface.  If glib
 * memory slices are not used or no AsyncQueue objects are constructed
 * on free store, it is not necessary to call g_thread_init() before
 * manipulating or using an AsyncQueue object in multiple threads, but
 * prior to glib version 2.32 glib itself (and thus glib memory
 * slices) are not thread safe unless that function has been called.
 */

template <class T, class Container = std::list<T> > class AsyncQueue {
public:
  typedef typename Container::value_type value_type;
  typedef typename Container::size_type size_type;
  typedef Container container_type;
private:
// TODO: put 'q' after 'mutex' at the next ABI break, so move
// construction is strongly exception safe
  std::queue<T, Container> q;
  mutable Thread::Mutex mutex;

// TODO: at the next ABI break make this method explicitly static
// this method won't throw: it is for the user to ensure the arguments
// do not refer to the same mutex object
  void lock2(Thread::Mutex& m1, Thread::Mutex& m2) {
    m1.lock();
    for(;;) {
      if (!m2.trylock()) {
	return;
      }
      m1.unlock();
      // spin nicely
#ifdef CGU_USE_SCHED_YIELD
      sched_yield();
#else
      usleep(10);
#endif
      m1.lock();
    }
  }
public:
/**
 * Pushes an item onto the queue.  This method has strong exception
 * safety if the container is a std::list or std::deque container (the
 * default is std::list), except that if std::deque is used as the
 * container and the copy constructor, move constructor, copy
 * assignment operator or move assignment operator of the queue item
 * throws, it only gives the basic exception guarantee (and the basic
 * guarantee is not given by std::deque if the queue item's move
 * constructor throws and it uses a non-default allocator which does
 * not provide for it to be CopyInsertable).  It is thread safe.
 * @param obj The item to be pushed onto the queue.
 * @exception std::bad_alloc The method might throw std::bad_alloc if
 * memory is exhausted and the system throws in that case.  It might
 * also throw if the copy constructor, move constructor, assignment
 * operator or move assignment operator of the queue item might throw.
 */
  void push(const value_type& obj) {
    Thread::Mutex::Lock lock{mutex};
    q.push(obj);
  }

/**
 * Pushes an item onto the queue.  This method has strong exception
 * safety if the container is a std::list or std::deque container (the
 * default is std::list), except that if std::deque is used as the
 * container and the copy constructor, move constructor, copy
 * assignment operator or move assignment operator of the queue item
 * throws, it only gives the basic exception guarantee (and the basic
 * guarantee is not given by std::deque if the queue item's move
 * constructor throws and it uses a non-default allocator which does
 * not provide for it to be CopyInsertable).  It is thread safe.
 * @param obj The item to be pushed onto the queue.
 * @exception std::bad_alloc The method might throw std::bad_alloc if
 * memory is exhausted and the system throws in that case.  It might
 * also throw if the copy constructor, move constructor, assignment
 * operator or move assignment operator of the queue item might throw.
 *
 * Since 2.0.0-rc5
 */
  void push(value_type&& obj) {
    Thread::Mutex::Lock lock{mutex};
    q.push(std::move(obj));
  }

/**
 * Pushes an item onto the queue by constructing it in place: that is,
 * by passing to this method the item's constructor's arguments,
 * rather than the item itself.  This method has strong exception
 * safety if the container is a std::list or std::deque container (the
 * default is std::list).  (Technically, for a std::deque container,
 * emplace() only offers the same exception guarantees as does push(),
 * namely only the basic guarantee where a copy or move of the queue
 * item throws during the call, but the purpose of emplace is to
 * construct in place and any reasonable implementation will not copy
 * or move the queue item.)  It is thread safe.
 * @param args The constructor arguments for the item to be pushed
 * onto the queue.
 * @exception std::bad_alloc The method might throw std::bad_alloc if
 * memory is exhausted and the system throws in that case.  It might
 * also throw if the item's constructor (including any of its
 * constructor arguments) might throw when constructing the item.
 * @note The constructor of the item pushed onto the queue must not
 * access any of the methods of the same queue object, or a deadlock
 * might occur.
 *
 * Since 2.0.0-rc5
 */
  template<class... Args>
  void emplace(Args&&... args) {
    Thread::Mutex::Lock lock{mutex};
    q.emplace(std::forward<Args>(args)...);
  }

/**
 * Pops an item from the queue.  This method has strong exception
 * safety if the container is a std::deque or std::list container (the
 * default is std::list), provided the destructor of a contained item
 * does not throw.  It is thread safe.
 * @param obj A value type reference to which the item at the front of
 * the queue will be assigned.
 * @exception AsyncQueuePopError If the queue is empty when a pop is
 * attempted, this method will throw AsyncQueuePopError.  It might
 * also throw if the copy assignment operator of the queue item might
 * throw.  In order to complete pop operations atomically under a
 * single lock and to retain strong exception safety, the object into
 * which the popped data is to be placed is passed as an argument by
 * reference (this avoids a copy from a temporary object after the
 * data has been extracted from the queue, which would occur if the
 * item extracted were returned by value).  It might also throw if the
 * destructor of the queue item might throw (but that should never
 * happen), or if the empty() method of the container type throws
 * (which would not happen on any sane implementation).
 */
  void pop(value_type& obj) {
    Thread::Mutex::Lock lock{mutex};
    if (q.empty()) throw AsyncQueuePopError();
    obj = q.front();
    q.pop();
  }

/**
 * Pops an item from the queue using the contained type's move
 * assignment operator, if it has one.  This method is identical to
 * the pop() method if that type has no move assignment operator.
 * This method has strong exception safety if the container is a
 * std::deque or std::list container (the default is std::list),
 * provided the destructor of a contained item does not throw and the
 * move assignment operator of a contained item has strong exception
 * safety.  It is thread safe.  Use this method in preference to the
 * pop() method if it is known that the contained items' move
 * assignment operator does not throw or is strongly exception safe,
 * or if the use case does not require strong exception safety.  This
 * method (or the move_pop_basic() method) must be used in place of
 * the pop() method if the contained type has a move assignment
 * operator but no copy assignment operator (such as a std::unique_ptr
 * object).
 * @param obj A value type reference to which the item at the front of
 * the queue will be move assigned.
 * @exception AsyncQueuePopError If the queue is empty when a pop is
 * attempted, this method will throw AsyncQueuePopError.  It might
 * also throw if the move assignment operator of the queue item might
 * throw, or if it has no move assignment operator and its copy
 * assignment operator throws.  In order to complete pop operations
 * atomically under a single lock and to retain strong exception
 * safety, the object into which the popped data is to be placed is
 * passed as an argument by reference (this avoids a move from a
 * temporary object after the data has been extracted from the queue,
 * which would occur if the item extracted were returned by value).
 * It might also throw if the destructor of the queue item might throw
 * (but that should never happen), or if the empty() method of the
 * container type throws (which would not happen on any sane
 * implementation).
 *
 * Since 2.0.11
 */
  void move_pop(value_type& obj) {
    Thread::Mutex::Lock lock{mutex};
    if (q.empty()) throw AsyncQueuePopError();
    obj = std::move(q.front());
    q.pop();
  }

/**
 * Pops an item from the queue using the contained type's move
 * assignment operator, if it has one, or if not using its copy
 * assignment operator.  This method is only available where the
 * queue's container is a std::list object (which is the default), and
 * does the same as the move_pop() method except that when popping the
 * only thing which is done to the queue's container's internal state
 * within the queue object's mutex is to swap some pointers: in
 * particular, deallocation of the list node at the front of the queue
 * occurs outside that mutex, as does assignment to this method's
 * argument.  Given that, if the queue's container is a list, any new
 * node is also constructed outside the mutex when pushing or
 * emplacing an item onto the queue, this minimizes contention between
 * threads: it gets as close to lock-free performance as it is
 * possible to get with the standard containers.
 *
 * However this minimizing of contention comes at a cost, which is
 * that if the contained item's move assignment operator (or if it has
 * none, its copy assignment operator) throws, then only the basic
 * exception guarantee is offered (hence the name of this method).
 * This means that although the AsyncQueue object would be left in a
 * valid state in the event of such throwing, the item at the front of
 * the queue would be lost.  As in the case of the pop() and
 * move_pop() methods, in addition only the basic exception guarantee
 * is offered if the destructor of the contained item throws.  Only
 * use this method if the queue's container is a std::list object, and
 * if either it is known that the contained item's move assignment
 * operator (or if it has none, its copy assignment operator) does not
 * throw, or the use case does not require strong exception safety.
 * This method is thread safe.
 *
 * If this method is called for an AsyncQueue object whose container
 * is not a std::list object, it will hand off to the move_pop()
 * method, which is separately documented.
 * @param obj A value type reference to which the item at the front of
 * the queue will be move assigned.
 * @exception AsyncQueuePopError If the queue is empty when a pop is
 * attempted, this method will throw AsyncQueuePopError.  It might
 * also throw if the move assignment operator of the queue item might
 * throw or it has no move assignment operator and its copy assignment
 * operator throws (in which case only the basic exception guarantee
 * is offered).  It might also throw if the destructor of the queue
 * item might throw (but that should never happen), if the empty()
 * method of the container type throws (which would not happen on any
 * sane implementation) or if the constructor of the implementation's
 * list allocator throws (which would be highly unusual).  In the
 * event of any of the last two throwing, the strong exception
 * guarantee is offered.
 *
 * Since 2.0.26
 */
// See the specialisation for lists for the implementation of this
// method.  For queues which do not use a list as their container,
// this hands off to move_pop().
  void move_pop_basic(value_type& obj) {
    move_pop(obj);
  }

/**
 * Discards the item at the front of the queue.  This method has
 * strong exception safety if the container is a std::deque or
 * std::list container (the default is std::list), provided the
 * destructor of a contained item does not throw.  It is thread safe.
 * @exception AsyncQueuePopError If the queue is empty when a pop is
 * attempted, this method will throw AsyncQueuePopError.  It might
 * also throw if the destructor of the queue item might throw (but
 * that should never happen), or if the empty() method of the
 * container type throws (which would not happen on any sane
 * implementation).
 */
  void pop() {
    Thread::Mutex::Lock lock{mutex};
    if (q.empty()) throw AsyncQueuePopError();
    q.pop();
  }

/**
 * @return Whether the queue is empty.  It will not throw assuming
 * that the empty() method of the container type does not throw, as it
 * will not on any sane implementation.
 * @note This method is thread safe, but the return value may not be
 * valid if another thread has pushed to or popped from the queue
 * before the value returned by the method is acted on.  It is
 * provided as a utility, but may not be meaningful, depending on the
 * intended usage.
 */
  bool empty() const {
    Thread::Mutex::Lock lock{mutex};
    return q.empty();
  }

/**
 * @return The number of items currently in the queue.  It will not
 * throw assuming that the size() method of the container type does
 * not throw, as it will not on any sane implementation.
 * @note This method is thread safe, but the return value may not be
 * valid if another thread has pushed to or popped from the queue
 * before the value returned by the method is acted on.  It is
 * provided as a utility, but may not be meaningful, depending on the
 * intended usage.
 *
 * Since 2.0.8
 */
  size_type size() const {
    Thread::Mutex::Lock lock{mutex};
    return q.size();
  }

/**
 * Swaps the contents of 'this' and 'other'.  It will not throw
 * assuming that the swap method of the container type does not throw
 * (which the C++11/14 standard requires not to happen with the
 * standard sequence containers).  It is thread safe and the swap is
 * thread-wise atomic.  A non-class function
 * Cgu::swap(Cgu::AsyncQueue&, Cgu::AsyncQueue&) method is also
 * provided which will call this method.
 * @param other The object to be swapped with this one.
 *
 * Since 2.0.8
 */
  void swap(AsyncQueue& other) {
    if (this != &other) {
      lock2(mutex, other.mutex); // doesn't throw
      Thread::Mutex::Lock l1{mutex, Thread::locked};
      Thread::Mutex::Lock l2{other.mutex, Thread::locked};
      q.swap(other.q);
    }
  }

/**
 * The copy assignment operator is strongly exception safe with the
 * standard sequence containers (it uses copy and swap).  It is also
 * thread safe, as it safely locks both the assignor's and assignee's
 * mutex to provide a thread-wise atomic assignment.
 * @param rhs The assignor.
 * @return The AsyncQueue object after assignment.
 * @exception std::bad_alloc The copy constructor of the queue's
 * container type, and so this assignment operator, might throw
 * std::bad_alloc if memory is exhausted and the system throws in that
 * case.  This assignment operator will also throw if the copy
 * constructor of the queue's container type throws any other
 * exceptions, including if any copy or move constructor or copy or
 * move assignment operator of a contained item throws.
 * @exception Thread::MutexError This assignment operator might throw
 * Thread::MutexError if initialization of a transitional object's
 * 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.)
 *
 * Since 2.0.8
 */
  AsyncQueue& operator=(const AsyncQueue& rhs) {
    if (this != &rhs) {
      lock2(mutex, rhs.mutex); // doesn't throw
      Thread::Mutex::Lock l1{mutex, Thread::locked};
      Thread::Mutex::Lock l2{rhs.mutex, Thread::locked};
      std::queue<T, Container> temp{rhs.q};
      q.swap(temp);
    }
    return *this;
  }

/**
 * This move assignment operator is thread safe as regards the
 * assignee (the object moved to), but no synchronization is carried
 * out with respect to the rvalue assignor/movant.  This is because
 * temporaries are only visible and accessible in the thread carrying
 * out the move operation and synchronization for them would represent
 * pointless overhead.  In a case where the user uses std::move to
 * force a move from a named object, and that named object's lifetime
 * is managed by (or the object is otherwise accessed by) a different
 * thread than the one making the move, the user must carry out her
 * own synchronization with respect to that different thread, both to
 * ensure that a consistent view of the the named object is obtained
 * and because that object will be mutated by the move.  This method
 * invokes std::queue's move assignment operator, and therefore has
 * the same exception safety as the standard library's implementation
 * of that operator.  It will not normally throw unless a custom
 * allocator is used which throws on move assignment, or the
 * destructor of a contained item throws.
 * @param rhs The assignor/movant.
 * @return The AsyncQueue object after move assignment.
 *
 * Since 2.0.8
 */
  AsyncQueue& operator=(AsyncQueue&& rhs) {
    Thread::Mutex::Lock lock{mutex};
    q = std::move(rhs.q);
    return *this;
  }

/**
 * @exception std::bad_alloc The default constructor might throw
 * std::bad_alloc if memory is exhausted and the system throws in that
 * case.
 * @exception Thread::MutexError The default constructor might throw
 * Thread::MutexError if initialization 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.)
 */
  AsyncQueue() = default;

/**
 * As regards thread safety, the move constructor does not synchronize
 * with respect to the initializing rvalue.  This is because
 * temporaries are only visible and accessible in the thread carrying
 * out the move operation and synchronization for them would represent
 * pointless overhead.  In a case where a user uses std::move to force
 * a move from a named object, and that named object's lifetime is
 * managed by (or the object is otherwise accessed by) a different
 * thread than the one making the move, the user must carry out her
 * own synchronization with respect to that different thread, both to
 * ensure that a consistent view of the the named object is obtained
 * and because that object will be mutated by the move.
 * @param rhs The AsyncQueue object to be moved.
 * @exception Thread::MutexError The move constructor might throw
 * Thread::MutexError if initialization 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.)  It might also throw if the queue's container type's
 * move constructor might throw, but it should not do that unless a
 * custom allocator is in use.
 * @note If this constructor throws Thread::MutexError, and a named
 * object is moved using std::move, this constructor is not strongly
 * exception safe (items in the moved queue will be lost).  Fixing
 * this efficiently requires changing the order of construction of
 * data members of this class, which cannot be done until the next ABI
 * break for this library as it would alter object layout.  As noted
 * above, in most cases the possibility of Thread::MutexError throwing
 * can be ignored, but where that is not the case and strong exception
 * safety is wanted, the user should either not employ std::move with
 * named objects when invoking this class's constructors, or should
 * construct an AsyncQueue object using the default constructor and
 * then move assign to it.
 *
 * Since 2.0.8
 */
  AsyncQueue(AsyncQueue&& rhs): q(std::move(rhs.q)) {}

/**
 * The copy constructor is thread safe, as it locks the initializing
 * object's mutex to obtain a consistent view of it.
 * @param rhs The AsyncQueue object to be copied.
 * @exception std::bad_alloc The copy constructor of the queue's
 * container type, and so this constructor, might throw std::bad_alloc
 * if memory is exhausted and the system throws in that case.  It will
 * also throw if the copy constructor of the queue's container type
 * throws any other exceptions, including if any copy or move
 * constructor or copy or move assignment operator of a contained item
 * throws.
 * @exception Thread::MutexError The copy constructor might throw
 * Thread::MutexError if initialization 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.)
 *
 * Since 2.0.8
 */
  // we use the comma operator here to lock the mutex and call the
  // copy constructor: the lock will be retained until the end of the
  // full expression in which it is lexically situated, namely until
  // the end of q's constructor - see C++11 1.9/10 and 12.2/3
  AsyncQueue(const AsyncQueue& rhs): q((Thread::Mutex::Lock(rhs.mutex), rhs.q)) {}

/**
 * The destructor does not throw unless the destructor of a contained
 * item throws.  It is thread safe (any thread may delete the
 * AsyncQueue object).
 */
  ~AsyncQueue() {
    // lock and unlock the mutex in the destructor so that we have an
    // acquire operation to ensure that when the std::queue object is
    // destroyed memory is synchronised, so any thread may destroy the
    // AsyncQueue object
    Thread::Mutex::Lock lock{mutex};
  }

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

/**
 * @class AsyncQueueDispatch async_queue.h c++-gtk-utils/async_queue.h
 * @brief A thread-safe asynchronous queue with a blocking pop()
 * method.
 * @sa AsyncQueue AsyncResult
 *
 * AsyncQueueDispatch is a class which has blocking pop_dispatch()
 * and move_pop_dispatch() methods, which allows it to be waited on by a dedicated
 * event/message dispatching thread for incoming work (represented by
 * the data pushed onto the queue).  In the same way, it can be used
 * to implement thread pools, by having threads in the pool waiting on
 * the queue.  The AsyncResult class can be useful for passing results
 * between threads in conjunction with AsyncQueueDispatch (the
 * documentation on AsyncResult gives an example).
 *
 * By default the queue uses a std::list object as its container
 * because when adding an item to the queue all allocation can take
 * place outside the queue object's mutex.  However, for types which
 * have low overhead copy or move constructors, this can be changed
 * to, say, a std::deque object by specifying it as the second
 * template parameter.
 *
 * If the library is installed using the
 * \--with-glib-memory-slices-compat or
 * \--with-glib-memory-slices-no-compat configuration options, any
 * AsyncQueueDispatch objects constructed on free store will be
 * constructed in glib memory slices.  This does not affect the queue
 * container itself: to change the allocator of the C++ container, a
 * custom allocator type can be provided when the AsyncQueueDispatch
 * object is instantiated offering the standard allocator interface.
 * If glib memory slices are not used or no AsyncQueueDispatch objects
 * are constructed on free store, it is not necessary to call
 * g_thread_init() before manipulating or using an AsyncQueueDispatch
 * object in multiple threads, but prior to glib version 2.32 glib
 * itself (and thus glib memory slices) are not thread safe unless
 * that function has been called.
 */

template <class T, class Container = std::list<T> > class AsyncQueueDispatch {
public:
  typedef typename Container::value_type value_type;
  typedef typename Container::size_type size_type;
  typedef Container container_type;
private:
// TODO: put 'q' after 'mutex' and 'cond' at the next ABI break, so
// move construction is strongly exception safe
  std::queue<T, Container> q;
  mutable Thread::Mutex mutex;
  Thread::Cond cond;

// TODO: at the next ABI break make this method explicitly static
// this method won't throw: it is for the user to ensure the arguments
// do not refer to the same mutex object
  void lock2(Thread::Mutex& m1, Thread::Mutex& m2) {
    m1.lock();
    for(;;) {
      if (!m2.trylock()) {
	return;
      }
      m1.unlock();
      // spin nicely
#ifdef CGU_USE_SCHED_YIELD
      sched_yield();
#else
      usleep(10);
#endif
      m1.lock();
    }
  }
public:
/**
 * Pushes an item onto the queue.  This method has strong exception
 * safety if the container is a std::list or std::deque container (the
 * default is std::list), except that if std::deque is used as the
 * container and the copy constructor, move constructor, copy
 * assignment operator or move assignment operator of the queue item
 * throws, it only gives the basic exception guarantee (and the basic
 * guarantee is not given by std::deque if the queue item's move
 * constructor throws and it uses a non-default allocator which does
 * not provide for it to be CopyInsertable).  It is thread safe.
 * @param obj The item to be pushed onto the queue.
 * @exception std::bad_alloc The method might throw std::bad_alloc if
 * memory is exhausted and the system throws in that case.  It might
 * also throw if the copy constructor, move constructor, assignment
 * operator or move assignment operator of the queue item might throw.
 */
  void push(const value_type& obj) {
    Thread::Mutex::Lock lock{mutex};
    q.push(obj);
    cond.signal();
  }

/**
 * Pushes an item onto the queue.  This method has strong exception
 * safety if the container is a std::list or std::deque container (the
 * default is std::list), except that if std::deque is used as the
 * container and the copy constructor, move constructor, copy
 * assignment operator or move assignment operator of the queue item
 * throws, it only gives the basic exception guarantee (and the basic
 * guarantee is not given by std::deque if the queue item's move
 * constructor throws and it uses a non-default allocator which does
 * not provide for it to be CopyInsertable).  It is thread safe.
 * @param obj The item to be pushed onto the queue.
 * @exception std::bad_alloc The method might throw std::bad_alloc if
 * memory is exhausted and the system throws in that case.  It might
 * also throw if the copy constructor, move constructor, assignment
 * operator or move assignment operator of the queue item might throw.
 *
 * Since 2.0.0-rc5
 */
  void push(value_type&& obj) {
    Thread::Mutex::Lock lock{mutex};
    q.push(std::move(obj));
    cond.signal();
  }

/**
 * Pushes an item onto the queue by constructing it in place: that is,
 * by passing to this method the item's constructor's arguments,
 * rather than the item itself.  This method has strong exception
 * safety if the container is a std::list or std::deque container (the
 * default is std::list).  (Technically, for a std::deque container,
 * emplace() only offers the same exception guarantees as does push(),
 * namely only the basic guarantee where a copy or move of the queue
 * item throws during the call, but the purpose of emplace is to
 * construct in place and any reasonable implementation will not copy
 * or move the queue item.)  It is thread safe.
 * @param args The constructor arguments for the item to be pushed
 * onto the queue.
 * @exception std::bad_alloc The method might throw std::bad_alloc if
 * memory is exhausted and the system throws in that case.  It might
 * also throw if the item's constructor (including any of its
 * constructor arguments) might throw when constructing the item.
 * @note The constructor of the item pushed onto the queue must not
 * access any of the methods of the same queue object, or a deadlock
 * might occur.
 *
 * Since 2.0.0-rc5
 */
  template<class... Args>
  void emplace(Args&&... args) {
    Thread::Mutex::Lock lock{mutex};
    q.emplace(std::forward<Args>(args)...);
    cond.signal();
  }

/**
 * Pops an item from the queue.  This method has strong exception
 * safety if the container is a std::deque or std::list container (the
 * default is std::list), provided the destructor of a contained item
 * does not throw.  It is thread safe.
 * @param obj A value type reference to which the item at the front of
 * the queue will be assigned.
 * @exception AsyncQueuePopError If the queue is empty when a pop is
 * attempted, this method will throw AsyncQueuePopError.  It might
 * also throw if the copy assignment operator of the queue item might
 * throw.  In order to complete pop operations atomically under a
 * single lock and to retain strong exception safety, the object into
 * which the popped data is to be placed is passed as an argument by
 * reference (this avoids a copy from a temporary object after the
 * data has been extracted from the queue, which would occur if the
 * item extracted were returned by value).  It might also throw if the
 * destructor of the queue item might throw (but that should never
 * happen), or if the empty() method of the container type throws
 * (which would not happen on any sane implementation).
 */
  void pop(value_type& obj) {
    Thread::Mutex::Lock lock{mutex};
    if (q.empty()) throw AsyncQueuePopError();
    obj = q.front();
    q.pop();
  }

/**
 * Pops an item from the queue using the contained type's move
 * assignment operator, if it has one.  This method is identical to
 * the pop() method if that type has no move assignment operator.
 * This method has strong exception safety if the container is a
 * std::deque or std::list container (the default is std::list),
 * provided the destructor of a contained item does not throw and the
 * move assignment operator of a contained item has strong exception
 * safety.  It is thread safe.  Use this method in preference to the
 * pop() method if it is known that the contained items' move
 * assignment operator does not throw or is strongly exception safe,
 * or if the use case does not require strong exception safety.  This
 * method (or the move_pop_basic() method) must be used in place of
 * the pop() method if the contained type has a move assignment
 * operator but no copy assignment operator (such as a std::unique_ptr
 * object).
 * @param obj A value type reference to which the item at the front of
 * the queue will be move assigned.
 * @exception AsyncQueuePopError If the queue is empty when a pop is
 * attempted, this method will throw AsyncQueuePopError.  It might
 * also throw if the move assignment operator of the queue item might
 * throw, or if it has no move assignment operator and its copy
 * assignment operator throws.  In order to complete pop operations
 * atomically under a single lock and to retain strong exception
 * safety, the object into which the popped data is to be placed is
 * passed as an argument by reference (this avoids a move from a
 * temporary object after the data has been extracted from the queue,
 * which would occur if the item extracted were returned by value).
 * It might also throw if the destructor of the queue item might throw
 * (but that should never happen), or if the empty() method of the
 * container type throws (which would not happen on any sane
 * implementation).
 *
 * Since 2.0.11
 */
  void move_pop(value_type& obj) {
    Thread::Mutex::Lock lock{mutex};
    if (q.empty()) throw AsyncQueuePopError();
    obj = std::move(q.front());
    q.pop();
  }

/**
 * Pops an item from the queue using the contained type's move
 * assignment operator, if it has one, or if not using its copy
 * assignment operator.  This method is only available where the
 * queue's container is a std::list object (which is the default), and
 * does the same as the move_pop() method except that when popping the
 * only thing which is done to the queue's container's internal state
 * within the queue object's mutex is to swap some pointers: in
 * particular, deallocation of the list node at the front of the queue
 * occurs outside that mutex, as does assignment to this method's
 * argument.  Given that, if the queue's container is a list, any new
 * node is also constructed outside the mutex when pushing or
 * emplacing an item onto the queue, this minimizes contention between
 * threads: it gets as close to lock-free performance as it is
 * possible to get with the standard containers.
 *
 * However this minimizing of contention comes at a cost, which is
 * that if the contained item's move assignment operator (or if it has
 * none, its copy assignment operator) throws, then only the basic
 * exception guarantee is offered (hence the name of this method).
 * This means that although the AsyncQueueDispatch object would be
 * left in a valid state in the event of such throwing, the item at
 * the front of the queue would be lost.  As in the case of the pop()
 * and move_pop() methods, in addition only the basic exception
 * guarantee is offered if the destructor of the contained item
 * throws.  Only use this method if the queue's container is a
 * std::list object, and if either it is known that the contained
 * item's move assignment operator (or if it has none, its copy
 * assignment operator) does not throw, or the use case does not
 * require strong exception safety.  This method is thread safe.
 *
 * If this method is called for an AsyncQueueDispatch object whose
 * container is not a std::list object, it will hand off to the
 * move_pop() method, which is separately documented.
 * @param obj A value type reference to which the item at the front of
 * the queue will be move assigned.
 * @exception AsyncQueuePopError If the queue is empty when a pop is
 * attempted, this method will throw AsyncQueuePopError.  It might
 * also throw if the move assignment operator of the queue item might
 * throw or it has no move assignment operator and its copy assignment
 * operator throws (in which case only the basic exception guarantee
 * is offered).  It might also throw if the destructor of the queue
 * item might throw (but that should never happen), if the empty()
 * method of the container type throws (which would not happen on any
 * sane implementation) or if the constructor of the implementation's
 * list allocator throws (which would be highly unusual).  In the
 * event of any of the last two throwing, the strong exception
 * guarantee is offered.
 *
 * Since 2.0.26
 */
// See the specialisation for lists for the implementation of this
// method.  For queues which do not use a list as their container,
// this hands off to move_pop().
  void move_pop_basic(value_type& obj){
    move_pop(obj);
  }

/**
 * Pops an item from the queue.  If the queue is empty, it will block
 * until an item becomes available.  If it blocks, the wait comprises
 * a cancellation point.  This method is cancellation safe if the
 * stack unwinds on cancellation, as cancellation is blocked while the
 * queue is being operated on after coming out of a wait.  This method
 * has strong exception safety if the container is a std::deque or
 * std::list container (the default is std::list), provided the
 * destructor of a contained item does not throw.  It is thread safe.
 * @param obj A value type reference to which the item at the front of
 * the queue will be assigned.  This method might throw if the copy
 * assignment operator of the queue item might throw.  In order to
 * complete pop operations atomically under a single lock and to
 * retain strong exception safety, the object into which the popped
 * data is to be placed is passed as an argument by reference (this
 * avoids a copy from a temporary object after the data has been
 * extracted from the queue, which would occur if the item extracted
 * were returned by value).  It might also throw if the destructor of
 * the queue item might throw (but that should never happen), or if
 * the empty() method of the container type throws (which would not
 * happen on any sane implementation).
 */
  void pop_dispatch(value_type& obj) {
    Thread::Mutex::Lock lock{mutex};
    while (q.empty()) cond.wait(mutex);
    Thread::CancelBlock b;
    obj = q.front();
    q.pop();
  }    

/**
 * Pops an item from the queue using the contained type's move
 * assignment operator, if it has one (this method is identical to the
 * pop_dispatch() method if that type has no move assignment
 * operator).  If the queue is empty, it will block until an item
 * becomes available.  If it blocks, the wait comprises a cancellation
 * point.  This method is cancellation safe if the stack unwinds on
 * cancellation, as cancellation is blocked while the queue is being
 * operated on after coming out of a wait.  This method has strong
 * exception safety if the container is a std::deque or std::list
 * container (the default is std::list), provided the destructor of a
 * contained item does not throw and the move assignment operator of a
 * contained item has strong exception safety.  It is thread safe.
 * Use this method in preference to the pop_dispatch() method if it is
 * known that the contained items' move assignment operator does not
 * throw or is strongly exception safe, or if the use case does not
 * require strong exception safety.  This method (or the
 * move_pop_dispatch_basic() method) must be used in place of the
 * pop_dispatch() method if the contained type has a move assignment
 * operator but no copy assignment operator (such as a std::unique_ptr
 * object).
 * @param obj A value type reference to which the item at the front of
 * the queue will be move assigned.  This method might throw if the
 * move assignment operator of the queue item might throw, or if it
 * has no move assignment operator and its copy assignment operator
 * throws.  In order to complete pop operations atomically under a
 * single lock and to retain strong exception safety, the object into
 * which the popped data is to be placed is passed as an argument by
 * reference (this avoids a move from a temporary object after the
 * data has been extracted from the queue, which would occur if the
 * item extracted were returned by value).  It might also throw if the
 * destructor of the queue item might throw (but that should never
 * happen), or if the empty() method of the container type throws
 * (which would not happen on any sane implementation).
 *
 * Since 2.0.11
 */
  void move_pop_dispatch(value_type& obj) {
    Thread::Mutex::Lock lock{mutex};
    while (q.empty()) cond.wait(mutex);
    Thread::CancelBlock b;
    obj = std::move(q.front());
    q.pop();
  }    

/**
 * Pops an item from the queue using the contained type's move
 * assignment operator, if it has one, or if not using its copy
 * assignment operator.  This method is only available where the
 * queue's container is a std::list object (which is the default), and
 * does the same as the move_pop_dispatch() method except that when
 * popping the only thing which is done to the queue's container's
 * internal state within the queue object's mutex is to swap some
 * pointers: in particular, deallocation of the list node at the front
 * of the queue occurs outside that mutex, as does assignment to this
 * method's argument.  Given that, if the queue's container is a list,
 * any new node is also constructed outside the mutex when pushing or
 * emplacing an item onto the queue, this minimizes contention between
 * threads: it gets as close to lock-free performance as it is
 * possible to get with the standard containers.
 *
 * However this minimizing of contention comes at a cost, which is
 * that if the contained item's move assignment operator (or if it has
 * none, its copy assignment operator) throws, then only the basic
 * exception guarantee is offered (hence the name of this method).
 * This means that although the AsyncQueueDispatch object would be
 * left in a valid state in the event of such throwing, the item at
 * the front of the queue would be lost.  As in the case of the
 * pop_dispatch() and move_pop_dispatch() methods, in addition only
 * the basic exception guarantee is offered if the destructor of the
 * contained item throws.  Only use this method if the queue's
 * container is a std::list object, and if either it is known that the
 * contained item's move assignment operator (or if it has none, its
 * copy assignment operator) does not throw, or the use case does not
 * require strong exception safety.  This method is thread safe.
 *
 * If the queue is empty, it will block until an item
 * becomes available.  If it blocks, the wait comprises a cancellation
 * point.  This method is cancellation safe if the stack unwinds on
 * cancellation, as cancellation is blocked while the queue is being
 * operated on after coming out of a wait.
 *
 * If this method is called for an AsyncQueueDispatch object whose
 * container is not a std::list object, it will hand off to the
 * move_pop_dispatch() method, which is separately documented.
 * @param obj A value type reference to which the item at the front of
 * the queue will be move assigned.  This method might throw if the
 * move assignment operator of the queue item might throw or it has no
 * move assignment operator and its copy assignment operator throws
 * (in which case only the basic exception guarantee is offered).  It
 * might also throw if the destructor of the queue item might throw
 * (but that should never happen), if the empty() method of the
 * container type throws (which would not happen on any sane
 * implementation) or if the constructor of the implementation's list
 * allocator throws (which would be highly unusual).  In the event of
 * any of the last two throwing, the strong exception guarantee is
 * offered.
 *
 * Since 2.0.26
 */
// See the specialisation for lists for the implementation of this
// method.  For queues which do not use a list as their container,
// this hands off to move_pop_dispatch().
  void move_pop_dispatch_basic(value_type& obj) {
    move_pop_dispatch(obj);
  }

/**
 * Pops an item from the queue.  If the queue is empty, it will block
 * until an item becomes available or until the timeout expires.  If
 * it blocks, the wait comprises a cancellation point.  This method is
 * cancellation safe if the stack unwinds on cancellation, as
 * cancellation is blocked while the queue is being operated on after
 * coming out of a wait.  This method has strong exception safety if
 * the container is a std::deque or std::list container (the default
 * is std::list), provided the destructor of a contained item does not
 * throw.  It is thread safe.
 * @param obj A value type reference to which the item at the front of
 * the queue will be assigned.  This method might throw if the copy
 * assignment operator of the queue item might throw.  In order to
 * complete pop operations atomically under a single lock and to
 * retain strong exception safety, the object into which the popped
 * data is to be placed is passed as an argument by reference (this
 * avoids a copy from a temporary object after the data has been
 * extracted from the queue, which would occur if the item extracted
 * were returned by value).  It might also throw if the destructor of
 * the queue item might throw (but that should never happen), or if
 * the empty() method of the container type throws (which would not
 * happen on any sane implementation).
 * @param millisec The timeout interval, in milliseconds.
 * @return If the timeout expires without an item becoming available,
 * the method will return true.  If an item from the queue is
 * extracted, it returns false.
 */
  bool pop_timed_dispatch(value_type& obj, unsigned int millisec) {
    timespec ts;
    Thread::Cond::get_abs_time(ts, millisec);
    Thread::Mutex::Lock lock{mutex};
    while (q.empty()) {
      if (cond.timed_wait(mutex, ts)) return true;
    }
    Thread::CancelBlock b;
    obj = q.front();
    q.pop();
    return false;
  }

/**
 * Pops an item from the queue using the contained type's move
 * assignment operator, if it has one (this method is identical to the
 * pop_timed_dispatch() method if that type has no move assignment
 * operator).  If the queue is empty, it will block until an item
 * becomes available or until the timeout expires.  If it blocks, the
 * wait comprises a cancellation point.  This method is cancellation
 * safe if the stack unwinds on cancellation, as cancellation is
 * blocked while the queue is being operated on after coming out of a
 * wait.  This method has strong exception safety if the container is
 * a std::deque or std::list container (the default is std::list),
 * provided the destructor of a contained item does not throw and the
 * move assignment operator of a contained item has strong exception
 * safety.  It is thread safe.  Use this method in preference to the
 * pop_timed_dispatch() method if it is known that the contained
 * items' move assignment operator does not throw or is strongly
 * exception safe, or if the use case does not require strong
 * exception safety.  This method (or the
 * move_pop_timed_dispatch_basic() method) must be used in place of
 * the pop_timed_dispatch() method if the contained type has a move
 * assignment operator but no copy assignment operator (such as a
 * std::unique_ptr object).
 * @param obj A value type reference to which the item at the front of
 * the queue will be move assigned.  This method might throw if the
 * move assignment operator of the queue item might throw, or if it
 * has no move assignment operator and its copy assignment operator
 * throws.  In order to complete pop operations atomically under a
 * single lock and to retain strong exception safety, the object into
 * which the popped data is to be placed is passed as an argument by
 * reference (this avoids a move from a temporary object after the
 * data has been extracted from the queue, which would occur if the
 * item extracted were returned by value).  It might also throw if the
 * destructor of the queue item might throw (but that should never
 * happen), or if the empty() method of the container type throws
 * (which would not happen on any sane implementation).
 * @param millisec The timeout interval, in milliseconds.
 * @return If the timeout expires without an item becoming available,
 * the method will return true.  If an item from the queue is
 * extracted, it returns false.
 *
 * Since 2.0.11
 */
  bool move_pop_timed_dispatch(value_type& obj, unsigned int millisec) {
    timespec ts;
    Thread::Cond::get_abs_time(ts, millisec);
    Thread::Mutex::Lock lock{mutex};
    while (q.empty()) {
      if (cond.timed_wait(mutex, ts)) return true;
    }
    Thread::CancelBlock b;
    obj = std::move(q.front());
    q.pop();
    return false;
  }

/**
 * Pops an item from the queue using the contained type's move
 * assignment operator, if it has one, or if not using its copy
 * assignment operator.  This method is only available where the
 * queue's container is a std::list object (which is the default), and
 * does the same as the move_pop_timed_dispatch() method except that
 * when popping the only thing which is done to the queue's
 * container's internal state within the queue object's mutex is to
 * swap some pointers: in particular, deallocation of the list node at
 * the front of the queue occurs outside that mutex, as does
 * assignment to this method's argument.  Given that, if the queue's
 * container is a list, any new node is also constructed outside the
 * mutex when pushing or emplacing an item onto the queue, this
 * minimizes contention between threads: it gets as close to lock-free
 * performance as it is possible to get with the standard containers.
 *
 * However this minimizing of contention comes at a cost, which is
 * that if the contained item's move assignment operator (or if it has
 * none, its copy assignment operator) throws, then only the basic
 * exception guarantee is offered (hence the name of this method).
 * This means that although the AsyncQueueDispatch object would be
 * left in a valid state in the event of such throwing, the item at
 * the front of the queue would be lost.  As in the case of the
 * pop_timed_dispatch() and move_pop_timed_dispatch() methods, in
 * addition only the basic exception guarantee is offered if the
 * destructor of the contained item throws.  Only use this method if
 * the queue's container is a std::list object, and if either it is
 * known that the contained item's move assignment operator (or if it
 * has none, its copy assignment operator) does not throw, or the use
 * case does not require strong exception safety.  This method is
 * thread safe.
 *
 * If the queue is empty, it will block until an item becomes
 * available or until the timeout expires.  If it blocks, the wait
 * comprises a cancellation point.  This method is cancellation safe
 * if the stack unwinds on cancellation, as cancellation is blocked
 * while the queue is being operated on after coming out of a wait.
 *
 * If this method is called for an AsyncQueueDispatch object whose
 * container is not a std::list object, it will hand off to the
 * move_pop_timed_dispatch() method, which is separately documented.
 * @param obj A value type reference to which the item at the front of
 * the queue will be move assigned.  This method might throw if the
 * move assignment operator of the queue item might throw or it has no
 * move assignment operator and its copy assignment operator throws
 * (in which case only the basic exception guarantee is offered).  It
 * might also throw if the destructor of the queue item might throw
 * (but that should never happen), if the empty() method of the
 * container type throws (which would not happen on any sane
 * implementation) or if the constructor of the implementation's list
 * allocator throws (which would be highly unusual).  In the event of
 * any of the last two throwing, the strong exception guarantee is
 * offered.
 * @param millisec The timeout interval, in milliseconds.
 * @return If the timeout expires without an item becoming available,
 * the method will return true.  If an item from the queue is
 * extracted, it returns false.
 *
 * Since 2.0.26
 */
// See the specialisation for lists for the implementation of this
// method.  For queues which do not use a list as their container,
// this hands off to move_pop_timed_dispatch().
  bool move_pop_timed_dispatch_basic(value_type& obj, unsigned int millisec) {
    return move_pop_timed_dispatch(obj, millisec);
  }

/**
 * Discards the item at the front of the queue.  This method has
 * strong exception safety if the container is a std::deque or
 * std::list container (the default is std::list), provided the
 * destructor of a contained item does not throw.  It is thread safe.
 * @exception AsyncQueuePopError If the queue is empty when a pop is
 * attempted, this method will throw AsyncQueuePopError.  It might
 * also throw if the destructor of the queue item might throw (but
 * that should never happen), or if the empty() method of the
 * container type throws (which would not happen on any sane
 * implementation).
 */
  void pop() {
    Thread::Mutex::Lock lock{mutex};
    if (q.empty()) throw AsyncQueuePopError();
    q.pop();
  }

/**
 * @return Whether the queue is empty.  It will not throw assuming
 * that the empty() method of the container type does not throw, as it
 * will not on any sane implementation.
 * @note This method is thread safe, but the return value may not be
 * valid if another thread has pushed to or popped from the queue
 * before the value returned by the method is acted on.  It is
 * provided as a utility, but may not be meaningful, depending on the
 * intended usage.
 */
  bool empty() const {
    Thread::Mutex::Lock lock{mutex};
    return q.empty();
  }

/**
 * @return The number of items currently in the queue.  It will not
 * throw assuming that the size() method of the container type does
 * not throw, as it will not on any sane implementation.
 * @note This method is thread safe, but the return value may not be
 * valid if another thread has pushed to or popped from the queue
 * before the value returned by the method is acted on.  It is
 * provided as a utility, but may not be meaningful, depending on the
 * intended usage.
 *
 * Since 2.0.8
 */
  size_type size() const {
    Thread::Mutex::Lock lock{mutex};
    return q.size();
  }

/**
 * Swaps the contents of 'this' and 'other'.  It will not throw
 * assuming that the swap method of the container type does not throw
 * (which the C++11/14 standard requires not to happen with the
 * standard sequence containers).  It is thread safe and the swap is
 * thread-wise atomic.  A non-class function
 * Cgu::swap(Cgu::AsyncQueueDispatch&, Cgu::AsyncQueueDispatch&)
 * method is also provided which will call this method.
 * @param other The object to be swapped with this one.
 * @note An object swapped does not, by virtue of the swap, inherit
 * any threads waiting on the other one.  However if threads were
 * waiting on a swapped object prior to the swap, and it acquires
 * items by virtue of the swap, the waiting threads will unblock and
 * extract those items.
 *
 * Since 2.0.8
 */
  void swap(AsyncQueueDispatch& other) {
    if (this != &other) {
      lock2(mutex, other.mutex); // doesn't throw
      Thread::Mutex::Lock l1{mutex, Thread::locked};
      Thread::Mutex::Lock l2{other.mutex, Thread::locked};
      q.swap(other.q);
      if (!q.empty()) cond.broadcast();
      if (!other.q.empty()) other.cond.broadcast();
    }
  }

/**
 * The copy assignment operator is strongly exception safe with the
 * standard sequence containers (it uses copy and swap).  It is also
 * thread safe, as it safely locks both the assignor's and assignee's
 * mutex to provide a thread-wise atomic assignment.
 * @param rhs The assignor.
 * @return The AsyncQueueDispatch object after assignment.
 * @exception std::bad_alloc The copy constructor of the queue's
 * container type, and so this assignment operator, might throw
 * std::bad_alloc if memory is exhausted and the system throws in that
 * case.  This assignment operator will also throw if the copy
 * constructor of the queue's container type throws any other
 * exceptions, including if any copy or move constructor or copy or
 * move assignment operator of a contained item throws.
 * @exception Thread::MutexError This assignment operator might
 * throw Thread::MutexError if initialization of a transitional
 * object's 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 Thread::CondError This assignment operator might throw
 * Thread::CondError if initialisation of a transitional object's
 * 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 The assignee does not, by virtue of the assignment, inherit
 * any threads waiting on the assignor.  However, if prior to the
 * assignment threads were waiting on the assignee and the assignee
 * acquires items from the assignor as a result of the assignment, the
 * waiting threads will unblock and extract those items.
 *
 * Since 2.0.8
 */
  AsyncQueueDispatch& operator=(const AsyncQueueDispatch& rhs) {
    if (this != &rhs) {
      lock2(mutex, rhs.mutex); // doesn't throw
      Thread::Mutex::Lock l1{mutex, Thread::locked};
      Thread::Mutex::Lock l2{rhs.mutex, Thread::locked};
      std::queue<T, Container> temp{rhs.q};
      q.swap(temp);
      if (!q.empty()) cond.broadcast();
    }
    return *this;
  }

/**
 * This move assignment operator is thread safe as regards the
 * assignee (the object moved to), but no synchronization is carried
 * out with respect to the rvalue assignor/movant.  This is because
 * temporaries are only visible and accessible in the thread carrying
 * out the move operation and synchronization for them would represent
 * pointless overhead.  In a case where the user uses std::move to
 * force a move from a named object, and that named object's lifetime
 * is managed by (or the object is otherwise accessed by) a different
 * thread than the one making the move, the user must carry out her
 * own synchronization with respect to that different thread, both to
 * ensure that a consistent view of the the named object is obtained
 * and because that object will be mutated by the move.  This method
 * invokes std::queue's move assignment operator, and therefore has
 * the same exception safety as the standard library's implementation
 * of that operator.  It will not normally throw unless a custom
 * allocator is used which throws on move assignment, or the
 * destructor of a contained item throws.
 * @param rhs The assignor/movant.
 * @return The AsyncQueueDispatch object after move assignment.
 * @note The assignee does not, by virtue of the move, inherit any
 * threads waiting on the assignor/movant.  However, if prior to the
 * move threads were waiting on the assignee and the assignee acquires
 * items from the assignor/movant as a result of the move, from
 * version 2.0.9 the waiting threads will unblock and extract those
 * items (such unblocking on move assignment did not happen with
 * version 2.0.8, which was a bug).
 *
 * Since 2.0.8
 */
  AsyncQueueDispatch& operator=(AsyncQueueDispatch&& rhs) {
    Thread::Mutex::Lock lock{mutex};
    q = std::move(rhs.q);
    if (!q.empty()) cond.broadcast();
    return *this;
  }

/**
 * @exception std::bad_alloc The default constructor might throw this
 * exception if memory is exhausted and the system throws in that
 * case.
 * @exception Thread::MutexError The default constructor might throw
 * this exception if initialisation of the contained mutex fails.  (It
 * is often not worth checking for this, as it means either memory is
 * exhausted or pthread has run out of other resources to create new
 * mutexes.)
 * @exception Thread::CondError The default constructor might throw
 * this exception if initialisation of the contained condition
 * variable fails.  (It is often not worth checking for this, as it
 * means either memory is exhausted or pthread has run out of other
 * resources to create new condition variables.)
 */
  AsyncQueueDispatch() = default;

/**
 * As regards thread safety, the move constructor does not synchronize
 * with respect to the initializing rvalue.  This is because
 * temporaries are only visible and accessible in the thread carrying
 * out the move operation and synchronization for them would represent
 * pointless overhead.  In a case where a user uses std::move to force
 * a move from a named object, and that named object's lifetime is
 * managed by (or the object is otherwise accessed by) a different
 * thread than the one making the move, the user must carry out her
 * own synchronization with respect to that different thread, both to
 * ensure that a consistent view of the the named object is obtained
 * and because that object will be mutated by the move.
 * @param rhs The AsyncQueueDispatch object to be moved.
 * @exception Thread::MutexError The move constructor might throw
 * Thread::MutexError if initialization 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.)  It might also throw if the queue's container type's
 * move constructor might throw, but it should not do that unless a
 * custom allocator is in use.
 * @exception Thread::CondError The move constructor might throw this
 * exception if initialisation of the contained condition variable
 * fails.  (It is often not worth checking for this, as it means
 * either memory is exhausted or pthread has run out of other
 * resources to create new condition variables.)  It might also throw
 * if the queue's container type's move constructor might throw, but
 * it should not do that unless a custom allocator is in use.
 * @note If this constructor throws Thread::MutexError or
 * Thread::CondError, and a named object is moved using std::move,
 * this constructor is not strongly exception safe (items in the moved
 * queue will be lost).  Fixing this efficiently requires changing the
 * order of construction of data members of this class, which cannot
 * be done without an ABI break for this library as it would alter
 * object layout: this is therefore fixed in the 2.2 series of the
 * library but not in this series.  As noted above, in most cases the
 * possibility of Thread::MutexError or Thread::CondError throwing can
 * be ignored, but where that is not the case and strong exception
 * safety is wanted, the user should either not employ std::move with
 * named objects when invoking this class's constructors, or should
 * construct an AsyncQueueDispatch object using the default
 * constructor and then move assign to it.
 *
 * Since 2.0.8
 */
  AsyncQueueDispatch(AsyncQueueDispatch&& rhs): q(std::move(rhs.q)) {}

/**
 * The copy constructor is thread safe, as it locks the initializing
 * object's mutex to obtain a consistent view of it.
 * @param rhs The AsyncQueueDispatch object to be copied.
 * @exception std::bad_alloc The copy constructor of the queue's
 * container type, and so this constructor, might throw std::bad_alloc
 * if memory is exhausted and the system throws in that case.  It will
 * also throw if the copy constructor of the queue's container type
 * throws any other exceptions, including if any copy or move
 * constructor or copy or move assignment operator of a contained item
 * throws.
 * @exception Thread::MutexError The copy constructor might throw
 * Thread::MutexError if initialization 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 Thread::CondError The copy constructor might throw this
 * exception if initialisation of the contained condition variable
 * fails.  (It is often not worth checking for this, as it means
 * either memory is exhausted or pthread has run out of other
 * resources to create new condition variables.)
 *
 * Since 2.0.8
 */
  // we use the comma operator here to lock the mutex and call the
  // copy constructor: the lock will be retained until the end of the
  // full expression in which it is lexically situated, namely until
  // the end of q's constructor - see C++11 1.9/10 and 12.2/3
  AsyncQueueDispatch(const AsyncQueueDispatch& rhs):
                        q((Thread::Mutex::Lock(rhs.mutex), rhs.q)) {}

/**
 * The destructor does not throw unless the destructor of a contained
 * item throws.  It is thread safe (any thread may delete the
 * AsyncQueueDispatch object).  Destroying an AsyncQueueDispatch
 * object on which another thread is currently blocked results in
 * undefined behavior.
 */
  ~AsyncQueueDispatch() {
    // lock and unlock the mutex in the destructor so that we have an
    // acquire operation to ensure that when the std::queue object is
    // destroyed memory is synchronised, so any thread may destroy the
    // AsyncQueueDispatch object
    Thread::Mutex::Lock lock{mutex};
  }

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

/**
 * Swaps the contents of two AsyncQueue objects.  It will not throw
 * assuming that the swap method of the container type does not throw
 * (which the C++11/13 standard requires not to happen with the
 * standard sequence containers).  It is thread safe and the swap is
 * thread-wise atomic.
 * @param q1 An object to be swapped with the other.
 * @param q2 An object to be swapped with the other.
 * @note Calling std::swap on AsyncQueue objects is thread safe but
 * does not provide a thread-wise atomic swap (the swapped objects may
 * not be mirror images if during the execution of std::swap's default
 * algorithm one of them has been modified), although in many cases
 * that doesn't matter.  If swap() is called without a namespace
 * qualifier, argument dependent look-up will pick this one correctly.
 *
 * Since 2.0.8
 */
template <class T, class Container>
void swap(Cgu::AsyncQueue<T, Container>& q1,
	  Cgu::AsyncQueue<T, Container>& q2) {
  q1.swap(q2);
}

/**
 * Swaps the contents of two AsyncQueueDispatch objects.  It will not
 * throw assuming that the swap method of the container type does not
 * throw (which the C++11/13 standard requires not to happen with the
 * standard sequence containers).  It is thread safe and the swap is
 * thread-wise atomic.
 * @param q1 An object to be swapped with the other.
 * @param q2 An object to be swapped with the other.
 * @note 1. An object swapped does not, by virtue of the swap, inherit
 * any threads waiting on the other one.  However if threads were
 * waiting on a swapped object prior to the swap, and it acquires
 * items by virtue of the swap, the waiting threads will unblock and
 * extract those items.
 * @note 2. Calling std::swap on AsyncQueueDispatch objects is thread
 * safe but does not provide a thread-wise atomic swap (the swapped
 * objects may not be mirror images if during the execution of
 * std::swap's default algorithm one of them has been modified),
 * although in many cases that doesn't matter.  If swap() is called
 * without a namespace qualifier, argument dependent look-up will pick
 * this one correctly.
 *
 * Since 2.0.8
 */
template <class T, class Container>
void swap(Cgu::AsyncQueueDispatch<T, Container>& q1,
	  Cgu::AsyncQueueDispatch<T, Container>& q2) {
  q1.swap(q2);
}

#if defined(CGU_USE_INHERITABLE_QUEUE) && !defined(DOXYGEN_PARSING)

/* This is a specialization of AsyncQueue for std::list objects, which
   uses std::list::splice() to push or emplace a new item on the
   queue.  This means that allocation for the push or emplacement
   occurs outside the mutex, so reducing contention (a tip from a talk
   by Sean Parent of Adobe).  This is first available in version
   2.0.20.
 */
template <class T, class Allocator>
class AsyncQueue<T, std::list<T, Allocator> > {
public:
  typedef std::list<T, Allocator> Container;
  typedef typename Container::value_type value_type;
  typedef typename Container::size_type size_type;
  typedef Container container_type;
private:
  // 23.6.3.1 of C++11 requires std::queue to have a protected
  // container member called 'c' for the purposes of derivation.  This
  // specialisation will have the same binary layout as the
  // unspecialized version on any practical implementation: all we do
  // is add splice_end() and unsplice_beginning() members
  class Q: public std::queue<T, Container> {
   public:
    void splice_end(Container&& lst) {
      this->c.splice(this->c.end(), std::move(lst));
    }
    void unsplice_beginning(Container& lst) {
      lst.splice(lst.begin(), this->c, this->c.begin());
    }
  } q;
  mutable Thread::Mutex mutex;

// TODO: at the next ABI break make this method explicitly static
  void lock2(Thread::Mutex& m1, Thread::Mutex& m2) {
    m1.lock();
    for(;;) {
      if (!m2.trylock()) {
	return;
      }
      m1.unlock();
      // spin nicely
#ifdef CGU_USE_SCHED_YIELD
      sched_yield();
#else
      usleep(10);
#endif
      m1.lock();
    }
  }
public:
  void push(const value_type& obj) {
    Container temp{obj};
    Thread::Mutex::Lock lock{mutex};
    // splice_end doesn't throw
    q.splice_end(std::move(temp));
  }

  void push(value_type&& obj) {
    // although move intialisation of a std::list object via an
    // initializer list with a single element is almost certain to be
    // strongly exception safe, it is not mandated by the standard so
    // use push_back() (which is required to be strongly exception
    // safe)
    Container temp;
    temp.push_back(std::move(obj));
    Thread::Mutex::Lock lock{mutex};
    // splice_end doesn't throw
    q.splice_end(std::move(temp));
  }

  template<class... Args>
  void emplace(Args&&... args) {
    Container temp;
    temp.emplace_back(std::forward<Args>(args)...);
    Thread::Mutex::Lock lock{mutex};
    // splice_end doesn't throw
    q.splice_end(std::move(temp));
  }

  void pop(value_type& obj) {
    Thread::Mutex::Lock lock{mutex};
    if (q.empty()) throw AsyncQueuePopError();
    obj = q.front();
    q.pop();
  }

  void move_pop(value_type& obj) {
    Thread::Mutex::Lock lock{mutex};
    if (q.empty()) throw AsyncQueuePopError();
    obj = std::move(q.front());
    q.pop();
  }

  void move_pop_basic(value_type& obj) {
    // the standard does not require it, but in practice constructing
    // an empty list will not throw unless constructing its allocator
    // throws
    Container temp;
    {
      Thread::Mutex::Lock lock{mutex};
      if (q.empty()) throw AsyncQueuePopError();
      // unsplice_beginning doesn't throw
      q.unsplice_beginning(temp);
    }
    obj = std::move(temp.front());
  }

  void pop() {
    Thread::Mutex::Lock lock{mutex};
    if (q.empty()) throw AsyncQueuePopError();
    q.pop();
  }

  bool empty() const {
    Thread::Mutex::Lock lock{mutex};
    return q.empty();
  }

  size_type size() const {
    Thread::Mutex::Lock lock{mutex};
    return q.size();
  }

  void swap(AsyncQueue& other) {
    if (this != &other) {
      lock2(mutex, other.mutex); // doesn't throw
      Thread::Mutex::Lock l1{mutex, Thread::locked};
      Thread::Mutex::Lock l2{other.mutex, Thread::locked};
      q.swap(other.q);
    }
  }

  AsyncQueue& operator=(const AsyncQueue& rhs) {
    if (this != &rhs) {
      lock2(mutex, rhs.mutex); // doesn't throw
      Thread::Mutex::Lock l1{mutex, Thread::locked};
      Thread::Mutex::Lock l2{rhs.mutex, Thread::locked};
      Q temp{rhs.q};
      q.swap(temp);
    }
    return *this;
  }

  AsyncQueue& operator=(AsyncQueue&& rhs) {
    Thread::Mutex::Lock lock{mutex};
    q = std::move(rhs.q);
    return *this;
  }

  AsyncQueue() = default;

  AsyncQueue(AsyncQueue&& rhs): q(std::move(rhs.q)) {}

  AsyncQueue(const AsyncQueue& rhs): q((Thread::Mutex::Lock(rhs.mutex), rhs.q)) {}

  ~AsyncQueue() {
    Thread::Mutex::Lock lock{mutex};
  }

  CGU_GLIB_MEMORY_SLICES_FUNCS
};

/* This is a specialization of AsyncQueueDispatch for std::list
   objects, which uses std::list::splice() to push or emplace a new
   item on the queue.  This means that allocation for the push or
   emplacement occurs outside the mutex, so reducing contention (a tip
   from a talk by Sean Parent of Adobe).  This is first available in
   version 2.0.20.
 */
template <class T, class Allocator>
class AsyncQueueDispatch<T, std::list<T, Allocator> > {
public:
  typedef std::list<T, Allocator> Container;
  typedef typename Container::value_type value_type;
  typedef typename Container::size_type size_type;
  typedef Container container_type;
private:
  // 23.6.3.1 of C++11 requires std::queue to have a protected
  // container member called 'c' for the purposes of derivation.  This
  // specialisation will have the same binary layout as the
  // unspecialized version on any practical implementation: all we do
  // is add splice_end() and unsplice_beginning() members
  class Q: public std::queue<T, Container> {
   public:
    void splice_end(Container&& lst) {
      this->c.splice(this->c.end(), std::move(lst));
    }
    void unsplice_beginning(Container& lst) {
      lst.splice(lst.begin(), this->c, this->c.begin());
    }
  } q;
  mutable Thread::Mutex mutex;
  Thread::Cond cond;

// TODO: at the next ABI break make this method explicitly static
  void lock2(Thread::Mutex& m1, Thread::Mutex& m2) {
    m1.lock();
    for(;;) {
      if (!m2.trylock()) {
	return;
      }
      m1.unlock();
      // spin nicely
#ifdef CGU_USE_SCHED_YIELD
      sched_yield();
#else
      usleep(10);
#endif
      m1.lock();
    }
  }
public:
  void push(const value_type& obj) {
    Container temp{obj};
    Thread::Mutex::Lock lock{mutex};
    // splice_end doesn't throw
    q.splice_end(std::move(temp));
    cond.signal();
  }

  void push(value_type&& obj) {
    // although move intialisation of a std::list object via an
    // initializer list with a single element is almost certain to be
    // strongly exception safe, it is not mandated by the standard so
    // use push_back() (which is required to be strongly exception
    // safe)
    Container temp;
    temp.push_back(std::move(obj));
    Thread::Mutex::Lock lock{mutex};
    // splice_end doesn't throw
    q.splice_end(std::move(temp));
    cond.signal();
  }

  template<class... Args>
  void emplace(Args&&... args) {
    Container temp;
    temp.emplace_back(std::forward<Args>(args)...);
    Thread::Mutex::Lock lock{mutex};
    // splice_end doesn't throw
    q.splice_end(std::move(temp));
    cond.signal();
  }

  void pop(value_type& obj) {
    Thread::Mutex::Lock lock{mutex};
    if (q.empty()) throw AsyncQueuePopError();
    obj = q.front();
    q.pop();
  }

  void move_pop(value_type& obj) {
    Thread::Mutex::Lock lock{mutex};
    if (q.empty()) throw AsyncQueuePopError();
    obj = std::move(q.front());
    q.pop();
  }

  void move_pop_basic(value_type& obj) {
    // the standard does not require it, but in practice constructing
    // an empty list will not throw unless constructing its allocator
    // throws
    Container temp;
    {
      Thread::Mutex::Lock lock{mutex};
      if (q.empty()) throw AsyncQueuePopError();
      // unsplice_beginning doesn't throw
      q.unsplice_beginning(temp);
    }
    obj = std::move(temp.front());
  }

  void pop_dispatch(value_type& obj) {
    Thread::Mutex::Lock lock{mutex};
    while (q.empty()) cond.wait(mutex);
    Thread::CancelBlock b;
    obj = q.front();
    q.pop();
  }    

  void move_pop_dispatch(value_type& obj) {
    Thread::Mutex::Lock lock{mutex};
    while (q.empty()) cond.wait(mutex);
    Thread::CancelBlock b;
    obj = std::move(q.front());
    q.pop();
  }    

  void move_pop_dispatch_basic(value_type& obj) {
    int old_state;
    int ignore;
    bool cancelstate_restored = false;
    try {
      pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &old_state);
      // the standard does not require it, but in practice
      // constructing an empty list will not throw unless constructing
      // its allocator throws
      Container temp;
      pthread_setcancelstate(old_state, &ignore);
      cancelstate_restored = true;
      Thread::Mutex::TrackLock lock{mutex};
      while (q.empty()) cond.wait(mutex);
      pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &ignore);
      cancelstate_restored = false;
      // unsplice_beginning doesn't throw
      q.unsplice_beginning(temp);
      lock.unlock();
      obj = std::move(temp.front());
      pthread_setcancelstate(old_state, &ignore);
    }
    catch (...) {
      // in practice we could only enter here with
      // cancelstate_restored set as true if there has been a
      // cancellation pseudo-exception (but the code doesn't depend on
      // that).  We could only enter here with a normal exception if
      // the construction of temp has thrown or if a queue element's
      // move/copy assignment operator has thrown (but again the code
      // doesn't depend on that).
      if (!cancelstate_restored) {
	pthread_setcancelstate(old_state, &ignore);
      }
      throw;
    }
  }    

  bool pop_timed_dispatch(value_type& obj, unsigned int millisec) {
    timespec ts;
    Thread::Cond::get_abs_time(ts, millisec);
    Thread::Mutex::Lock lock{mutex};
    while (q.empty()) {
      if (cond.timed_wait(mutex, ts)) return true;
    }
    Thread::CancelBlock b;
    obj = q.front();
    q.pop();
    return false;
  }

  bool move_pop_timed_dispatch(value_type& obj, unsigned int millisec) {
    timespec ts;
    Thread::Cond::get_abs_time(ts, millisec);
    Thread::Mutex::Lock lock{mutex};
    while (q.empty()) {
      if (cond.timed_wait(mutex, ts)) return true;
    }
    Thread::CancelBlock b;
    obj = std::move(q.front());
    q.pop();
    return false;
  }

  bool move_pop_timed_dispatch_basic(value_type& obj, unsigned int millisec) {
    timespec ts;
    Thread::Cond::get_abs_time(ts, millisec);
    int old_state;
    int ignore;
    bool cancelstate_restored = false;
    try {
      pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &old_state);
      // the standard does not require it, but in practice
      // constructing an empty list will not throw unless constructing
      // its allocator throws
      Container temp;
      pthread_setcancelstate(old_state, &ignore);
      cancelstate_restored = true;
      Thread::Mutex::TrackLock lock{mutex};
      while (q.empty()) {
	if (cond.timed_wait(mutex, ts)) return true;
      }
      pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &ignore);
      cancelstate_restored = false;
      // unsplice_beginning doesn't throw
      q.unsplice_beginning(temp);
      lock.unlock();
      obj = std::move(temp.front());
      pthread_setcancelstate(old_state, &ignore);
      return false;
    }
    catch (...) {
      // in practice we could only enter here with
      // cancelstate_restored set as true if there has been a
      // cancellation pseudo-exception (but the code doesn't depend on
      // that).  We could only enter here with a normal exception if
      // the construction of temp has thrown or if a queue element's
      // move/copy assignment operator has thrown (but again the code
      // doesn't depend on that).
      if (!cancelstate_restored) {
	pthread_setcancelstate(old_state, &ignore);
      }
      throw;
    }
  }

  void pop() {
    Thread::Mutex::Lock lock{mutex};
    if (q.empty()) throw AsyncQueuePopError();
    q.pop();
  }

  bool empty() const {
    Thread::Mutex::Lock lock{mutex};
    return q.empty();
  }

  size_type size() const {
    Thread::Mutex::Lock lock{mutex};
    return q.size();
  }

  void swap(AsyncQueueDispatch& other) {
    if (this != &other) {
      lock2(mutex, other.mutex); // doesn't throw
      Thread::Mutex::Lock l1{mutex, Thread::locked};
      Thread::Mutex::Lock l2{other.mutex, Thread::locked};
      q.swap(other.q);
      if (!q.empty()) cond.broadcast();
      if (!other.q.empty()) other.cond.broadcast();
    }
  }

  AsyncQueueDispatch& operator=(const AsyncQueueDispatch& rhs) {
    if (this != &rhs) {
      lock2(mutex, rhs.mutex); // doesn't throw
      Thread::Mutex::Lock l1{mutex, Thread::locked};
      Thread::Mutex::Lock l2{rhs.mutex, Thread::locked};
      Q temp{rhs.q};
      q.swap(temp);
      if (!q.empty()) cond.broadcast();
    }
    return *this;
  }

  AsyncQueueDispatch& operator=(AsyncQueueDispatch&& rhs) {
    Thread::Mutex::Lock lock{mutex};
    q = std::move(rhs.q);
    if (!q.empty()) cond.broadcast();
    return *this;
  }

  AsyncQueueDispatch() = default;

  AsyncQueueDispatch(AsyncQueueDispatch&& rhs): q(std::move(rhs.q)) {}

  AsyncQueueDispatch(const AsyncQueueDispatch& rhs):
                        q((Thread::Mutex::Lock(rhs.mutex), rhs.q)) {}

  ~AsyncQueueDispatch() {
    Thread::Mutex::Lock lock{mutex};
  }

  CGU_GLIB_MEMORY_SLICES_FUNCS
};

#endif // CGU_USE_INHERITABLE_QUEUE

} // namespace Cgu

#endif
