touched up QSE::Thread
This commit is contained in:
parent
5a3586232f
commit
8256cee77f
@ -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<int(QSE::Thread*)>;
|
||||
|
||||
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 <typename F>
|
||||
int start (F&& f, int flags)
|
||||
int startlx (F&& f, int flags)
|
||||
{
|
||||
this->x_func = std::bind(f);
|
||||
return qse_thr_start (this, (qse_thr_rtn_t)Thread::call_lambda, 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
|
||||
|
||||
#if (__cplusplus >= 201103L) || (defined(_MSC_VER) && _MSC_VER >= 1900) //C++11 or later
|
||||
using lfunc_t = std::function<int(QSE::Thread*thr)>;
|
||||
|
||||
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<int(QSE::Thread*)> 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);
|
||||
};
|
||||
|
||||
|
||||
|
@ -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 */
|
||||
);
|
||||
|
||||
|
@ -27,13 +27,12 @@
|
||||
#include <qse/si/Thread.hpp>
|
||||
#include "thr-prv.h"
|
||||
|
||||
#include <stdio.h>
|
||||
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
|
||||
|
@ -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)
|
||||
{
|
||||
|
@ -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]);
|
||||
|
||||
|
@ -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);
|
||||
|
@ -1,4 +1,5 @@
|
||||
#include <qse/si/Thread.hpp>
|
||||
#include <qse/si/mtx.h>
|
||||
#include <qse/si/sio.h>
|
||||
#include <qse/cmn/mem.h>
|
||||
|
||||
@ -12,18 +13,22 @@
|
||||
#include <string.h>
|
||||
|
||||
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_printf (QSE_T("cannot start thread\n"));
|
||||
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 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);
|
||||
|
Loading…
Reference in New Issue
Block a user