diff --git a/qse/include/qse/si/Thread.hpp b/qse/include/qse/si/Thread.hpp index 3635b6bc..151d174b 100644 --- a/qse/include/qse/si/Thread.hpp +++ b/qse/include/qse/si/Thread.hpp @@ -35,13 +35,14 @@ QSE_BEGIN_NAMESPACE(QSE) - class Thread: protected qse_thr_t, public Uncopyable { public: // native thread hadnle type typedef qse_thr_hnd_t Handle; + typedef int (*ThreadRoutine) (Thread* thr); + enum State { INCUBATING = QSE_THR_INCUBATING, @@ -69,44 +70,58 @@ public: void setStackSize (qse_size_t num) QSE_CPP_NOEXCEPT { qse_thr_setstacksize(this, num); } -#if 0 - static int call_lambda (QSE::Thread* thr) +#if (__cplusplus >= 201103L) || (defined(_MSC_VER) && _MSC_VER >= 1900) //C++11 or later + using ThreadLambda = std::function; + + static int call_lambda (qse_thr_t* thr, void* ctx) { - return thr->x_func (thr); + 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 - int start (F&& f, int flags) - { - this->x_func = std::bind(f); - return qse_thr_start (this, (qse_thr_rtn_t)Thread::call_lambda, flags); + 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 -#if (__cplusplus >= 201103L) || (defined(_MSC_VER) && _MSC_VER >= 1900) //C++11 or later - using lfunc_t = std::function; - - static int call_lambda (QSE::Thread* thr) - { - return thr->x_func (thr); - } - - int start (lfunc_t f, int flags) - { - this->x_func = std::move(f); - return qse_thr_start (this, (qse_thr_rtn_t)Thread::call_lambda, flags); - } - - //std::function x_func; - lfunc_t x_func; #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; + virtual int stop () QSE_CPP_NOEXCEPT; virtual int main () { return 0; } + // return the context pointer value + const void* getContext () const { return this->__exctx; } + void* getContext () { return this->__exctx; } + + // change the context pointer value + void setContext (void* ctx) { this->__exctx = ctx; } + int join () QSE_CPP_NOEXCEPT { return qse_thr_join(this); } int detach () QSE_CPP_NOEXCEPT { return qse_thr_detach(this); } @@ -125,7 +140,16 @@ public: int unblockAllSignals () QSE_CPP_NOEXCEPT { return qse_thr_unblockallsigs (this); } 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; + + + static int thr_func_call_rtn (qse_thr_t* rtn, void* ctx); }; diff --git a/qse/include/qse/si/thr.h b/qse/include/qse/si/thr.h index ce50133c..a6d191b8 100644 --- a/qse/include/qse/si/thr.h +++ b/qse/include/qse/si/thr.h @@ -40,7 +40,7 @@ typedef struct qse_thr_t qse_thr_t; * qse_thr_open() and qse_thr_start(). When it is executed, the pointer to the * calling thread object is passed as its first argument. */ -typedef int (*qse_thr_rtn_t) (qse_thr_t*); +typedef int (*qse_thr_rtn_t) (qse_thr_t* thr, void* ctx); enum qse_thr_state_t { @@ -98,11 +98,13 @@ struct qse_thr_t { qse_mmgr_t* mmgr; - qse_thr_rtn_t __main_routine; - qse_thr_rtn_t __temp_routine; + unsigned int __flags; qse_size_t __stacksize; + qse_thr_rtn_t __main_routine; + void* __ctx; + qse_thr_hnd_t __handle; qse_thr_state_t __state; @@ -172,6 +174,7 @@ QSE_EXPORT void qse_thr_setstacksize ( QSE_EXPORT int qse_thr_start ( qse_thr_t* thr, qse_thr_rtn_t func, + void* ctx, int flags /**< 0 or bitwise-or of the #qse_thr_flag_t enumerators */ ); diff --git a/qse/lib/si/Thread.cpp b/qse/lib/si/Thread.cpp index 3618791e..39853842 100644 --- a/qse/lib/si/Thread.cpp +++ b/qse/lib/si/Thread.cpp @@ -27,13 +27,12 @@ #include #include "thr-prv.h" -#include QSE_BEGIN_NAMESPACE(QSE) Thread::Handle Thread::INVALID_HANDLE = QSE_THR_HND_INVALID; -Thread::Thread() QSE_CPP_NOEXCEPT //: thread_target (QSE_NULL) +Thread::Thread() QSE_CPP_NOEXCEPT : __exctx(QSE_NULL) { //qse_thr_init (this, this->getMmgr()); qse_thr_init (this, QSE_NULL); @@ -41,7 +40,7 @@ Thread::Thread() QSE_CPP_NOEXCEPT //: thread_target (QSE_NULL) Thread::~Thread () QSE_CPP_NOEXCEPT { - QSE_ASSERT (this->__state != Thread::RUNNING); + QSE_ASSERT (this->__state != QSE_THR_RUNNING); // it is subclasses' responsibility to stop the thread gracefully. // so stop is not called here. // this->stop (); @@ -50,15 +49,39 @@ Thread::~Thread () QSE_CPP_NOEXCEPT qse_thr_fini (this); } -int thr_func (qse_thr_t* thr) +int Thread::thr_func_call_rtn (qse_thr_t* thr, void* ctx) { - Thread* t = (Thread*)thr; + // '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) +{ + Thread* t = (Thread*)ctx; return t->main (); } int Thread::start (int flags) QSE_CPP_NOEXCEPT { - return qse_thr_start(this, thr_func, flags); + return qse_thr_start(this, thr_func_call_main, this, flags); } int Thread::stop () QSE_CPP_NOEXCEPT diff --git a/qse/lib/si/thr.c b/qse/lib/si/thr.c index 9eef7119..16dc1c11 100644 --- a/qse/lib/si/thr.c +++ b/qse/lib/si/thr.c @@ -139,7 +139,7 @@ static void* __thread_main (void* arg) pthread_setcanceltype (PTHREAD_CANCEL_ASYNCHRONOUS, QSE_NULL); #endif - thr->__return_code = thr->__main_routine(thr); + thr->__return_code = thr->__main_routine(thr, thr->__ctx); thr->__state = QSE_THR_TERMINATED; #if defined(_WIN32) @@ -243,12 +243,13 @@ static int __cancel_thread (qse_thr_t* thr) return 0; } -int qse_thr_start (qse_thr_t* thr, qse_thr_rtn_t func, int flags) +int qse_thr_start (qse_thr_t* thr, qse_thr_rtn_t func, void* ctx, int flags) { if (thr->__state == QSE_THR_RUNNING) return -1; thr->__flags = flags; thr->__main_routine = func; + thr->__ctx = ctx; if (__create_thread(thr) == -1) { diff --git a/qse/samples/si/rwl01.c b/qse/samples/si/rwl01.c index 0c3add84..c46eff81 100644 --- a/qse/samples/si/rwl01.c +++ b/qse/samples/si/rwl01.c @@ -99,7 +99,7 @@ static void test_001 (void) xtn->id = i; } - for (i = 0; i < QSE_COUNTOF(t); i++) qse_thr_start (t[i], thr_exec, 0); + for (i = 0; i < QSE_COUNTOF(t); i++) qse_thr_start (t[i], thr_exec, QSE_NULL, 0); for (i = 0; i < QSE_COUNTOF(t); i++) qse_thr_join (t[i]); for (i = 0; i < QSE_COUNTOF(t); i++) qse_thr_close (t[i]); diff --git a/qse/samples/si/thr01.c b/qse/samples/si/thr01.c index 5f07f7d7..d6fb1da7 100644 --- a/qse/samples/si/thr01.c +++ b/qse/samples/si/thr01.c @@ -48,7 +48,7 @@ static int test1 (void) qse_thr_setstacksize (thr, 64000); - if (qse_thr_start(thr, thr_func, QSE_THR_SIGNALS_BLOCKED) <= -1) + if (qse_thr_start(thr, thr_func, QSE_NULL, QSE_THR_SIGNALS_BLOCKED) <= -1) { qse_printf (QSE_T("cannot start thread\n")); qse_thr_close (thr); diff --git a/qse/samples/si/thr02.cpp b/qse/samples/si/thr02.cpp index f3feec25..726cf65c 100644 --- a/qse/samples/si/thr02.cpp +++ b/qse/samples/si/thr02.cpp @@ -1,4 +1,5 @@ #include +#include #include #include @@ -12,18 +13,22 @@ #include static int g_stopreq = 0; - +static qse_mtx_t* g_prmtx = QSE_NULL; class MyThread: public QSE::Thread { public: + MyThread(): stopreq(0) {} + int main () { int i = 0; while (!this->stopreq) { - qse_printf (QSE_T("%d\n"), i); + qse_mtx_lock (g_prmtx, QSE_NULL); + qse_printf (QSE_T("m %p -> %d\n"), this, i); + qse_mtx_unlock (g_prmtx); i++; sleep (1); } @@ -36,47 +41,26 @@ public: static int test1 (void) { - MyThread thr; + MyThread thr1; + QSE::Thread thr2; + QSE::Thread thr3; + int localstopreq = 0; - thr.setStackSize (64000); + g_prmtx = qse_mtx_open (QSE_MMGR_GETDFL(), 0); - //thr = QSE::Thread::start (task, QSE::Thread::SIGNALS_BLOCKED); - if (thr.start(QSE::Thread::SIGNALS_BLOCKED) <= -1) - { - qse_printf (QSE_T("cannot start thread\n")); - return -1; - } + thr1.setStackSize (64000); + thr2.setStackSize (64000); + thr3.setStackSize (64000); - while (!g_stopreq) - { - if (thr.getState() == QSE::Thread::TERMINATED) break; - sleep (1); - } - - if (g_stopreq) - { - /*qse_thr_stop (thr);*/ - thr.stopreq = 1; - } - - thr.join (); - qse_printf (QSE_T("thread ended with retcode %d\n"), thr.getReturnCode()); - - return 0; -} - -static int test2 (void) -{ - QSE::Thread thr; - - thr.setStackSize (64000); - - auto lambda = [&g_stopreq](QSE::Thread* thr) { + auto lambda = [](QSE::Thread* thr) { int i = 0; + int* stopreqptr = (int*)thr->getContext(); - while (!g_stopreq) + while (!*stopreqptr) { - qse_printf (QSE_T("%d\n"), i); + qse_mtx_lock (g_prmtx, QSE_NULL); + qse_printf (QSE_T("l %p -> %d\n"), thr, i); + qse_mtx_unlock (g_prmtx); i++; sleep (1); } @@ -84,31 +68,71 @@ static int test2 (void) return i; }; - if (thr.start(lambda, QSE::Thread::SIGNALS_BLOCKED) <= -1) + auto lambda_with_capture = [&localstopreq](QSE::Thread* thr) { + int i = 0; + + while (!localstopreq) + { + qse_mtx_lock (g_prmtx, QSE_NULL); + qse_printf (QSE_T("lc %p -> %d\n"), thr, i); + qse_mtx_unlock (g_prmtx); + i++; + sleep (1); + } + + return i; + }; + + if (thr1.start(QSE::Thread::SIGNALS_BLOCKED) <= -1) { - qse_printf (QSE_T("cannot start thread\n")); + qse_printf (QSE_T("cannot start thread1\n")); return -1; } + // the lambda expression with no capture can be passed as a function pointer + // as long as the signature matches QSE::Thread::ThreadRoutine. + thr2.setContext (&localstopreq); + if (thr2.start(lambda, QSE::Thread::SIGNALS_BLOCKED) <= -1) + { + qse_printf (QSE_T("cannot start thread2\n")); + return -1; + } + + + if (thr3.startl(lambda_with_capture, QSE::Thread::SIGNALS_BLOCKED) <= -1) + { + qse_printf (QSE_T("cannot start thread3\n")); + return -1; + } + + while (!g_stopreq) { - if (thr.getState() == QSE::Thread::TERMINATED) break; + if (thr1.getState() == QSE::Thread::TERMINATED && + thr2.getState() == QSE::Thread::TERMINATED && + thr3.getState() == QSE::Thread::TERMINATED) break; sleep (1); } if (g_stopreq) { - /*qse_thr_stop (thr);*/ - //thr.stopreq = 1; + thr1.stopreq = 1; + localstopreq = 1; } - thr.join (); - qse_printf (QSE_T("thread ended with retcode %d\n"), thr.getReturnCode()); + thr1.join (); + thr2.join (); + thr3.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_mtx_close (g_prmtx); return 0; } + static void handle_sigint (int sig, siginfo_t* siginfo, void* ctx) { g_stopreq = 1; @@ -163,7 +187,7 @@ int main () set_signal (SIGINT, handle_sigint); qse_open_stdsios (); - test2(); + test1(); qse_close_stdsios (); set_signal_to_default (SIGINT);