added QSE::ThreadR and QSE::ThreadC
This commit is contained in:
		| @ -29,9 +29,6 @@ | ||||
|  | ||||
| #include <qse/si/thr.h> | ||||
| #include <qse/Uncopyable.hpp> | ||||
| #include <qse/cmn/Mmged.hpp> | ||||
|  | ||||
| #include <functional> | ||||
|  | ||||
| QSE_BEGIN_NAMESPACE(QSE) | ||||
|  | ||||
| @ -41,8 +38,6 @@ public: | ||||
| 	// native thread hadnle type | ||||
| 	typedef qse_thr_hnd_t Handle; | ||||
|  | ||||
| 	typedef int (*ThreadRoutine) (Thread* thr); | ||||
|  | ||||
| 	enum State | ||||
| 	{ | ||||
| 		INCUBATING = QSE_THR_INCUBATING, | ||||
| @ -69,45 +64,6 @@ public: | ||||
| 	qse_size_t getStackSize () const QSE_CPP_NOEXCEPT { return this->__stacksize; } | ||||
| 	void setStackSize (qse_size_t num) QSE_CPP_NOEXCEPT { qse_thr_setstacksize(this, num); } | ||||
|  | ||||
|  | ||||
| #if (__cplusplus >= 201103L) || (defined(_MSC_VER) && _MSC_VER >= 1900) //C++11 or later | ||||
| 	using ThreadLambda = std::function<int(QSE::Thread*)>; | ||||
|  | ||||
| 	static int call_lambda (qse_thr_t* thr, void* ctx) | ||||
| 	{ | ||||
| 		Thread* t = (Thread*)ctx; | ||||
| 		return t->__tmplam (t); | ||||
| 	} | ||||
|  | ||||
| 	int startl (ThreadLambda&& f, int flags) | ||||
| 	{ | ||||
| 		this->__tmplam = QSE_CPP_RVREF(f); | ||||
| 		return qse_thr_start (this, (qse_thr_rtn_t)Thread::call_lambda, this, flags); | ||||
| 	} | ||||
|  | ||||
| #if 0 | ||||
| 	static int call_lambda_lx (qse_thr_t* thr, void* ctx) | ||||
| 	{ | ||||
| 		Thread* t = (Thread*)ctx; | ||||
| 		//return ([]int(QSE::Thread*))t->__tmpvoid (t); | ||||
| 	} | ||||
|  | ||||
| 	template <typename F> | ||||
| 	int startlx (F&& f, int flags) | ||||
| 	{ | ||||
| 		this->__tmplam = QSE_CPP_RVREF(f); | ||||
| 		auto xx = QSE_CPP_RVREF(f); | ||||
| 		this->__tmpvoid = (void*)&xx; | ||||
| 		return qse_thr_start (this, (qse_thr_rtn_t)Thread::call_lambda_lx, this, flags); | ||||
| 	} | ||||
| #endif | ||||
|  | ||||
| #endif | ||||
|  | ||||
|  | ||||
| 	// execute the given function in a thread. | ||||
| 	virtual int start (ThreadRoutine rtn, int flags = 0) QSE_CPP_NOEXCEPT; | ||||
|  | ||||
| 	// execute the main method defined in this class in a thread. | ||||
| 	virtual int start (int flags = 0) QSE_CPP_NOEXCEPT; | ||||
|  | ||||
| @ -141,17 +97,72 @@ public: | ||||
|  | ||||
| protected: | ||||
| 	void* __exctx; | ||||
| 	ThreadRoutine __tmprtn; | ||||
| #if (__cplusplus >= 201103L) || (defined(_MSC_VER) && _MSC_VER >= 1900) //C++11 or later | ||||
| 	ThreadLambda __tmplam; | ||||
| //	void* __tmpvoid; | ||||
| #endif | ||||
| 	static Handle INVALID_HANDLE; | ||||
| }; | ||||
|  | ||||
| class ThreadR: public Thread | ||||
| { | ||||
| public: | ||||
| 	typedef int (*ThreadRoutine) (Thread* thr); | ||||
|  | ||||
| 	// execute the given function in a thread. | ||||
| 	virtual int start (ThreadRoutine rtn, int flags = 0) QSE_CPP_NOEXCEPT; | ||||
|  | ||||
| protected: | ||||
| 	ThreadRoutine __tmprtn; | ||||
| 	static int thr_func_call_rtn (qse_thr_t* rtn, void* ctx); | ||||
| }; | ||||
|  | ||||
| #if (__cplusplus >= 201103L) || (defined(_MSC_VER) && _MSC_VER >= 1900) //C++11 or later | ||||
|  | ||||
| #if 0 | ||||
| // i don't want to use std::function.  | ||||
| class LambdaThread: public Thread | ||||
| { | ||||
| public: | ||||
| 	static int call_lambda (qse_thr_t* thr, void* ctx) | ||||
| 	{ | ||||
| 		LambdaThread* t = (LambdaThread*)ctx; | ||||
| 		return t->__tmplam (t); | ||||
| 	} | ||||
|  | ||||
| 	template <typename X> | ||||
| 	int startl (X&& f, int flags) QSE_CPP_NOEXCEPT | ||||
| 	{ | ||||
| 		this->__tmplam = QSE_CPP_RVREF(f); | ||||
| 		return qse_thr_start (this, (qse_thr_rtn_t)LambdaThread::call_lambda, this, flags); | ||||
| 	} | ||||
|  | ||||
| protected: | ||||
| 	std::function<int(LambdaThread*)> __tmplam;  | ||||
| }; | ||||
| #endif | ||||
|  | ||||
| template <typename F> | ||||
| class ThreadC: public Thread | ||||
| { | ||||
| public: | ||||
| 	ThreadC () {} | ||||
| 	ThreadC (F& f): __lfunc(f) { } | ||||
| 	//ThreadC (F&& f): __lfunc(QSE_CPP_RVREF(f)) { } | ||||
|  | ||||
| 	static int call_lambda (qse_thr_t* thr, void* ctx) | ||||
| 	{ | ||||
| 		ThreadC<F>* t = (ThreadC<F>*)ctx; | ||||
| 		return t->__lfunc (t); | ||||
| 	} | ||||
|  | ||||
| 	int start (int flags = 0) QSE_CPP_NOEXCEPT | ||||
| 	{ | ||||
| 		return qse_thr_start (this, (qse_thr_rtn_t)ThreadC<F>::call_lambda, this, flags); | ||||
| 	} | ||||
|  | ||||
| protected: | ||||
| 	F __lfunc; | ||||
| }; | ||||
|  | ||||
| #endif | ||||
|  | ||||
|  | ||||
| QSE_END_NAMESPACE(QSE) | ||||
|  | ||||
|  | ||||
| @ -49,29 +49,6 @@ Thread::~Thread () QSE_CPP_NOEXCEPT | ||||
| 	qse_thr_fini (this); | ||||
| } | ||||
|  | ||||
| int Thread::thr_func_call_rtn (qse_thr_t* thr, void* ctx) | ||||
| { | ||||
| 	// 'thr' may not be point to the actual Thread  | ||||
| 	// for the reason stated in Thread::start().  | ||||
| 	// utilize the ctx pointer passed in Thread::start(). | ||||
| 	Thread* t = (Thread*)ctx; | ||||
| 	return t->__tmprtn(t); | ||||
| } | ||||
|  | ||||
| int Thread::start (ThreadRoutine rtn, int flags) QSE_CPP_NOEXCEPT | ||||
| { | ||||
| 	if (this->__state == QSE_THR_RUNNING) return -1; | ||||
|  | ||||
| 	// this != (qse_thr_t*)this may not be equal if this class | ||||
| 	// has some internal added data fields. e.g. it contains | ||||
| 	// a virtual function. direct invocation without the extra ctx pointer | ||||
| 	// like this has some implications when attempting to convert | ||||
| 	// qse_thr_t* to Thread*. | ||||
| 	// 	qse_thr_start (this, (qse_thr_rtn_t)rtn, QSE_NULL, flags); | ||||
| 	// so i pass a void pointer 'this' as the third argument. | ||||
| 	this->__tmprtn = rtn; | ||||
| 	return qse_thr_start(this, thr_func_call_rtn, this, flags); | ||||
| } | ||||
|  | ||||
| static int thr_func_call_main (qse_thr_t* thr, void* ctx) | ||||
| { | ||||
| @ -93,4 +70,29 @@ int Thread::stop () QSE_CPP_NOEXCEPT | ||||
| 	return qse_thr_stop(this); | ||||
| } | ||||
|  | ||||
|  | ||||
| int ThreadR::thr_func_call_rtn (qse_thr_t* thr, void* ctx) | ||||
| { | ||||
| 	// 'thr' may not be point to the actual Thread  | ||||
| 	// for the reason stated in Thread::start().  | ||||
| 	// utilize the ctx pointer passed in Thread::start(). | ||||
| 	ThreadR* t = (ThreadR*)ctx; | ||||
| 	return t->__tmprtn(t); | ||||
| } | ||||
|  | ||||
| int ThreadR::start (ThreadRoutine rtn, int flags) QSE_CPP_NOEXCEPT | ||||
| { | ||||
| 	if (this->__state == QSE_THR_RUNNING) return -1; | ||||
|  | ||||
| 	// this != (qse_thr_t*)this may not be equal if this class | ||||
| 	// has some internal added data fields. e.g. it contains | ||||
| 	// a virtual function. direct invocation without the extra ctx pointer | ||||
| 	// like this has some implications when attempting to convert | ||||
| 	// qse_thr_t* to Thread*. | ||||
| 	// 	qse_thr_start (this, (qse_thr_rtn_t)rtn, QSE_NULL, flags); | ||||
| 	// so i pass a void pointer 'this' as the third argument. | ||||
| 	this->__tmprtn = rtn; | ||||
| 	return qse_thr_start(this, thr_func_call_rtn, this, flags); | ||||
| } | ||||
|  | ||||
| QSE_END_NAMESPACE(QSE) | ||||
|  | ||||
| @ -39,20 +39,41 @@ public: | ||||
| 	int stopreq; | ||||
| }; | ||||
|  | ||||
| class Functor | ||||
| { | ||||
| public: | ||||
| 	int operator() (QSE::Thread* thr) | ||||
| 	{ | ||||
| 		int i = 0; | ||||
| 		int* stopreqptr = (int*)thr->getContext(); | ||||
|  | ||||
| 		while (!*stopreqptr) | ||||
| 		{ | ||||
| 			qse_mtx_lock (g_prmtx, QSE_NULL); | ||||
| 			qse_printf (QSE_T("fc %p -> %d\n"), this, i); | ||||
| 			qse_mtx_unlock (g_prmtx); | ||||
| 			i++; | ||||
| 			sleep (1); | ||||
| 		} | ||||
|  | ||||
| 		return i; | ||||
| 	} | ||||
| }; | ||||
|  | ||||
| static int test1 (void) | ||||
| { | ||||
| 	MyThread thr1; | ||||
| 	QSE::Thread thr2; | ||||
| 	QSE::Thread thr3; | ||||
| 	int localstopreq = 0; | ||||
| 	QSE::ThreadR thr2; | ||||
|  | ||||
| 	int localstopreq = 0; | ||||
| 	g_prmtx = qse_mtx_open (QSE_MMGR_GETDFL(), 0); | ||||
|  | ||||
| 	thr1.setStackSize (64000); | ||||
| 	thr2.setStackSize (64000); | ||||
| 	thr3.setStackSize (64000); | ||||
| 	 | ||||
| 	auto lambda = [](QSE::Thread* thr) {  | ||||
|  | ||||
| 	auto lambda = [](QSE::Thread* thr)->int  | ||||
| 	{  | ||||
| 		int i = 0; | ||||
| 		int* stopreqptr = (int*)thr->getContext(); | ||||
|  | ||||
| @ -68,7 +89,8 @@ static int test1 (void) | ||||
| 		return i; | ||||
| 	}; | ||||
|  | ||||
| 	auto lambda_with_capture = [&localstopreq](QSE::Thread* thr) {  | ||||
| 	auto lambda_with_capture = [&localstopreq](QSE::Thread* thr)->int | ||||
| 	{  | ||||
| 		int i = 0; | ||||
|  | ||||
| 		while (!localstopreq) | ||||
| @ -98,34 +120,62 @@ static int test1 (void) | ||||
| 		return -1; | ||||
| 	} | ||||
|  | ||||
|  | ||||
| 	if (thr3.startl(lambda_with_capture, QSE::Thread::SIGNALS_BLOCKED) <= -1) | ||||
| 	//QSE::LambdaThread thr3; | ||||
| 	QSE::ThreadC<decltype(lambda)> thr3 (lambda); | ||||
| 	thr3.setStackSize (64000); | ||||
| 	thr3.setContext (&localstopreq); | ||||
| 	if (thr3.start(QSE::Thread::SIGNALS_BLOCKED) <= -1) | ||||
| 	{ | ||||
| 		qse_printf (QSE_T("cannot start thread3\n")); | ||||
| 		return -1; | ||||
| 	} | ||||
|  | ||||
| 	// turn a lambda with capture to a thread | ||||
| 	QSE::ThreadC<decltype(lambda_with_capture)> thr4 (lambda_with_capture); | ||||
| 	thr4.setStackSize (64000); | ||||
| 	if (thr4.start(QSE::Thread::SIGNALS_BLOCKED) <= -1) | ||||
| 	{ | ||||
| 		qse_printf (QSE_T("cannot start thread4\n")); | ||||
| 		return -1; | ||||
| 	} | ||||
|  | ||||
| 	// turn a functor to a thread | ||||
| 	QSE::ThreadC<Functor> thr5; | ||||
| 	thr5.setStackSize (64000); | ||||
| 	thr5.setContext (&localstopreq); | ||||
| 	if (thr5.start(QSE::Thread::SIGNALS_BLOCKED) <= -1) | ||||
| 	{ | ||||
| 		qse_printf (QSE_T("cannot start thread4\n")); | ||||
| 		return -1; | ||||
| 	} | ||||
|  | ||||
| 	while (!g_stopreq) | ||||
| 	{ | ||||
| 		if (thr1.getState() == QSE::Thread::TERMINATED &&  | ||||
| 		    thr2.getState() == QSE::Thread::TERMINATED && | ||||
| 		    thr3.getState() == QSE::Thread::TERMINATED) break; | ||||
| 		    thr3.getState() == QSE::Thread::TERMINATED && | ||||
| 		    thr4.getState() == QSE::Thread::TERMINATED && | ||||
| 		    thr5.getState() == QSE::Thread::TERMINATED) break; | ||||
| 		sleep (1); | ||||
| 	} | ||||
|  | ||||
| 	if (g_stopreq)  | ||||
| 	{ | ||||
| 		thr1.stopreq = 1; | ||||
| 		localstopreq = 1; | ||||
| 		thr1.stopreq = 1; | ||||
| 	} | ||||
|  | ||||
| 	thr1.join (); | ||||
| 	thr2.join (); | ||||
| 	thr3.join (); | ||||
| 	thr4.join (); | ||||
| 	thr5.join (); | ||||
|  | ||||
| 	qse_printf (QSE_T("thread1 ended with retcode %d\n"), thr1.getReturnCode()); | ||||
| 	qse_printf (QSE_T("thread2 ended with retcode %d\n"), thr2.getReturnCode()); | ||||
| 	qse_printf (QSE_T("thread3 ended with retcode %d\n"), thr3.getReturnCode()); | ||||
| 	qse_printf (QSE_T("thread4 ended with retcode %d\n"), thr4.getReturnCode()); | ||||
| 	qse_printf (QSE_T("thread5 ended with retcode %d\n"), thr5.getReturnCode()); | ||||
|  | ||||
| 	qse_mtx_close (g_prmtx); | ||||
| 	return 0; | ||||
|  | ||||
		Reference in New Issue
	
	Block a user