aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authormoteus <mimir@newmail.ru>2013-12-26 18:52:58 +0400
committermoteus <mimir@newmail.ru>2013-12-26 18:52:58 +0400
commite3fe5123a36643634e00de6a1bb940e6e1febade (patch)
tree8eecc3aa10e8c6842efe1d798758942f35f555f6
parent5291caae5be68250a4d9aafa54fe2081ab2a6113 (diff)
downloadlua-llthreads2-e3fe5123a36643634e00de6a1bb940e6e1febade.tar.gz
lua-llthreads2-e3fe5123a36643634e00de6a1bb940e6e1febade.tar.bz2
lua-llthreads2-e3fe5123a36643634e00de6a1bb940e6e1febade.zip
Add. `joinable` parameter to `start` method which control in which thread child Lua VM will be destroyed.
-rw-r--r--.travis.yml1
-rw-r--r--README.md41
-rw-r--r--src/llthread.c95
-rw-r--r--test/test_join_detach.lua26
-rw-r--r--test/test_join_timeout.lua2
5 files changed, 137 insertions, 28 deletions
diff --git a/.travis.yml b/.travis.yml
index 4a28f5e..7162735 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -49,6 +49,7 @@ script:
49 - lua$LUA_SFX test_llthreads.lua 49 - lua$LUA_SFX test_llthreads.lua
50 - lua$LUA_SFX test_register_llthreads.lua 50 - lua$LUA_SFX test_register_llthreads.lua
51 - lua$LUA_SFX test_join_timeout.lua 51 - lua$LUA_SFX test_join_timeout.lua
52 - lua$LUA_SFX test_join_detach.lua
52 53
53notifications: 54notifications:
54 email: 55 email:
diff --git a/README.md b/README.md
index b14d44d..a5dc46e 100644
--- a/README.md
+++ b/README.md
@@ -16,6 +16,7 @@ This is full dropin replacement for [llthreads](https://github.com/Neopallium/lu
16* thread:join() method support zero timeout to check if thread alive 16* thread:join() method support zero timeout to check if thread alive
17* thread:join() method support arbitrary timeout on Windows platform 17* thread:join() method support arbitrary timeout on Windows platform
18* set_logger function allow logging errors (crash Lua VM) in current llthread's threads 18* set_logger function allow logging errors (crash Lua VM) in current llthread's threads
19* thread:start() has additional parameter which control in which thread child Lua VM will be destroyed
19 20
20##Usage 21##Usage
21 22
@@ -33,5 +34,45 @@ llthread.set_logger(function(msg) LOG.error(msg) end)
33error("SOME ERROR") 34error("SOME ERROR")
34``` 35```
35 36
37### Start atached thread collectd in child thread
38``` Lua
39-- This is main thread.
40local thread = require "llthreads".new[[
41 require "utils".sleep(5)
42]]
43
44-- We tell that we start atached thread
45-- but child Lua State shuld be close
46-- in child thread.
47-- So thread:join() can not return any values.
48-- If `thread` became garbage in main thread then
49-- finallizer calls thread:join() and main thread
50-- may hungup.
51thread:start(false, false)
52
53-- we can call join
54thread:join()
55```
56
57### Start detached thread collectd on which we can call join
58``` Lua
59-- This is main thread.
60local thread = require "llthreads".new[[
61 require "utils".sleep(5)
62]]
63
64-- We tell that we start detached thread
65-- but with ability call thread:join()
66-- and gets lua return values from child thread.
67-- In fact we start atached thread but if `thread`
68-- became garbage in main thread then finallizer
69-- just detach child thread and main thread
70-- may not hungup.
71thread:start(true, true)
72
73-- we can call join
74thread:join()
75```
76
36[![Bitdeli Badge](https://d2weczhvl823v0.cloudfront.net/moteus/lua-llthreads2/trend.png)](https://bitdeli.com/free "Bitdeli Badge") 77[![Bitdeli Badge](https://d2weczhvl823v0.cloudfront.net/moteus/lua-llthreads2/trend.png)](https://bitdeli.com/free "Bitdeli Badge")
37 78
diff --git a/src/llthread.c b/src/llthread.c
index 0596b2a..b61bf11 100644
--- a/src/llthread.c
+++ b/src/llthread.c
@@ -289,6 +289,7 @@ static int fail(lua_State *L, const char *msg){
289#define TSTATE_STARTED (flags_t)1<<0 289#define TSTATE_STARTED (flags_t)1<<0
290#define TSTATE_DETACHED (flags_t)1<<1 290#define TSTATE_DETACHED (flags_t)1<<1
291#define TSTATE_JOINED (flags_t)1<<2 291#define TSTATE_JOINED (flags_t)1<<2
292#define FLAG_JOIN_LUA (flags_t)1<<3
292 293
293/*At leas one flag*/ 294/*At leas one flag*/
294#define FLAG_IS_SET(O, F) (O->flags & (flags_t)(F)) 295#define FLAG_IS_SET(O, F) (O->flags & (flags_t)(F))
@@ -397,7 +398,7 @@ static OS_THREAD_RETURT llthread_child_thread_run(void *arg) {
397 } 398 }
398 399
399 /* if thread is detached, then destroy the child state. */ 400 /* if thread is detached, then destroy the child state. */
400 if(FLAG_IS_SET(this, TSTATE_DETACHED)) { 401 if(!FLAG_IS_SET(this, FLAG_JOIN_LUA)) {
401 /* thread is detached, so it must clean-up the child state. */ 402 /* thread is detached, so it must clean-up the child state. */
402 llthread_child_destroy(this); 403 llthread_child_destroy(this);
403 this = NULL; 404 this = NULL;
@@ -450,7 +451,8 @@ static void llthread_destroy(llthread_t *this) {
450 * we have joined the thread. 451 * we have joined the thread.
451 */ 452 */
452 if(FLAG_IS_SET(this, TSTATE_JOINED)||(this->flags == TSTATE_NONE)) { 453 if(FLAG_IS_SET(this, TSTATE_JOINED)||(this->flags == TSTATE_NONE)) {
453 llthread_cleanup_child(this); 454 if(FLAG_IS_SET(this, FLAG_JOIN_LUA))
455 llthread_cleanup_child(this);
454 } 456 }
455 FREE_STRUCT(this); 457 FREE_STRUCT(this);
456} 458}
@@ -463,12 +465,33 @@ static int llthread_push_results(lua_State *L, llthread_child_t *child, int idx,
463 return llthread_copy_values(child->L, L, idx, top, 0 /* is_arg */); 465 return llthread_copy_values(child->L, L, idx, top, 0 /* is_arg */);
464} 466}
465 467
466static int llthread_start(llthread_t *this, int start_detached) { 468static int llthread_detach(llthread_t *this){
469 int rc = 0;
470 this->child = NULL;
471 FLAG_SET(this, TSTATE_DETACHED);
472 FLAG_UNSET(this, FLAG_JOIN_LUA);
473#ifdef USE_PTHREAD
474 rc = pthread_detach(this->thread);
475#endif
476 return rc;
477}
478
479/* | detached | join_lua || return values | which thread | gc calls | detach on |
480 | | || thread:join() | closes lua state | | |
481* -------------------------------------------------------------------------------------
482* | false | falas || <NONE> | child | <NONE> | <NEVER> |
483* *| false | true || Lua values | parent | join | <NEVER> |
484* *| true | false || <ERROR> | child | <NONE> | start |
485* | true | true || <NONE> | parent | detach | gc |
486* -------------------------------------------------------------------------------------
487* * llthread behavior.
488*/
489static int llthread_start(llthread_t *this, int start_detached, int join_lua) {
467 llthread_child_t *child = this->child; 490 llthread_child_t *child = this->child;
468 int rc = 0; 491 int rc = 0;
469 492
470 if(start_detached){ 493 if(join_lua){ /*child does not close lua_State*/
471 FLAG_SET(child, TSTATE_DETACHED); 494 FLAG_SET(child, FLAG_JOIN_LUA);
472 } 495 }
473 496
474#ifndef USE_PTHREAD 497#ifndef USE_PTHREAD
@@ -482,12 +505,10 @@ static int llthread_start(llthread_t *this, int start_detached) {
482 505
483 if(rc == 0) { 506 if(rc == 0) {
484 FLAG_SET(this, TSTATE_STARTED); 507 FLAG_SET(this, TSTATE_STARTED);
485 if(start_detached) { 508 if(join_lua) FLAG_SET(this, FLAG_JOIN_LUA);
486 FLAG_SET(this, TSTATE_DETACHED); 509 if(start_detached) FLAG_SET(this, TSTATE_DETACHED);
487 this->child = NULL; 510 if((start_detached)&&(!join_lua)){
488#ifdef USE_PTHREAD 511 rc = llthread_detach(this);
489 rc = pthread_detach(this->thread);
490#endif
491 } 512 }
492 } 513 }
493 514
@@ -523,7 +544,7 @@ static int llthread_join(llthread_t *this, join_timeout_t timeout) {
523 return rc; 544 return rc;
524 } 545 }
525 546
526 // @todo use pthread_tryjoin_np to support timeout 547 /* @todo use pthread_tryjoin_np to support timeout */
527 548
528 /* then join the thread. */ 549 /* then join the thread. */
529 rc = pthread_join(this->thread, NULL); 550 rc = pthread_join(this->thread, NULL);
@@ -582,17 +603,25 @@ static int l_llthread_delete(lua_State *L) {
582 /*already exists*/ 603 /*already exists*/
583 if(this == NULL) return 0; 604 if(this == NULL) return 0;
584 605
585 /* if the thread has been started and has not been detached/joined. */ 606 do{/* join the thread. */
586 if( FLAG_IS_SET(this, TSTATE_STARTED) && 607 if(!FLAG_IS_SET(this, TSTATE_STARTED)) break;
587 !FLAG_IS_SET(this, (TSTATE_DETACHED|TSTATE_JOINED)) 608 if( FLAG_IS_SET(this, TSTATE_JOINED)) break;
588 ){ 609
589 /* then join the thread. */ 610 if(FLAG_IS_SET(this, TSTATE_DETACHED)){
590 llthread_child_t *child = this->child; 611 if(FLAG_IS_SET(this, FLAG_JOIN_LUA)){
591 llthread_join(this, INFINITE_JOIN_TIMEOUT); 612 llthread_detach(this);
592 if(child && child->status != 0) { 613 }
593 llthread_log(child->L, "Error from non-joined thread: ", lua_tostring(child->L, -1)); 614 break;
594 } 615 }
595 } 616
617 { /* @todo log warning about lost thread for debug? */
618 llthread_child_t *child = this->child;
619 llthread_join(this, INFINITE_JOIN_TIMEOUT);
620 if(child && child->status != 0) {
621 llthread_log(child->L, "Error from non-joined thread: ", lua_tostring(child->L, -1));
622 }
623 }
624 }while(0);
596 625
597 llthread_destroy(this); 626 llthread_destroy(this);
598 *pthis = NULL; 627 *pthis = NULL;
@@ -603,13 +632,16 @@ static int l_llthread_delete(lua_State *L) {
603static int l_llthread_start(lua_State *L) { 632static int l_llthread_start(lua_State *L) {
604 llthread_t *this = l_llthread_at(L, 1); 633 llthread_t *this = l_llthread_at(L, 1);
605 int start_detached = lua_toboolean(L, 2); 634 int start_detached = lua_toboolean(L, 2);
606 int rc; 635 int join_lua, rc;
636
637 if(!lua_isnone(L, 3)) join_lua = lua_toboolean(L, 3);
638 else join_lua = start_detached ? 0 : 1;
607 639
608 if(this->flags != TSTATE_NONE) { 640 if(this->flags != TSTATE_NONE) {
609 return fail(L, "Thread already started."); 641 return fail(L, "Thread already started.");
610 } 642 }
611 643
612 rc = llthread_start(this, start_detached); 644 rc = llthread_start(this, start_detached, join_lua);
613 if(rc != 0) { 645 if(rc != 0) {
614 char buf[ERROR_LEN]; 646 char buf[ERROR_LEN];
615 strerror_r(errno, buf, ERROR_LEN); 647 strerror_r(errno, buf, ERROR_LEN);
@@ -628,7 +660,7 @@ static int l_llthread_join(lua_State *L) {
628 if(!FLAG_IS_SET(this, TSTATE_STARTED )) { 660 if(!FLAG_IS_SET(this, TSTATE_STARTED )) {
629 return fail(L, "Can't join a thread that hasn't be started."); 661 return fail(L, "Can't join a thread that hasn't be started.");
630 } 662 }
631 if( FLAG_IS_SET(this, TSTATE_DETACHED)) { 663 if( FLAG_IS_SET(this, TSTATE_DETACHED) && !FLAG_IS_SET(this, FLAG_JOIN_LUA)) {
632 return fail(L, "Can't join a thread that has been detached."); 664 return fail(L, "Can't join a thread that has been detached.");
633 } 665 }
634 if( FLAG_IS_SET(this, TSTATE_JOINED )) { 666 if( FLAG_IS_SET(this, TSTATE_JOINED )) {
@@ -638,9 +670,17 @@ static int l_llthread_join(lua_State *L) {
638 /* join the thread. */ 670 /* join the thread. */
639 rc = llthread_join(this, luaL_optint(L, 2, INFINITE_JOIN_TIMEOUT)); 671 rc = llthread_join(this, luaL_optint(L, 2, INFINITE_JOIN_TIMEOUT));
640 672
641 /* Push all results after the Lua code. */
642 if(child && FLAG_IS_SET(this, TSTATE_JOINED)) { 673 if(child && FLAG_IS_SET(this, TSTATE_JOINED)) {
643 int top; 674 int top;
675
676 if(!FLAG_IS_SET(this, FLAG_JOIN_LUA)){
677 /*child lua state has been destroyed by child thread*/
678 /*@todo return thread exit code*/
679 lua_pushnumber(L, 0);
680 return 1;
681 }
682
683 /* copy values from child lua state */
644 if(child->status != 0) { 684 if(child->status != 0) {
645 const char *err_msg = lua_tostring(child->L, -1); 685 const char *err_msg = lua_tostring(child->L, -1);
646 lua_pushboolean(L, 0); 686 lua_pushboolean(L, 0);
@@ -652,12 +692,13 @@ static int l_llthread_join(lua_State *L) {
652 /* return results to parent thread. */ 692 /* return results to parent thread. */
653 llthread_push_results(L, child, 2, top); 693 llthread_push_results(L, child, 2, top);
654 } 694 }
695
655 llthread_cleanup_child(this); 696 llthread_cleanup_child(this);
656 return top; 697 return top;
657 } 698 }
658 699
659 if( rc == JOIN_ETIMEDOUT ){ 700 if( rc == JOIN_ETIMEDOUT ){
660 lua_pushboolean(L, 0); 701 lua_pushnil(L);
661 lua_pushstring(L, "timeout"); 702 lua_pushstring(L, "timeout");
662 return 2; 703 return 2;
663 } 704 }
diff --git a/test/test_join_detach.lua b/test/test_join_detach.lua
new file mode 100644
index 0000000..dd3ef11
--- /dev/null
+++ b/test/test_join_detach.lua
@@ -0,0 +1,26 @@
1local llthreads = require"llthreads"
2local utils = require "utils"
3
4do
5
6local thread = llthreads.new(utils.thread_init .. [[
7 local sleep = require"utils".sleep
8 while true do sleep(1) end
9]])
10
11-- detached + joindable
12thread:start(true, true)
13
14local ok, err = thread:join(0)
15print("thread:join(0): ", ok, err)
16assert(ok == nil)
17assert(err == "timeout")
18
19end
20
21-- enforce collect `thread` object
22-- we should not hungup
23for i = 1, 10 do collectgarbage("collect") end
24
25print("Done!")
26
diff --git a/test/test_join_timeout.lua b/test/test_join_timeout.lua
index 085497f..aa8f88d 100644
--- a/test/test_join_timeout.lua
+++ b/test/test_join_timeout.lua
@@ -14,7 +14,7 @@ local thread = llthreads.new(include .. [[
14thread:start() 14thread:start()
15local ok, err = thread:join(0) 15local ok, err = thread:join(0)
16print("thread:join(0): ", ok, err) 16print("thread:join(0): ", ok, err)
17assert(ok == false) 17assert(ok == nil)
18assert(err == "timeout") 18assert(err == "timeout")
19 19
20print("thread:join(): ", thread:join()) 20print("thread:join(): ", thread:join())