/*  PCSX2 - PS2 Emulator for PCs
 *  Copyright (C) 2002-2010  PCSX2 Dev Team
 *
 *  PCSX2 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 Found-
 *  ation, either version 3 of the License, or (at your option) any later version.
 *
 *  PCSX2 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 PCSX2.
 *  If not, see <http://www.gnu.org/licenses/>.
 */

#pragma once

#include "Threading.h"
#include "ScopedPtrMT.h"
#include "EventSource.h"

namespace Threading
{

// --------------------------------------------------------------------------------------
//  ThreadDeleteEvent
// --------------------------------------------------------------------------------------
	class EventListener_Thread : public IEventDispatcher<int>
	{
	public:
		typedef int EvtParams;

	protected:
		pxThread* m_thread;

	public:
		EventListener_Thread()
		{
			m_thread = NULL;
		}

		virtual ~EventListener_Thread() throw() {}

		void SetThread( pxThread& thr ) { m_thread = &thr; }
		void SetThread( pxThread* thr ) { m_thread = thr; }

		void DispatchEvent( const int& params )
		{
			OnThreadCleanup();
		}

	protected:
		// Invoked by the pxThread when the thread execution is ending.  This is
		// typically more useful than a delete listener since the extended thread information
		// provided by virtualized functions/methods will be available.
		// Important!  This event is executed *by the thread*, so care must be taken to ensure
		// thread sync when necessary (posting messages to the main thread, etc).
		virtual void OnThreadCleanup()=0;
	};

// --------------------------------------------------------------------------------------
// pxThread - Helper class for the basics of starting/managing persistent threads.
// --------------------------------------------------------------------------------------
// This class is meant to be a helper for the typical threading model of "start once and
// reuse many times."  This class incorporates a lot of extra overhead in stopping and
// starting threads, but in turn provides most of the basic thread-safety and event-handling
// functionality needed for a threaded operation.  In practice this model is usually an
// ideal one for efficiency since Operating Systems themselves typically subscribe to a
// design where sleeping, suspending, and resuming threads is very efficient, but starting
// new threads has quite a bit of overhead.
//
// To use this as a base class for your threaded procedure, overload the following virtual
// methods:
//  void OnStart();
//  void ExecuteTaskInThread();
//  void OnCleanupInThread();
//
// Use the public methods Start() and Cancel() to start and shutdown the thread, and use
// m_sem_event internally to post/receive events for the thread (make a public accessor for
// it in your derived class if your thread utilizes the post).
//
// Notes:
//  * Constructing threads as static global vars isn't recommended since it can potentially
//    confuse w32pthreads, if the static initializers are executed out-of-order (C++ offers
//    no dependency options for ensuring correct static var initializations).  Use heap
//    allocation to create thread objects instead.
//
	class pxThread
	{
		DeclareNoncopyableObject(pxThread);

		friend void pxYield( int ms );

	protected:
		wxString	m_name;				// diagnostic name for our thread.
		pthread_t	m_thread;
		uptr		m_native_id;		// typically an id, but implementing platforms can do whatever.
		uptr		m_native_handle;	// typically a pointer/handle, but implementing platforms can do whatever.

		Semaphore	m_sem_event;		// general wait event that's needed by most threads
		Semaphore	m_sem_startup;		// startup sync tool
		Mutex		m_mtx_InThread;		// used for canceling and closing threads in a deadlock-safe manner
		MutexRecursive	m_mtx_start;	// used to lock the Start() code from starting simultaneous threads accidentally.
		Mutex		m_mtx_ThreadName;

		volatile vol_t m_detached;		// a boolean value which indicates if the m_thread handle is valid
		volatile s32 m_running;		// set true by Start(), and set false by Cancel(), Block(), etc.

		// exception handle, set non-NULL if the thread terminated with an exception
		// Use RethrowException() to re-throw the exception using its original exception type.
		ScopedPtrMT<BaseException> m_except;

		EventSource<EventListener_Thread> m_evtsrc_OnDelete;


	public:
		virtual ~pxThread() throw();
		pxThread( const wxString& name=L"pxThread" );

		pthread_t GetId() const { return m_thread; }
		u64 GetCpuTime() const;

		virtual void Start();
		virtual void Cancel( bool isBlocking = true );
		virtual bool Cancel( const wxTimeSpan& timeout );
		virtual bool Detach();
		virtual void Block();
		virtual bool Block( const wxTimeSpan& timeout );
		virtual void RethrowException() const;

		void AddListener( EventListener_Thread& evt );
		void AddListener( EventListener_Thread* evt )
		{
			if( evt == NULL ) return;
			AddListener( *evt );
		}

		void WaitOnSelf( Semaphore& mutex ) const;
		void WaitOnSelf( Mutex& mutex ) const;
		bool WaitOnSelf( Semaphore& mutex, const wxTimeSpan& timeout ) const;
		bool WaitOnSelf( Mutex& mutex, const wxTimeSpan& timeout ) const;

		bool IsRunning() const;
		bool IsSelf() const;
		bool HasPendingException() const { return !!m_except; }

		wxString GetName() const;
		void SetName( const wxString& newname );

	protected:
		// Extending classes should always implement your own OnStart(), which is called by
		// Start() once necessary locks have been obtained.  Do not override Start() directly
		// unless you're really sure that's what you need to do. ;)
		virtual void OnStart();

		virtual void OnStartInThread();

		// This is called when the thread has been canceled or exits normally.  The pxThread
		// automatically binds it to the pthread cleanup routines as soon as the thread starts.
		virtual void OnCleanupInThread();

		// Implemented by derived class to perform actual threaded task!
		virtual void ExecuteTaskInThread()=0;

		void TestCancel() const;

		// Yields this thread to other threads and checks for cancellation.  A sleeping thread should
		// always test for cancellation, however if you really don't want to, you can use Threading::Sleep()
		// or better yet, disable cancellation of the thread completely with DisableCancellation().
		//
		// Parameters:
		//   ms - 'minimum' yield time in milliseconds (rough -- typically yields are longer by 1-5ms
		//         depending on operating system/platform).  If ms is 0 or unspecified, then a single
		//         timeslice is yielded to other contending threads.  If no threads are contending for
		//         time when ms==0, then no yield is done, but cancellation is still tested.
		void Yield( int ms = 0 )
		{
			pxAssert( IsSelf() );
			Threading::Sleep( ms );
			TestCancel();
		}

		void FrankenMutex( Mutex& mutex );

		bool AffinityAssert_AllowFromSelf( const DiagnosticOrigin& origin ) const;
		bool AffinityAssert_DisallowFromSelf( const DiagnosticOrigin& origin ) const;

		// ----------------------------------------------------------------------------
		// Section of methods for internal use only.

		void _platform_specific_OnStartInThread();
		void _platform_specific_OnCleanupInThread();
		bool _basecancel();
		void _selfRunningTest( const wxChar* name ) const;
		void _DoSetThreadName( const wxString& name );
		void _DoSetThreadName( const char* name );
		void _internal_execute();
		void _try_virtual_invoke( void (pxThread::*method)() );
		void _ThreadCleanup();

		static void* _internal_callback( void* func );
		static void _pt_callback_cleanup( void* handle );
	};


// --------------------------------------------------------------------------------------
//  BaseTaskThread
// --------------------------------------------------------------------------------------
// an abstract base class which provides simple parallel execution of single tasks.
//
// FIXME: This class is incomplete and untested!  Don't use, unless you want to fix it
// while you're at it. :D
//
// Implementation:
//   To use this class your derived class will need to implement its own Task() function
//   and also a "StartTask( parameters )" function which suits the need of your task, along
//   with any local variables your task needs to do its job.  You may additionally want to
//   implement a "GetResult()" function, which would be a combination of WaitForResult()
//   and a return value of the computational result.
//
// Thread Safety:
//   If operating on local variables, you must execute WaitForResult() before leaving the
//   variable scope -- or alternatively have your StartTask() implementation make full
//   copies of dependent data.  Also, by default PostTask() always assumes the previous
//   task has completed.  If your system can post a new task before the previous one has
//   completed, then it needs to explicitly call WaitForResult() or provide a mechanism
//   to cancel the previous task (which is probably more work than it's worth).
//
// Performance notes:
//  * Remember that thread creation is generally slow, so you should make your object
//    instance once early and then feed it tasks repeatedly over the course of program
//    execution.
//
//  * For threading to be a successful speedup, the task being performed should be as lock
//    free as possible.  For example using STL containers in parallel usually fails to
//    yield any speedup due to the gratuitous amount of locking that the STL performs
//    internally.
//
//  * The best application of tasking threads is to divide a large loop over a linear array
//    into smaller sections.  For example, if you have 20,000 items to process, the task
//    can be divided into two threads of 10,000 items each.
//
	class BaseTaskThread : public pxThread
	{
	protected:
		volatile bool m_Done;
		volatile bool m_TaskPending;
		Semaphore m_post_TaskComplete;
		Mutex m_lock_TaskComplete;

	public:
		virtual ~BaseTaskThread() throw() {}
		BaseTaskThread() :
			m_Done( false )
		,	m_TaskPending( false )
		,	m_post_TaskComplete()
		{
		}

		void Block();
		void PostTask();
		void WaitForResult();

	protected:
		// Abstract method run when a task has been posted.  Implementing classes should do
		// all your necessary processing work here.
		virtual void Task()=0;

		virtual void ExecuteTaskInThread();
	};
}
