/* Copyright (C) 2009 and 2012 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.

*/

#ifndef CGU_TIMEOUT_H
#define CGU_TIMEOUT_H

/**
 * @defgroup timeout timeout
 *
 * \#include <c++-gtk-utils/timeout.h>
 *
 * The start_timeout() function connects a timeout to an event loop
 * owned by a GMainContext object (normally the main program loop).
 * By so doing, it provides a convenient way of attaching the callback
 * for the timeout, and also provides for automatic disconnection when
 * an object whose function the callback represents is destroyed.  The
 * timeout will keep executing the callback at the intervals set in
 * start_timeout() until it is terminated in one of the ways mentioned
 * below.
 *
 * start_timeout() is thread-safe (it may be called in any thread)
 * provided that, if glib < 2.32 is used, the glib main loop has been
 * made thread-safe by a call to g_thread_init().  glib >= 2.32 does
 * not require g_thread_init() to be called in order to be
 * thread-safe.
 *
 * start_timeout() takes ownership of the passed Callback object.  The
 * function comes in two versions.  The one which takes a
 * Callback::Releaser object as its third argument provides for
 * automatic termination of the execution of the callback at the
 * specified interval if the target object which has the Releaser as a
 * member is destroyed.  (Note that for this to be race free, the
 * lifetime of the remote target object whose method is to be invoked
 * must be determined by the thread to whose main loop the timeout has
 * been attached.  When the main loop begins invoking the execution of
 * the timeout callback, the remote object must either wholly exist,
 * in which case the callback will be invoked, or have been destroyed,
 * in which case the callback will be ignored, and not be in some
 * transient half-state governed by another thread.)
 *
 * The connected function encapsulated by the callback passed to
 * start_timeout() and executed by the main loop should take a single
 * unbound bool& argument (with any other arguments bound in the
 * callback).  If that bool& argument is set by the connected function
 * to false, then the timeout calls will be ended and all resources
 * connected with it deleted without further user action being
 * required (there is no need for the connected function to set it to
 * true if timeout execution is to continue, as that is the default).
 * In addition, the timeout will be ended automatically and resources
 * deleted if (i) as mentioned above, the callback passed to
 * start_timeout() is protected by a Releaser object and the target
 * object whose method is encapsulated by the callback is destroyed,
 * or (ii) g_source_remove() is called on the source id returned by
 * start_timeout() (where the timeout is attached to the default main
 * context) or g_source_destroy() is called on the GSource object
 * obtained from that id with g_main_context_find_source_by_id()
 * (where the timeout has been attached to a non-default main
 * context).  If the source has been removed automatically by virtue
 * of the bool& argument being set to false or by virtue of a Releaser
 * object releasing, g_source_remove() or g_source_destroy() should
 * not afterwards be called in respect of the id value returned by
 * start_timeout() in case it has been reused by the main context
 * concerned in the meantime.
 *
 * The start_timeout_seconds() functions do the same as their
 * start_timeout() counterparts, except that they use the larger
 * granularity glib timeout-seconds main loop event sources (and take
 * seconds and not milliseconds as their timeout argument).  The idea
 * behind the glib timeout-seconds sources is to group long timeout
 * events which do not have critical timing resolution requirements so
 * that they are aligned together with one second granularity.  This
 * minimises the number of processor wake-ups required to handle such
 * events, thereby helping power efficiency.  These functions are to
 * be preferred for long timeouts where one second granularity is
 * acceptable.  These larger granularity functions are only compiled
 * into the library if glib >= 2.14 is installed.
 */

#include <glib.h>
#include <c++-gtk-utils/callback.h>
#include <c++-gtk-utils/cgu_config.h>

namespace Cgu {

class Releaser;

/**
 * Starts a timeout in the glib main loop, and executes the callback
 * when the timeout expires.  It is thread-safe (it may be called in
 * any thread) provided that, if glib < 2.32 is used, g_thread_init()
 * has been called.  glib >= 2.32 does not require g_thread_init() to
 * be called to be thread-safe.  This function will not throw.
 * @param millisec The interval of the timeout, in milliseconds.
 * @param cb The callback object.  Ownership is taken of this object,
 * and it will be deleted when it has been finished with.
 * @param priority The priority to be given to the timeout in the
 * main loop.  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. The default is
 * G_PRIORITY_DEFAULT.  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 to which the timeout is to be
 * attached (the default of NULL will cause the timeout to be attached
 * to the main program loop, and this is almost always what is
 * wanted).
 * @return The glib source id of the timeout.
 * @note Cancellation of the thread to which the timeout is attached is
 * blocked during execution of the callback.
 * @ingroup timeout
 */
guint start_timeout(guint millisec, const Callback::CallbackArg<bool&>* cb,
		    gint priority = G_PRIORITY_DEFAULT, GMainContext* context = 0);

/**
 * Starts a timeout in the glib main loop, and executes the callback
 * when the timeout expires.  This version provides for automatic
 * timeout disconnection when the object whose function the callback
 * represents is destroyed, via the Releaser object.  It is
 * thread-safe (it may be called in any thread) provided that, if glib
 * < 2.32 is used, g_thread_init() has been called.  glib >= 2.32 does
 * not require g_thread_init() to be called to be thread-safe.
 * @param millisec The interval of the timeout, in milliseconds.
 * @param cb The callback object.  Ownership is taken of this object,
 * and it will be deleted when it has been finished with.
 * @param r A Releaser object which the protected object has as a
 * public member.
 * @param priority The priority to be given to the timeout in the
 * main loop.  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. The default is
 * G_PRIORITY_DEFAULT.  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 to which the timeout is to be
 * attached (the default of NULL will cause the timeout to be attached
 * to the main program loop, and this is almost always what is
 * wanted).
 * @return The glib source id of the timeout.
 * @exception std::bad_alloc This function might throw std::bad_alloc
 * if memory is exhausted and the system throws in that case.  If it
 * does so, the CallbackArg object will be disposed of.
 * @exception Cgu::Thread::MutexError This method might throw
 * Cgu:Thread::MutexError if initialisation of the mutex in a
 * SafeEmitterArg object constructed by this method fails.  If it does
 * so, the CallbackArg object will be disposed of.  (It is often not
 * worth checking for this exception, as it means either memory is
 * exhausted or pthread has run out of other resources to create new
 * mutexes.)
 * @note 1. Cancellation of the thread to which the timeout is
 * attached is blocked during execution of the callback.
 * @note 2. By virtue of the Releaser object, 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 timeout 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) and thus will be delayed until expiry of the next timeout
 * interval.  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.
 * @ingroup timeout
 */
guint start_timeout(guint millisec, const Callback::CallbackArg<bool&>* cb,
		    Releaser& r, gint priority = G_PRIORITY_DEFAULT,
		    GMainContext* context = 0);

/**
 * Starts a timeout in the glib main loop using the higher granularity
 * glib timeout-seconds event sources, and executes the callback when
 * the timeout expires.  It is thread-safe (it may be called in any
 * thread) provided that, if glib < 2.32 is used, g_thread_init() has
 * been called.  glib >= 2.32 does not require g_thread_init() to be
 * called to be thread-safe.  This function will not throw.
 * @param sec The interval of the timeout, in seconds.
 * @param cb The callback object.  Ownership is taken of this object,
 * and it will be deleted when it has been finished with.
 * @param priority The priority to be given to the timeout in the
 * main loop.  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. The default is
 * G_PRIORITY_DEFAULT.  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 to which the timeout is to be
 * attached (the default of NULL will cause the timeout to be attached
 * to the main program loop, and this is almost always what is
 * wanted).
 * @return The glib source id of the timeout.
 * @note 1. Cancellation of the thread to which the timeout is
 * attached is blocked during execution of the callback.
 * @note 2. This function is only compiled into the library if glib >=
 * 2.14 is installed.
 * @ingroup timeout
 */
guint start_timeout_seconds(guint sec, const Callback::CallbackArg<bool&>* cb,
			    gint priority = G_PRIORITY_DEFAULT, GMainContext* context = 0);

/**
 * Starts a timeout in the glib main loop using the higher granularity
 * glib timeout-seconds event sources, and executes the callback when
 * the timeout expires.  This version provides for automatic timeout
 * disconnection when the object whose function the callback
 * represents is destroyed, via the Releaser object.  It is
 * thread-safe (it may be called in any thread) provided that, if glib
 * < 2.32 is used, g_thread_init() has been called.  glib >= 2.32 does
 * not require g_thread_init() to be called to be thread-safe.
 * @param sec The interval of the timeout, in seconds.
 * @param cb The callback object.  Ownership is taken of this object,
 * and it will be deleted when it has been finished with.
 * @param r A Releaser object which the protected object has as a
 * public member.
 * @param priority The priority to be given to the timeout in the
 * main loop.  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. The default is
 * G_PRIORITY_DEFAULT.  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 to which the timeout is to be
 * attached (the default of NULL will cause the timeout to be attached
 * to the main program loop, and this is almost always what is
 * wanted).
 * @return The glib source id of the timeout.
 * @exception std::bad_alloc This function might throw std::bad_alloc
 * if memory is exhausted and the system throws in that case.  If it
 * does so, the CallbackArg object will be disposed of.
 * @exception Cgu::Thread::MutexError This method might throw
 * Cgu:Thread::MutexError if initialisation of the mutex in a
 * SafeEmitterArg object constructed by this method fails.  If it does
 * so, the CallbackArg object will be disposed of.  (It is often not
 * worth checking for this exception, as it means either memory is
 * exhausted or pthread has run out of other resources to create new
 * mutexes.)
 * @note 1. Cancellation of the thread to which the timeout is
 * attached is blocked during execution of the callback.
 * @note 2. This function is only compiled into the library if glib >=
 * 2.14 is installed.
 * @note 3. By virtue of the Releaser object, 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 timeout 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) and thus will be delayed until expiry of the next timeout
 * interval.  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.
 * @ingroup timeout
 */
guint start_timeout_seconds(guint sec, const Callback::CallbackArg<bool&>* cb,
			    Releaser& r, gint priority = G_PRIORITY_DEFAULT,
			    GMainContext* context = 0);

} // namespace Cgu

#endif
