/*
 * Copyright 2011, Blender Foundation.
 *
 * 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 2
 * 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, write to the Free Software Foundation,
 * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
 */

#ifndef __UTIL_THREAD_H__
#define __UTIL_THREAD_H__

#include <boost/thread.hpp>
#include <queue>

CCL_NAMESPACE_BEGIN

#if 0

/* Use STL for threading */

using std::thread;
using std::thread_mutex;
typedef std::lock_guard thread_scoped_lock;
using std::condition_variable;

#else

/* Use boost for threading */

using boost::thread;
typedef boost::mutex thread_mutex;
typedef boost::mutex::scoped_lock thread_scoped_lock;
typedef boost::condition_variable thread_condition_variable;

#endif

/* Thread Safe Queue to pass tasks from one thread to another. Tasks should be
 * pushed into the queue, while the worker thread waits to pop the next task
 * off the queue. Once all tasks are into the queue, calling stop() will stop
 * the worker threads from waiting for more tasks once all tasks are done. */

template<typename T> class ThreadQueue
{
public:
	ThreadQueue()
	{
		tot = 0;
		tot_done = 0;
		do_stop = false;
		do_cancel = false;
	}

	/* Main thread functions */

	/* push a task to be executed */
	void push(const T& value)
	{
		thread_scoped_lock lock(queue_mutex);
		queue.push(value);
		tot++;
		lock.unlock();

		queue_cond.notify_one();
	}

	/* wait until all tasks are done */
	void wait_done()
	{
		thread_scoped_lock lock(done_mutex);

		while(tot_done != tot)
			done_cond.wait(lock);
	}

	/* stop all worker threads */
	void stop()
	{
		clear();
		do_stop = true;
		queue_cond.notify_all();
	}

	/* cancel all tasks, but keep worker threads running */
	void cancel()
	{
		clear();
		do_cancel = true;
		wait_done();
		do_cancel = false;
	}

	/* Worker thread functions
     *
	 * while(queue.worker_wait_pop(task)) {
	 *		for(..) {
	 *			... do work ...
	 *
	 *			if(queue.worker_cancel())
	 *				break;
	 *      }
	 *		
	 *		queue.worker_done();
	 * }
	 */

	bool worker_wait_pop(T& value)
	{
		thread_scoped_lock lock(queue_mutex);

		while(queue.empty() && !do_stop)
			queue_cond.wait(lock);

		if(queue.empty())
			return false;
		
		value = queue.front();
		queue.pop();

		return true;
	}

	void worker_done()
	{
		thread_scoped_lock lock(done_mutex);
		tot_done++;
		lock.unlock();

		assert(tot_done <= tot);

		done_cond.notify_all();
	}

	bool worker_cancel()
	{
		return do_cancel;
	}

protected:
	void clear()
	{
		thread_scoped_lock lock(queue_mutex);

		while(!queue.empty()) {
			thread_scoped_lock done_lock(done_mutex);
			tot_done++;
			done_lock.unlock();

			queue.pop();
		}

		done_cond.notify_all();
	}

	std::queue<T> queue;
	thread_mutex queue_mutex;
	thread_mutex done_mutex;
	thread_condition_variable queue_cond;
	thread_condition_variable done_cond;
	volatile bool do_stop;
	volatile bool do_cancel;
	volatile int tot, tot_done;
};

/* Thread Local Storage
 *
 * Boost implementation is a bit slow, and Mac OS X __thread is not supported
 * but the pthreads implementation is optimized, so we use these macros. */

#ifdef __APPLE__

#define tls_ptr(type, name) \
	pthread_key_t name
#define tls_set(name, value) \
	pthread_setspecific(name, value)
#define tls_get(type, name) \
	((type*)pthread_getspecific(name))
#define tls_create(type, name) \
	pthread_key_create(&name, NULL)
#define tls_delete(type, name) \
	pthread_key_delete(name);

#else

#ifdef __WIN32
#define __thread __declspec(thread)
#endif

#define tls_ptr(type, name) \
	__thread type *name
#define tls_set(name, value) \
	name = value
#define tls_get(type, name) \
	name
#define tls_create(type, name)
#define tls_delete(type, name)

#endif

CCL_NAMESPACE_END

#endif /* __UTIL_THREAD_H__ */

