/* closure.hh - Cooperative multitasking
 * Copyright 2008 Bas Wijnen <wijnen@debian.org>
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program 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 General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

#ifndef SHEVEK_CLOSURE_HH
#define SHEVEK_CLOSURE_HH

#include <pthread.h>
#include "refbase.hh"

namespace shevek
{
	/// Block and resume without blocking the main loop.
	/** Closures allow blocking and resuming the main loop.  They
	 * are implemented with threads, which means that they don't
	 * work well with multi-threaded programs.
	 *
	 * If a function wants to be able to block using a closure, it must be
	 * called using closure().  It (or any function it calls) can then
	 * suspend by calling closure::block().  It can be awoken again by
	 * calling closure::wake() on the closure object.
	 */
	class closure : public refbase
	{
		static closure *current;
		enum state_t { EMPTY, BLOCKING, RUNNING } state;
		sigc::slot0 <void> callback, function;
		pthread_t thread;
		int blocking_pipe[2];
		int waking_pipe[2];
		void do_write (int *fds);
		void do_read (int *fds);
		static void *start_wrapper (void *me);
		closure ();
	public:
		/// Create a new closure.
		/** Create a new closure.  It will be empty initially.
		 */
		static Glib::RefPtr <closure> create ()
		{ return Glib::RefPtr <closure> (new closure ()); }
		/// Check if the closure is empty.
		/** Check if the closure is empty.  If it is, set_function()
		 * can be called.
		 */
		bool empty () const { return state == EMPTY; }
		/// Set running function on an empty closure.
		/** Set running function.  The closure must be empty when this
		 * is called.  When the function exits, the closure returns to
		 * the empty state, and the callback is called, if given.
		 */
		void set_function (sigc::slot0 <void> func, bool run = true,
				sigc::slot0 <void> cb = sigc::slot0 <void> ());
		/// Destructor.
		~closure ();
		/// Sleep, returning control to the caller until awoken.
		/** This function puts the current closure to sleep.  It will
		 * continue to run when awoken with wake().  It can also be
		 * destroyed.  This function uses a global variable to know
		 * which is the current closure, so it can be called without an
		 * object, as closure::block (); .
		 */
		static void block ();
		/// Continue running the closure.
		/** Wake a closure.  It is an error to wake a closure which
		 * isn't blocking (in particular also the currently running
		 * closure).
		 */
		void wake ();
	};
}

#endif
