// xthread C++0X header
#ifndef _THREADS_XTHREAD_
#define _THREADS_XTHREAD_
#include <Dinkum/threads/xthrcommon.h>
#include <Dinkum/threads/xtime>
#include <Dinkum/threads/xthreads.h>
#include <tuple>
#include <type_traits>
#include <utility>

 #if _HAS_NOEXCEPT
 #else /* _HAS_NOEXCEPT */
#include <exception>
 #endif /* _HAS_NOEXCEPT */

_STD_BEGIN

enum {	// constants for error codes
	_DEVICE_OR_RESOURCE_BUSY,
	_INVALID_ARGUMENT,
	_NO_SUCH_PROCESS,
	_NOT_ENOUGH_MEMORY,
	_OPERATION_NOT_PERMITTED,
	_RESOURCE_DEADLOCK_WOULD_OCCUR,
	_RESOURCE_UNAVAILABLE_TRY_AGAIN
	};

void _Throw_C_error(int _Code);
void _Throw_Cpp_error(int _Code);

inline int _Check_C_return(int _Res)
	{	// throw exception on failure
	if (_Res != _Thrd_success)
		_Throw_C_error(_Res);
	return (_Res);
	}

inline int _Check_C_return(int _Res, int _Other)
	{	// throw exception on failure
	if (_Res != _Thrd_success && _Res != _Other)
		_Throw_C_error(_Res);
	return (_Res);
	}

	// C++ WRAPPERS FOR C FUNCTIONS (SAME NAMES, IN NAMESPACE std)
inline int _Thrd_startX(_Thrd_imp_t *_Thr, _Thrd_callback_t _Fp, void *_Arg)
	{	// throw exception on failure
	int _Res = _Thrd_start(_Thr, _Fp, _Arg);
#if defined(__ghs)
	return (_Check_C_return(_Res));
#else
	return (_Check_C_return(_Res != _Thrd_error ? _Res : _Thrd_nomem));
#endif
	}

#if defined(__ghs)
inline int _Thrd_start_configX(_Thrd_imp_t *_Thr, long priority,
		size_t stacksize, _Thrd_callback_t _Fp, void *_Arg)
	{	// throw exception on failure
	int _Res = _Thrd_start_config(_Thr, priority, stacksize, _Fp, _Arg);
	return (_Check_C_return(_Res));
	}
#endif

inline int _Thrd_detachX(_Thrd_t _Thr)
	{	// throw exception on failure
	return (_Check_C_return(_Thrd_detach(_Thr)));
	}

#if defined(__ghs)
inline int _Thrd_joinX(_Thrd_t _Thr, long *_Res)
#else
inline int _Thrd_joinX(_Thrd_t _Thr, int *_Res)
#endif
	{	// throw exception on failure
	return (_Check_C_return(_Thrd_join(_Thr, _Res)));
	}

inline int _Mtx_initX(_Mtx_t *_Mtx, int _Type)
	{	// throw exception on failure
	return (_Check_C_return(_Mtx_init(_Mtx, _Type)));
	}

inline int _Mtx_lockX(_Mtx_t _THR_INDIR _Mtx)
	{	// throw exception on failure
	return (_Check_C_return(_Mtx_lock(_Mtx)));
	}

inline int _Mtx_trylockX(_Mtx_t _THR_INDIR _Mtx)
	{	// throw exception on failure
	return (_Check_C_return(_Mtx_trylock(_Mtx), _Thrd_busy));
	}

inline int _Mtx_timedlockX(_Mtx_t _THR_INDIR _Mtx, const xtime *_Xt)
	{	// throw exception on failure
	return (_Check_C_return(_Mtx_timedlock(_Mtx, _Xt), _Thrd_timedout));
	}

#if defined(__ghs)
inline int _Mtx_rel_timedlockX(_Mtx_t _THR_INDIR _Mtx, const xtime *_Xt)
	{	// throw exception on failure
	return (_Check_C_return(_Mtx_rel_timedlock(_Mtx, _Xt), _Thrd_timedout));
	}
#endif

inline int _Mtx_unlockX(_Mtx_t _THR_INDIR _Mtx)
	{	// throw exception on failure
	return (_Check_C_return(_Mtx_unlock(_Mtx)));
	}

inline int _Cnd_initX(_Cnd_t *_Cnd)
	{	// throw exception on failure
	return (_Check_C_return(_Cnd_init(_Cnd)));
	}

inline int _Cnd_waitX(_Cnd_t _THR_INDIR _Cnd, _Mtx_t _THR_INDIR _Mtx)
	{	// throw exception on failure
	return (_Check_C_return(_Cnd_wait(_Cnd, _Mtx)));
	}

inline int _Cnd_timedwaitX(_Cnd_t _THR_INDIR _Cnd,
	_Mtx_t _THR_INDIR _Mtx, const xtime *_Xt)
	{	// throw exception on failure
	return (_Check_C_return(_Cnd_timedwait(_Cnd, _Mtx, _Xt), _Thrd_timedout));
	}

#if defined(__ghs)
inline int _Cnd_rel_timedwaitX(_Cnd_t _THR_INDIR _Cnd,
	_Mtx_t _THR_INDIR _Mtx, const xtime *_Xt)
	{	// throw exception on failure
	return (_Check_C_return(_Cnd_rel_timedwait(_Cnd, _Mtx, _Xt), _Thrd_timedout));
	}
#endif

inline int _Cnd_broadcastX(_Cnd_t _THR_INDIR _Cnd)
	{	// throw exception on failure
	return (_Check_C_return(_Cnd_broadcast(_Cnd)));
	}

inline int _Cnd_signalX(_Cnd_t _THR_INDIR _Cnd)
	{	// throw exception on failure
	return (_Check_C_return(_Cnd_signal(_Cnd)));
	}

#if defined(__ghs)
inline int _Sem_initX(_Sem_t *_Sem, int sval)
        {
        return (_Check_C_return(_Sem_init(_Sem, sval)));
        }

inline int _Sem_destroyX(_Sem_t _THR_INDIR _Sem)
        {
        return (_Check_C_return(_Sem_destroy(_Sem)));
        }

inline int _Sem_waitX(_Sem_t _THR_INDIR _Sem)
        {
        return (_Check_C_return(_Sem_wait(_Sem)));
        }

inline int _Sem_timedwaitX(_Sem_t _THR_INDIR _Sem, const xtime *_Xt)
        {
        return (_Check_C_return(_Sem_timedwait(_Sem, _Xt),
				_Thrd_timedout));
        }

inline int _Sem_rel_timedwaitX(_Sem_t _THR_INDIR _Sem, const xtime *_Xt)
        {
        return (_Check_C_return(_Sem_rel_timedwait(_Sem, _Xt),
				_Thrd_timedout));
        }

inline int _Sem_postX(_Sem_t _THR_INDIR _Sem)
        {
        return (_Check_C_return(_Sem_post(_Sem)));
        }

inline int _Sem_getvalueX(_Sem_t _THR_INDIR _Sem, int *sval)
        {
        return (_Check_C_return(_Sem_getvalue(_Sem, sval)));
        }

#endif /* __ghs */

class _Auto_cnd
	{	// clean up condition variable on destruction
public:
	_Auto_cnd(_Cnd_t _THR_INDIR _Cndp)
		: _Active(true), _MyCndp(_Cndp)
		{	// construct from condition variable pointer
		}

	~_Auto_cnd() _NOEXCEPT
		{	// destroy the object
		if (_Active)
			_Cnd_destroy(_MyCndp);
			}

	void _Release()
		{	// release the condition variable
		_Active = false;
		}

private:
	bool _Active;
	_Cnd_t _THR_INDIR _MyCndp;
	};

class _Auto_mtx
	{	// clean up mutex on destruction
public:
	_Auto_mtx(_Mtx_t _THR_INDIR _Mtxp)
		: _Active(true), _MyMtxp(_Mtxp)
		{	// construct from mutex
		}

	~_Auto_mtx() _NOEXCEPT
		{	// destroy the object
		if (_Active)
			_Mtx_destroy(_MyMtxp);
		}

	void _Release()
		{	// release the mutex
		_Active = false;
		}

private:
	bool _Active;
	_Mtx_t _THR_INDIR _MyMtxp;
	};

class _Pad
	{	// base class for launching threads
public:
	_Pad()
		{	// initialize handshake
#if defined(__ghs) && !defined(_USE_CONDITION_VARS)
	         _Sem_initX(&_Sem, 0);
#else /* !defined(__ghs) */
		_Cnd_initX(&_Cond);
		_Auto_cnd _Cnd_cleaner(_THR_ADDR _Cond);
		_Mtx_initX(&_Mtx, _Mtx_plain);
		_Auto_mtx _Mtx_cleaner(_THR_ADDR _Mtx);
		_Started = false;
		_Mtx_lockX(_THR_ADDR _Mtx);
		_Mtx_cleaner._Release();
		_Cnd_cleaner._Release();
#endif
		}

	~_Pad() _NOEXCEPT
		{	// clean up handshake
#if defined(__ghs) && !defined(_USE_CONDITION_VARS)
	        _Sem_destroyX(_Sem);
#else /* !defined(__ghs) */
		_Auto_cnd _Cnd_cleaner(_THR_ADDR _Cond);
		_Auto_mtx _Mtx_cleaner(_THR_ADDR _Mtx);
		_Mtx_unlockX(_THR_ADDR _Mtx);
#endif
		}

	void _Launch(_Thrd_t *_Thr)
		{	// launch a thread
		_Thrd_startX(_Thr, _Call_func, this);
#if defined(__ghs) && !defined(_USE_CONDITION_VARS)
		_Sem_waitX(_Sem);
#else  /* !defined(__ghs) */
		while (!_Started)
			_Cnd_waitX(_THR_ADDR _Cond, _THR_ADDR _Mtx);
#endif
		}

#if defined(__ghs)
	void _Launch_config(long priority, size_t stacksize, _Thrd_t *_Thr)
		{	// launch a thread
		_Thrd_start_configX(_Thr, priority, stacksize, _Call_func, this);
#if !defined(_USE_CONDITION_VARS)
		_Sem_waitX(_Sem);
#else  /* !defined(__ghs) */
		while (!_Started)
			_Cnd_waitX(_THR_ADDR _Cond, _THR_ADDR _Mtx);
#endif
		}
#endif /* defined(__ghs) */

	void _Release()
		{	// notify caller that it's okay to continue
#if defined(__ghs) && !defined(_USE_CONDITION_VARS)
	        _Sem_postX(_Sem);
#else /* !defined(__ghs) */
		_Mtx_lockX(_THR_ADDR _Mtx);
		_Started = true;
		_Cnd_signalX(_THR_ADDR _Cond);
		_Mtx_unlockX(_THR_ADDR _Mtx);
#endif
		}

	virtual void _Go() = 0;

private:
 #if _WIN32_C_LIB && !(defined(__ghs) && defined(__CUSTOM_CXX11_THREAD_LIBRARY))
typedef unsigned int _Call_func_ret;
#define _CALL_FUNC_MODIFIER	_STDCALL

 #elif _HAS_POSIX_C_LIB
typedef void *_Call_func_ret;
#define _CALL_FUNC_MODIFIER

 #elif defined(__ghs__)

typedef long _Call_func_ret;
#define _CALL_FUNC_MODIFIER

 #else /* library type */
  #error unknown library type
 #endif /* library type */

	static _Call_func_ret _STDCALL _Call_func(void *_Data)
#if defined(__ghs) // This is instead defined in the libraries
		;
#else
		{	// entry point for new thread
		static_cast<_Pad *>(_Data)->_Go();
		_Cnd_do_broadcast_at_thread_exit();
		return (0);
		}
#endif /* defined(__ghs) */

	
#if defined(__ghs__) && !defined(_USE_CONDITION_VARS)
	_Sem_t _THR_INDIR _Sem;
#else /* !(defined(__ghs__) && !defined(_USE_CONDITION_VARS)) */
	_Cnd_t _Cond;
	_Mtx_t _Mtx;
	bool _Started;
#endif
	};

template<class _Target>
	class _LaunchPad
		: public _Pad
	{	// template class for launching threads
public:
	template<class _Other> inline
		_LaunchPad(_Other _REFREF _Tgt)
		: _MyTarget(_STD forward<_Other>(_Tgt))
		{	// construct from target
		}

	virtual void _Go()
#if defined(__ghs)
	                                 __attribute__((entry_exit_history(0)))
#endif /* defined(__ghs) */
		{	// run the thread function object
		_Run(this);
		}

private:
	template<size_t... _Idxs>
		static void _Execute(typename _Target::element_type& _Tup,
			integer_sequence<size_t, _Idxs...>)
#if defined(__ghs)
                                         __attribute__((entry_exit_history(0)))
#endif /* defined(__ghs) */
		{	// invoke function object packed in tuple
		_STD invoke(_STD move(_STD get<_Idxs>(_Tup))...);
		}

 #if _HAS_NOEXCEPT
	static void _Run(_LaunchPad *_Ln) _NOEXCEPT	// enforces termination
#if defined(__ghs)
	                                 __attribute__((entry_exit_history(0)))
#endif /* defined(__ghs) */
		{	// construct local unique_ptr and call function object within
		_Target _Local(_STD forward<_Target>(_Ln->_MyTarget));
		_Ln->_Release();
		_Execute(*_Local,
			make_integer_sequence<size_t,
				tuple_size<typename _Target::element_type>::value>());
		}

 #else /* _HAS_NOEXCEPT */
	static void _Run(_LaunchPad *_Ln)
#if defined(__ghs)
	                                 __attribute__((entry_exit_history(0)))
#endif /* defined(__ghs) */
		{	// make local copy of function object and call it
		_TRY_BEGIN
		_Target _Local(_STD forward<_Target>(_Ln->_MyTarget));
		_Ln->_Release();
		_Execute(*_Local,
			make_integer_sequence<size_t,
				tuple_size<typename _Target::element_type>::value>());
		_CATCH_ALL
		_XSTD terminate();
		_CATCH_END
		}
 #endif /* _HAS_NOEXCEPT */

	_Target _MyTarget;
	};

template<class _Target> inline
	void _Launch(_Thrd_t *_Thr, _Target _REFREF _Tg)
	{	// launch a new thread
	_LaunchPad<_Target> _Launcher(_STD forward<_Target>(_Tg));
	_Launcher._Launch(_Thr);
	}

#if defined(__ghs__)
template<class _Target> inline
	void _Launch_config(long priority, size_t stacksize, _Thrd_t *_Thr, 
			_Target _REFREF _Tg)
	{	// launch a new thread
	_LaunchPad<_Target> _Launcher(_STD forward<_Target>(_Tg));
	_Launcher._Launch_config(priority, stacksize, _Thr);
	}
#endif

#if defined(__ghs)
struct _Associated_state_sync_record
{
	_Mtx_t                   _Nat_mtx;
	                        // native mutex to guard _Associated_state
	_Sem_t       _THR_INDIR _Sem;
	unsigned int            _Waiting;
	                        // Semaphore and counter used for
				// synchronization
	int                     _Ready;
	                        // flag that indicates when _Associated_state
				// has been set

	_Associated_state_sync_record
	                        *_Next;
				// next record in the list if this state is
				// registered to be set at thread exit

	_Associated_state_sync_record()
		  : _Nat_mtx(0),
		  _Sem(0),
		  _Waiting(0),
		  _Ready(0),
		  _Next(NULL) { }

	// PRECONDITION:
	// - _Nat_mtx must be held by the current thread
	// - _Ready is not set
	// POSTCONDITION:
	// - _Nat_mtx is unlocked
	// - thread successfully waited on _Sem
	// - (_Ready should have gotten set by someone else)
	void _Wait(void);
	
	// PRECONDITION:
	// - _Nat_mtx must be held by the current thread
	// - _Ready is not set
	// - _Tgt is absolute time for timeout
	// POSTCONDITION:
	// - _Nat_mtx is unlocked
	// - a) thread successfully waited on _Sem (_Ready set by someone else)
	// - b) or, thread timed out waiting on _Sem
	//   - if _Ready was set by someone else, _Sem was possibly posted once
	//     too many times, which is harmless. Return _Thrd_success
	//   - if _Ready was not set, Return _Thrd_timedout
	int _Wait_timed(const xtime *_Tgt);

	// PRECONDITION:
	// - _Nat_mtx must be held by the current thread
	// - _Ready is not set
	// - _Tgt is a relative time interval for the timeout
	// POSTCONDITION:
	// - _Nat_mtx is unlocked
	// - a) thread successfully waited on _Sem (_Ready set by someone else)
	// - b) or, thread timed out waiting on _Sem
	//   - if _Ready was set by someone else, _Sem was possibly posted once
	//     too many times, which is harmless. Return _Thrd_success
	//   - if _Ready was not set, Return _Thrd_timedout
	int _Wait_rel_timed(const xtime *_Tgt);

	// PRECONDITION:
	// - _Ready is not set
	// - _Nat_mtx is held by the current thread
	// POSTCONDITION:
	// - _Ready set to true
	// - _Waiting reset to 0
	// - _Nat_mtx unlocked
	// - _Sem posted on _Waiting number of times (value used from entry)
	void _Notify(void);

	// Add _Sync_rec to the thread-specific list of all records to be
	// notified at thread exit
	static void register_at_thread_exit(
				_Associated_state_sync_record *_Sync_rec);

	// Remove _Sync_rec from the thread-specific list of all records to be
	// notified at thread exit
	static void unregister_at_thread_exit(
				_Associated_state_sync_record *_Sync_rec);
	
};

#endif /* __ghs__ */


_STD_END

#endif /* _THREADS_XTHREAD_ */

/*
 * Copyright (c) by P.J. Plauger. All rights reserved.
 * Consult your license regarding permissions and restrictions.
V8.03b/17:0063 */
