/* Copyright (C) 2005 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.

*/

#ifndef CGU_IO_WATCH_H
#define CGU_IO_WATCH_H

/**
 */

/**
 * @defgroup io_watch io_watch
 *
 * \#include <c++-gtk-utils/io_watch.h>
 *
 * The start_iowatch() function connects a Unix file descriptor to an
 * event loop owned by a GMainContext object (normally the main
 * program loop).  It both saves the overhead of having to construct a
 * GIOChannel object where the only thing wanted is to execute a
 * callback when there is something to be read from a pipe, fifo or
 * socket or a pipe or fifo can be written to, and it also provides
 * for automatic disconnection when an object whose function the
 * callback represents or calls into is destroyed.
 *
 * For the GIOCondition argument of start_iowatch(), G_IO_IN can be
 * bitwise-ORed with G_IO_HUP, and should be if the callback has the
 * task of cleaning up if EOF is reached (see
 * http://www.greenend.org.uk/rjk/2001/06/poll.html ), which is
 * detected by read() returning 0.  A cast will be required to do this
 * for the third argument of start_iowatch() (that is, pass
 * GIOCondition(G_IO_IN | G_IO_HUP)).  In addition, G_IO_IN and
 * G_IO_OUT can be bitwise-ORed with G_IO_ERR (and passed as
 * GIOCondition(G_IO_IN | G_IO_HUP | G_IO_ERR) or
 * GIOCondition(G_IO_OUT | G_IO_ERR)), which would be detected in the
 * callback by read() or write() returning -1.
 *
 * The start_iowatch() function has a number of overloads for the
 * callback to be executed when the file descriptor becomes available
 * for reading or writing.  The simplest to use take an ordinary
 * callable object, such as a lambda expression or the return value of
 * std::bind.  For these, the callable object should take two unbound
 * arguments, the first a GIOCondition type and the second a bool&
 * type.  When the callable object is executed, the GIOCondition
 * argument is passed a value representing the bitwise-ORed events
 * which caused the call: this enables a single watch to be provided
 * for both reading and writing (if the file descriptor has been
 * opened for reading and writing), by testing for G_IO_IN and
 * G_IO_OUT on that argument in the callback function.  It also
 * enables, by testing for G_IO_HUP and G_IO_ERR, a hang-up or error
 * condition to be detected without having to inspect the return value
 * of read() or write() (but note the test results referred to in
 * http://www.greenend.org.uk/rjk/2001/06/poll.html , which show that
 * the ending of a connection can only reliably be determined by
 * testing whether read() returns 0, or whether the iostream wrapper
 * on top of it reports end of file after attempting a read.)
 *
 * The second bool& argument can be used to end the watch. If it is
 * set by the callable object when executed to false, say because
 * end-of-file has been reached, then the watch will be ended and all
 * resources connected with it deleted without further user action
 * being required (there is no need for the callable object to set it
 * to true if the watch is to continue, as that is the default).
 *
 * Other overloads of start_iowatch() take the more explicitly typed
 * Callback::CallbackArg<GIOCondition, bool&> callback object (as
 * constructed with Callback::lambda(), Callback::make() or
 * Callback::make_ref()) instead of a simple callable object, and
 * others take a Callback::CallbackArg<bool&> object for cases where a
 * GIOCondition argument is unnecessary (although there is nothing
 * stopping the other overloads being used and that argument being
 * ignored, and that version is mainly kept to retain compatibility
 * with the 1.2 and 2.0 series of the library).
 *
 * All of the start_iowatch() overloads have an option to take a
 * Callback::Releaser object as their third argument, which provides
 * for automatic ceasing of the watch 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 watch has been attached.  When the main loop begins invoking
 * the execution of the watch 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.)
 *
 * start_iowatch() 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.
 * 
 * As mentioned above, the connected callback passed to
 * start_iowatch() has an unbound bool& argument.  The watch will be
 * ended if that argument is set by the connected callback to false,
 * say because end-of-file has been reached.  In addition, the watch
 * will be ended automatically and resources deleted if (i) as
 * mentioned above, the callback passed to start_iowatch() is
 * protected by a Releaser object and the target object having the
 * Releaser as a member is destroyed, or (ii) g_source_remove() is
 * called on the source id returned by start_iowatch() (where the
 * watch 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 watch
 * 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_iowatch() in
 * case it has been reused by the main context concerned in the
 * meantime.
 */

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

namespace Cgu {

class Releaser;

/**
 * Starts an io watch in the glib main loop on a file descriptor, and
 * executes the callback if the condition in io_condition is met.  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.
 * From version 2.0.24/2.2.7 of the library this function will not
 * throw.  (Prior to that, it could throw std::bad_alloc if memory was
 * exhausted and the system threw in that case, or
 * Cgu::Thread::MutexError if initialisation of the mutex in a
 * SafeEmitterArg object failed, in which case the CallbackArg object
 * would be disposed of.)
 * @param fd The file descriptor.
 * @param cb The callback object.  Ownership is taken of this object,
 * and it will be deleted when it has been finished with
 * @param io_condition The condition to be watched for (G_IO_IN may be
 * bitwise-ored with G_IO_HUP, and G_IO_IN and G_IO_OUT may be
 * bitwise-ored with G_IO_ERR).
 * @param priority The priority to be given to the watch 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 watch is to be
 * attached (the default of NULL will cause the watch to be attached
 * to the main program loop, and this is almost always what is
 * wanted).
 * @return The glib source id of the watch.
 * @note 1. Cancellation of the thread to which the watch is attached
 * is blocked during execution of the callback.
 * @note 2. If the callback throws an exception, the exception will be
 * consumed to protect the main loop and a g_critical() warning will
 * be issued.
 * @ingroup io_watch
 */
guint start_iowatch(int fd, const Callback::CallbackArg<bool&>* cb,
		    GIOCondition io_condition, gint priority = G_PRIORITY_DEFAULT,
		    GMainContext* context = 0);

/**
 * Starts an io watch in the glib main loop on a file descriptor, and
 * executes the callback if the condition in io_condition is met.
 * This version provides for automatic watch 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 fd The file descriptor.
 * @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 io_condition The condition to be watched for (G_IO_IN may be
 * bitwise-ored with G_IO_HUP, and G_IO_IN and G_IO_OUT may be
 * bitwise-ored with G_IO_ERR).
 * @param priority The priority to be given to the watch 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 watch is to be
 * attached (the default of NULL will cause the watch to be attached
 * to the main program loop, and this is almost always what is
 * wanted).
 * @return The glib source id of the watch.
 * @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 function might throw
 * Cgu:Thread::MutexError if initialisation of the mutex in a
 * SafeEmitterArg object constructed by this function 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 watch is attached
 * is blocked during execution of the callback.
 * @note 2. If the callback throws an exception, the exception will be
 * consumed to protect the main loop and a g_critical() warning will
 * be issued.
 * @ingroup io_watch
 */
guint start_iowatch(int fd, const Callback::CallbackArg<bool&>* cb, Releaser& r,
		    GIOCondition io_condition, gint priority = G_PRIORITY_DEFAULT,
		    GMainContext* context = 0);

/**
 * Starts an io watch in the glib main loop on a file descriptor, and
 * executes the callback if the condition in io_condition is met.
 * This version provides the GIOCondition status which caused the
 * callback to be invoked as the first unbound argument of the
 * callback 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.  From version 2.0.24/2.2.7 of the library
 * this function will not throw.  (Prior to that, it could throw
 * std::bad_alloc if memory was exhausted and the system threw in that
 * case, or Cgu::Thread::MutexError if initialisation of the mutex in
 * a SafeEmitterArg object failed, in which case the CallbackArg
 * object would be disposed of.)
 * @param fd The file descriptor.
 * @param cb The callback object.  Ownership is taken of this object,
 * and it will be deleted when it has been finished with.
 * @param io_condition The condition(s) to be watched for (G_IO_IN,
 * G_IO_OUT, G_IO_HUP and G_IO_ERR may all be bitwise-ored).
 * @param priority The priority to be given to the watch 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 watch is to be
 * attached (the default of NULL will cause the watch to be attached
 * to the main program loop, and this is almost always what is
 * wanted).
 * @return The glib source id of the watch.
 * @note 1. Cancellation of the thread to which the watch is attached
 * is blocked during execution of the callback.
 * @note 2. If the callback throws an exception, the exception will be
 * consumed to protect the main loop and a g_critical() warning will
 * be issued.
 * @ingroup io_watch
 * 
 * Since 2.0.0-rc2
 */
guint start_iowatch(int fd, const Callback::CallbackArg<GIOCondition, bool&>* cb,
		    GIOCondition io_condition, gint priority = G_PRIORITY_DEFAULT,
		    GMainContext* context = 0);

/**
 * Starts an io watch in the glib main loop on a file descriptor, and
 * executes the callback if the condition in io_condition is met.
 * This version provides both automatic watch disconnection when the
 * object whose function the callback represents is destroyed, via the
 * Releaser object, and provides the GIOCondition status which caused
 * the callback to be invoked as the first unbound argument of the
 * callback 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 fd The file descriptor.
 * @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 io_condition The condition(s) to be watched for (G_IO_IN,
 * G_IO_OUT, G_IO_HUP and G_IO_ERR may all be bitwise-ored).
 * @param priority The priority to be given to the watch 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 watch is to be
 * attached (the default of NULL will cause the watch to be attached
 * to the main program loop, and this is almost always what is
 * wanted).
 * @return The glib source id of the watch.
 * @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 function might throw
 * Cgu:Thread::MutexError if initialisation of the mutex in a
 * SafeEmitterArg object constructed by this function 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 watch is attached
 * is blocked during execution of the callback.
 * @note 2. If the callback throws an exception, the exception will be
 * consumed to protect the main loop and a g_critical() warning will
 * be issued.
 * @ingroup io_watch
 * 
 * Since 2.0.0-rc2
 */
guint start_iowatch(int fd, const Callback::CallbackArg<GIOCondition, bool&>* cb,
		    Releaser& r, GIOCondition io_condition, gint priority = G_PRIORITY_DEFAULT,
		    GMainContext* context = 0);

/**
 * Starts an io watch in the glib main loop on a file descriptor, and
 * executes a callable object if the condition in io_condition is met.
 * It provides the GIOCondition status which caused the callback to be
 * invoked as the first unbound argument of the callable object, and
 * the second is a bool& argument which if set to 'true' will cause
 * the io watch to end.  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 fd The file descriptor.
 * @param func A callable object, such as formed by a lambda
 * expression or the result of std::bind.  It should take two unbound
 * arguments of type 'GIOCondition' and 'bool&'.
 * @param io_condition The condition to be watched for (G_IO_IN may be
 * bitwise-ored with G_IO_HUP, and G_IO_IN and G_IO_OUT may be
 * bitwise-ored with G_IO_ERR).
 * @param priority The priority to be given to the watch 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 watch is to be
 * attached (the default of NULL will cause the watch to be attached
 * to the main program loop, and this is almost always what is
 * wanted).
 * @return The glib source id of the watch.
 * @exception std::bad_alloc This function might throw std::bad_alloc
 * if memory is exhausted and the system throws in that case. (From
 * version 2.2.7 of the library this exception will not be thrown if
 * the library has been installed using the
 * \--with-glib-memory-slices-no-compat configuration option.  Prior
 * to version 2.2.7 Cgu::Thread::MutexError could also be thrown if
 * initialisation of the mutex in a SafeEmitterArg object failed.)
 * @note 1.  This function may also throw if the copy or move
 * constructor of the callable object throws.
 * @note 2. Cancellation of the thread to which the watch is attached
 * is blocked during execution of the callback.
 * @ingroup io_watch
 *
 * Since 2.1.0
 */
// we need to use enable_if so that where this function is passed a
// pointer to non-const Callback::CallbackArg, or some other
// convertible pointer, this templated overload is dropped from the
// overload set, in order to support the Callback::CallbackArg pointer
// overloads of this function.  This overload calls into the version
// of this function taking a pointer to const Callback::CallbackArg in
// order to perform type erasure.
template <class F,
	  class = typename std::enable_if<!std::is_convertible<typename std::remove_reference<F>::type,
							       const Callback::CallbackArg<GIOCondition, bool&>*>::value
					  && !std::is_convertible<typename std::remove_reference<F>::type,
								  const Callback::CallbackArg<bool&>*>::value>::type>
guint start_iowatch(int fd, F&& func,
		    GIOCondition io_condition, gint priority = G_PRIORITY_DEFAULT,
		    GMainContext* context = 0) {
  return start_iowatch(fd, Callback::lambda<GIOCondition, bool&>(std::forward<F>(func)),
		       io_condition, priority,
		       context);
}

/**
 * Starts an io watch in the glib main loop on a file descriptor, and
 * executes a callable object if the condition in io_condition is met.
 * It provides the GIOCondition status which caused the callback to be
 * invoked as the first unbound argument of the callable object, and
 * the second is a bool& argument which if set to 'true' will cause
 * the io watch to end.  This version provides for automatic watch
 * disconnection if an object whose function the callback represents
 * or calls into 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 fd The file descriptor.
 * @param func A callable object, such as formed by a lambda
 * expression or the result of std::bind.  It should take two unbound
 * arguments of type 'GIOCondition' and 'bool&'.
 * @param r A Releaser object which the protected object has as a
 * public member.
 * @param io_condition The condition to be watched for (G_IO_IN may be
 * bitwise-ored with G_IO_HUP, and G_IO_IN and G_IO_OUT may be
 * bitwise-ored with G_IO_ERR).
 * @param priority The priority to be given to the watch 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 watch is to be
 * attached (the default of NULL will cause the watch to be attached
 * to the main program loop, and this is almost always what is
 * wanted).
 * @return The glib source id of the watch.
 * @exception std::bad_alloc This function might throw std::bad_alloc
 * if memory is exhausted and the system throws in that case.
 * @exception Cgu::Thread::MutexError This function might throw
 * Cgu:Thread::MutexError if initialisation of the mutex in a
 * SafeEmitterArg object constructed by this function fails.  (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.  This function may also throw if the copy or move
 * constructor of the callable object throws.
 * @note 2. Cancellation of the thread to which the watch is attached
 * is blocked during execution of the callback.
 * @ingroup io_watch
 *
 * Since 2.1.0
 */
// we need to use enable_if so that where this function is passed a
// pointer to non-const Callback::CallbackArg, or some other
// convertible pointer, this templated overload is dropped from the
// overload set, in order to support the Callback::CallbackArg pointer
// overloads of this function.  This overload calls into the version
// of this function taking a pointer to const Callback::CallbackArg in
// order to perform type erasure.
template <class F,
	  class = typename std::enable_if<!std::is_convertible<typename std::remove_reference<F>::type,
							       const Callback::CallbackArg<GIOCondition, bool&>*>::value
					  && !std::is_convertible<typename std::remove_reference<F>::type,
								  const Callback::CallbackArg<bool&>*>::value>::type>
guint start_iowatch(int fd, F&& func, Releaser& r,
		    GIOCondition io_condition, gint priority = G_PRIORITY_DEFAULT,
		    GMainContext* context = 0) {
  return start_iowatch(fd, Callback::lambda<GIOCondition, bool&>(std::forward<F>(func)), r,
		       io_condition, priority,
		       context);
}

} // namespace Cgu

#endif
