/* $Id: thread.hpp 8172 2005-09-06 13:53:14Z ott $ */
/*
   Copyright (C) 2003-5 by David White <davidnwhite@verizon.net>
   Part of the Battle for Wesnoth Project http://www.wesnoth.org/

   This program is free software; you can redistribute it and/or modify
   it under the terms of the GNU General Public License.
   This program is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY.

   See the COPYING file for more details.
*/

#ifndef THREAD_HPP_INCLUDED
#define THREAD_HPP_INCLUDED

#include "SDL.h"
#include "SDL_thread.h"

// Threading primitives wrapper for SDL_Thread.
//
// This module defines primitives for wrapping C++ around SDL's threading
// interface
namespace threading
{

struct manager
{
	~manager();
};

// Threading object.
//
// This class defines threading objects. One such object represents a
// thread and admits killing and joining on threads. Intended to be
// used for manipulating threads instead of poking around with SDL_Thread
// calls.
class thread
{
public:
	// Construct a new thread to start executing the function
	// pointed to by f. The void* data will be passed to f, to
	// facilitate passing of parameters to f.
	//
	// \param f the function at which the thread should start executing
	// \param data passed to f
	//
	// \pre f != NULL
	explicit thread(int (*f)(void*), void* data=NULL);

	// Destroy the thread object. This is done by waiting on the
	// thread with the join() operation, thus blocking until the
	// thread object has finished its operation.
	~thread();

	// Kill the thread. If the thread has already been killed, this
	// is a no-op.
	void kill();

	// Join (wait) on the thread to finish. When the thread finishes,
	// the function will return. calling wait() on an already killed
	// thread is a no-op.
	void join();

	void detach();
private:
	thread(const thread&);
	void operator=(const thread&);

	SDL_Thread* thread_;
};

// Binary mutexes.
//
// Implements an interface to binary mutexes. This class only defines the
// mutex itself. Locking is handled through the friend class lock,
// and monitor interfacing through condition variables is handled through
// the friend class condition.
class mutex
{
public:
	mutex();
	~mutex();

	friend class lock;
	friend class condition;

private:
	mutex(const mutex&);
	void operator=(const mutex&);

	SDL_mutex* const m_;
};

// Binary mutex locking.
//
// Implements a locking object for mutexes. The creation of a lock
// object on a mutex will lock the mutex as long as this object is
// not deleted.
class lock
{
public:
	// Create a lock object on the mutex given as a parameter to
	// the constructor. The lock will be held for the duration
	// of the object existence.
	// If the mutex is already locked, the constructor will
	// block until the mutex lock can be acquired.
	//
	// \param m the mutex on which we should try to lock.
	explicit lock(mutex& m);
	// Delete the lock object, thus releasing the lock aquired
	// on the mutex which the lock object was created with.
	~lock();
private:
	lock(const lock&);
	void operator=(const lock&);

	mutex& m_;
};

// Condition variable locking.
//
// Implements condition variables for mutexes. A condition variable
// allows you to free up a lock inside a critical section
// of the code and regain it later. Condition classes only make
// sense to do operations on, if one already aquired a mutex.
class condition
{
public:
	condition();
	~condition();

	// Wait on the condition. When the condition is met, you
	// have a lock on the mutex and can do work in the critical
	// section. When the condition is not met, wait blocks until
	// the condition is met and atomically frees up the lock on
	// the mutex. One will automatically regain the lock when the
	// thread unblocks.
	//
	// If wait returns false we have an error. In this case one cannot
	// assume that he has a lock on the mutex anymore.
	//
	// \param m the mutex you wish to free the lock for
	// \returns true: the wait was successful, false: an error occurred
	//
	// \pre You have already aquired a lock on mutex m
	//
	bool wait(const mutex& m);

	enum WAIT_TIMEOUT_RESULT { WAIT_OK, WAIT_TIMEOUT, WAIT_ERROR };

	// wait on the condition with a timeout. Basically the same as the
	// wait() function, but if the lock is not aquired before the
	// timeout, the function returns with an error.
	//
	// \param m the mutex you wish free the lock for.
	// \param timeout the allowed timeout in milliseconds (ms)
	// \returns result based on whether condition was met, it timed out,
	// or there was an error
	WAIT_TIMEOUT_RESULT wait_timeout(const mutex& m, unsigned int timeout);
	// signal the condition and wake up one thread waiting on the
	// condition. If no thread is waiting, notify_one() is a no-op.
	// Does not unlock the mutex.
	bool notify_one();

	// signal all threads waiting on the condition and let them contend
	// for the lock. This is often used when varying resource amounts are
	// involved and you do not know how many processes might continue.
	// The function should be used with care, especially if many threads are
	// waiting on the condition variable.
	bool notify_all();

private:
	condition(const condition&);
	void operator=(const condition&);

	SDL_cond* const cond_;
};

//class which defines an interface for waiting on an asynchronous operation
class waiter {
public:
	enum ACTION { WAIT, ABORT };

	virtual ~waiter() {}
	virtual ACTION process() = 0;
};

//class which defines an asynchronous operation. Objects of this class are accessed from
//both the worker thread and the calling thread, and so it has 'strange' allocation semantics.
//It is allocated by the caller, and generally deleted by the caller. However, in some cases
//the asynchronous operation is aborted, and the caller abandons it. The caller cannot still
//delete the operation, since the worker thread might still access it, so in the case when the
//operation is aborted, the worker thread will delete it.
//
//The caller should hold these objects using the async_operation_holder class below, which will
//handle the delete semantics
class async_operation
{
public:

	enum RESULT { COMPLETED, ABORTED };

	async_operation() : aborted_(false), finishedVar_(false) {}
	virtual ~async_operation() {}

	RESULT execute(waiter& wait);

	mutex& get_mutex() { return mutex_; }

	virtual void run() = 0;

	//notify that the operation is finished. Can be called from within the thread
	//while holding the mutex and after checking is_aborted()
	//if we want to be sure that if the operation is completed, the caller is notified.
	//will be called in any case after the operation returns
	bool notify_finished();

	//must hold the mutex before calling this function from the worker thread
	bool is_aborted() const { return aborted_; }

private:
	bool aborted_;
	condition finished_;
	bool finishedVar_;
	mutex mutex_;
};

//T should be a type derived from async_operation
template<typename T>
class async_operation_holder
{
public:
	explicit async_operation_holder(T* op) : op_(op)
	{}

	~async_operation_holder() {
		//it's okay to call is_aborted() without the mutex here,
		//because we are in the calling thread, not the worker thread
		if(op_->is_aborted() == false) {
			delete op_;
		}
	}

	T& operation() const { return *op_; }

private:
	T* const op_;
};

}

#endif
