From cef9a7a112a8322e2c6498021df59e4a8f7b5246 Mon Sep 17 00:00:00 2001 From: Alexey Melnichuk Date: Tue, 4 Feb 2014 11:06:17 +0400 Subject: Add. `thread:alive()` method. --- .travis.yml | 1 + README.md | 18 ++++++++++++++ lakefile | 1 + src/llthread.c | 69 +++++++++++++++++++++++++++++++++++++++++++++++++++++ test/test_alive.lua | 35 +++++++++++++++++++++++++++ 5 files changed, 124 insertions(+) create mode 100644 test/test_alive.lua diff --git a/.travis.yml b/.travis.yml index b6dc082..dc2ef16 100644 --- a/.travis.yml +++ b/.travis.yml @@ -56,6 +56,7 @@ script: - lua$LUA_SFX test_logger.lua - lua$LUA_SFX test_pass_cfunction.lua - lua$LUA_SFX test_load_llthreads2.lua + - lua$LUA_SFX test_alive.lua notifications: email: diff --git a/README.md b/README.md index ba38ebd..66e4d8b 100644 --- a/README.md +++ b/README.md @@ -13,6 +13,7 @@ This is full dropin replacement for [llthreads](https://github.com/Neopallium/lu ##Additional * thread:join() method support zero timeout to check if thread alive (does not work on Windows with pthreads) * thread:join() method support arbitrary timeout on Windows threads +* thread:alive() method return whether the thread is alive (does not work on Windows with pthreads) * set_logger function allow logging errors (crash Lua VM) in current llthread's threads * thread:start() has additional parameter which control in which thread child Lua VM will be destroyed * allow pass cfunctions to child thread (e.g. to initialize Lua state) @@ -89,5 +90,22 @@ llthreads.new([[ ]], preload):start(true) ``` +### Wait while thread is alive +``` Lua +local thread = require "llthreads".new[[ + require "utils".sleep(5) + return 1 +]] +thread:start() + +-- we can not use `thread:join(0)` because we can not call it twice +-- so all returned vaules will be lost +while thread:alive() do + -- do some work +end + +local ok, ret = thread:join() -- true, 1 +``` + [![Bitdeli Badge](https://d2weczhvl823v0.cloudfront.net/moteus/lua-llthreads2/trend.png)](https://bitdeli.com/free "Bitdeli Badge") diff --git a/lakefile b/lakefile index b7786b1..060035a 100644 --- a/lakefile +++ b/lakefile @@ -36,6 +36,7 @@ target('test', install, function() run_test('test_register_ffi.lua') run_test('test_logger.lua') run_test('test_pass_cfunction.lua') + run_test('test_alive.lua') if not test_summary() then quit("test fail") diff --git a/src/llthread.c b/src/llthread.c index ac485e3..0ffa727 100644 --- a/src/llthread.c +++ b/src/llthread.c @@ -457,6 +457,35 @@ static int llthread_join(llthread_t *this, join_timeout_t timeout) { } } +static int llthread_alive(llthread_t *this) { + llthread_validate(this); + + if(IS(this, JOINED)){ + return JOIN_OK; + } else{ +#ifndef USE_PTHREAD + DWORD ret = 0; + if(INVALID_THREAD == this->thread) return JOIN_OK; + ret = WaitForSingleObject( this->thread, 0 ); + if( ret == WAIT_OBJECT_0) return JOIN_OK; + if( ret == WAIT_TIMEOUT ) return JOIN_ETIMEDOUT; + return JOIN_FAIL; +#else + int rc = pthread_kill(this->thread, 0); + if(rc == 0){ /* still alive */ + return JOIN_ETIMEDOUT; + } + + if(rc != ESRCH){ + /*@fixme what else it can be ?*/ + return rc; + } + + return JOIN_OK; +#endif + } +} + static llthread_t *llthread_create(lua_State *L, const char *code, size_t code_len) { llthread_t *this = llthread_new(); llthread_child_t *child = this->child; @@ -592,6 +621,45 @@ static int l_llthread_join(lua_State *L) { } +static int l_llthread_alive(lua_State *L) { + llthread_t *this = l_llthread_at(L, 1); + llthread_child_t *child = this->child; + int rc; + + if(!IS(this, STARTED )) { + return fail(L, "Can't join a thread that hasn't be started."); + } + if( IS(this, DETACHED) && !IS(this, JOINABLE)) { + return fail(L, "Can't join a thread that has been detached."); + } + if( IS(this, JOINED )) { + return fail(L, "Can't join a thread that has already been joined."); + } + + /* join the thread. */ + rc = llthread_alive(this); + + if( rc == JOIN_ETIMEDOUT ){ + lua_pushboolean(L, 1); + return 1; + } + + if(rc == JOIN_OK){ + lua_pushboolean(L, 0); + return 1; + } + + { + char buf[ERROR_LEN]; + strerror_r(errno, buf, ERROR_LEN); + + /* llthread_cleanup_child(this); */ + + return fail(L, buf); + } + +} + static int l_llthread_new(lua_State *L) { size_t lua_code_len; const char *lua_code = luaL_checklstring(L, 1, &lua_code_len); llthread_t **this = lutil_newudatap(L, llthread_t*, LLTHREAD_TAG); @@ -605,6 +673,7 @@ static int l_llthread_new(lua_State *L) { static const struct luaL_Reg l_llthread_meth[] = { {"start", l_llthread_start }, {"join", l_llthread_join }, + {"alive", l_llthread_alive }, {"__gc", l_llthread_delete }, {NULL, NULL} diff --git a/test/test_alive.lua b/test/test_alive.lua new file mode 100644 index 0000000..ecce163 --- /dev/null +++ b/test/test_alive.lua @@ -0,0 +1,35 @@ +local llthreads = require"llthreads" +local utils = require "utils" +local sleep = utils.sleep + +local include = utils.thread_init .. [[ +local llthreads = require"llthreads" +local sleep = require "utils".sleep +]] + +local thread = llthreads.new(include .. [[ + sleep(5) + return 1,2,3 +]]) + +assert(nil == thread:alive()) + +thread:start() + +assert(true == thread:alive()) + +for i = 1, 10 do + if not thread:alive() then break end + sleep(1) +end + +assert(false == thread:alive()) + +local ok,a,b,c = thread:join(0) +assert(ok == true) +assert(a == 1) +assert(b == 2) +assert(c == 3) + +print("Done!") + -- cgit v1.2.3-55-g6feb