aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authormoteus <mimir@newmail.ru>2013-12-30 18:36:05 +0400
committermoteus <mimir@newmail.ru>2013-12-30 18:36:05 +0400
commitcff52e9ad84dea2ded2ee66bb0aef3e92a0c9e88 (patch)
treef5cd19984318e70fe437f99c9903677a8f3253f6 /src
parent18464f368d90b0f8ce48cb5d45def6f9d87e3432 (diff)
downloadlua-llthreads2-cff52e9ad84dea2ded2ee66bb0aef3e92a0c9e88.tar.gz
lua-llthreads2-cff52e9ad84dea2ded2ee66bb0aef3e92a0c9e88.tar.bz2
lua-llthreads2-cff52e9ad84dea2ded2ee66bb0aef3e92a0c9e88.zip
Fix. call `join` on gc for attached not joinable thread.
Code refactoring.
Diffstat (limited to 'src')
-rw-r--r--src/copy.inc170
-rw-r--r--src/llthread.c338
-rw-r--r--src/traceback.inc56
3 files changed, 298 insertions, 266 deletions
diff --git a/src/copy.inc b/src/copy.inc
new file mode 100644
index 0000000..21ba261
--- /dev/null
+++ b/src/copy.inc
@@ -0,0 +1,170 @@
1/******************************************************************************
2* Copyright (c) 2011 by Robert G. Jakabosky <bobby@sharedrealm.com>
3*
4* Permission is hereby granted, free of charge, to any person obtaining a copy
5* of this software and associated documentation files (the "Software"), to deal
6* in the Software without restriction, including without limitation the rights
7* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8* copies of the Software, and to permit persons to whom the Software is
9* furnished to do so, subject to the following conditions:
10*
11* The above copyright notice and this permission notice shall be included in
12* all copies or substantial portions of the Software.
13*
14* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
20* THE SOFTWARE.
21******************************************************************************/
22
23/* maximum recursive depth of table copies. */
24#define MAX_COPY_DEPTH 30
25
26typedef struct {
27 lua_State *from_L;
28 lua_State *to_L;
29 int has_cache;
30 int cache_idx;
31 int is_arg;
32} llthread_copy_state;
33
34static int llthread_copy_table_from_cache(llthread_copy_state *state, int idx) {
35 void *ptr;
36
37 /* convert table to pointer for lookup in cache. */
38 ptr = (void *)lua_topointer(state->from_L, idx);
39 if(ptr == NULL) return 0; /* can't convert to pointer. */
40
41 /* check if we need to create the cache. */
42 if(!state->has_cache) {
43 lua_newtable(state->to_L);
44 lua_replace(state->to_L, state->cache_idx);
45 state->has_cache = 1;
46 }
47
48 lua_pushlightuserdata(state->to_L, ptr);
49 lua_rawget(state->to_L, state->cache_idx);
50 if(lua_isnil(state->to_L, -1)) {
51 /* not in cache. */
52 lua_pop(state->to_L, 1);
53 /* create new table and add to cache. */
54 lua_newtable(state->to_L);
55 lua_pushlightuserdata(state->to_L, ptr);
56 lua_pushvalue(state->to_L, -2);
57 lua_rawset(state->to_L, state->cache_idx);
58 return 0;
59 }
60 /* found table in cache. */
61 return 1;
62}
63
64static int llthread_copy_value(llthread_copy_state *state, int depth, int idx) {
65 const char *str;
66 size_t str_len;
67 int kv_pos;
68
69 /* Maximum recursive depth */
70 if(++depth > MAX_COPY_DEPTH) {
71 return luaL_error(state->from_L, "Hit maximum copy depth (%d > %d).", depth, MAX_COPY_DEPTH);
72 }
73
74 /* only support string/number/boolean/nil/table/lightuserdata. */
75 switch(lua_type(state->from_L, idx)) {
76 case LUA_TNIL:
77 lua_pushnil(state->to_L);
78 break;
79 case LUA_TNUMBER:
80 lua_pushnumber(state->to_L, lua_tonumber(state->from_L, idx));
81 break;
82 case LUA_TBOOLEAN:
83 lua_pushboolean(state->to_L, lua_toboolean(state->from_L, idx));
84 break;
85 case LUA_TSTRING:
86 str = lua_tolstring(state->from_L, idx, &(str_len));
87 lua_pushlstring(state->to_L, str, str_len);
88 break;
89 case LUA_TLIGHTUSERDATA:
90 lua_pushlightuserdata(state->to_L, lua_touserdata(state->from_L, idx));
91 break;
92 case LUA_TTABLE:
93 /* make sure there is room on the new state for 3 values (table,key,value) */
94 if(!lua_checkstack(state->to_L, 3)) {
95 return luaL_error(state->from_L, "To stack overflow!");
96 }
97 /* make room on from stack for key/value pairs. */
98 luaL_checkstack(state->from_L, 2, "From stack overflow!");
99
100 /* check cache for table. */
101 if(llthread_copy_table_from_cache(state, idx)) {
102 /* found in cache don't need to copy table. */
103 break;
104 }
105 lua_pushnil(state->from_L);
106 while (lua_next(state->from_L, idx) != 0) {
107 /* key is at (top - 1), value at (top), but we need to normalize these
108 * to positive indices */
109 kv_pos = lua_gettop(state->from_L);
110 /* copy key */
111 llthread_copy_value(state, depth, kv_pos - 1);
112 /* copy value */
113 llthread_copy_value(state, depth, kv_pos);
114 /* Copied key and value are now at -2 and -1 in state->to_L. */
115 lua_settable(state->to_L, -3);
116 /* Pop value for next iteration */
117 lua_pop(state->from_L, 1);
118 }
119 break;
120 case LUA_TFUNCTION:
121 if(lua_iscfunction(state->from_L, idx)){
122 lua_CFunction fn = lua_tocfunction(state->from_L, idx);
123 lua_pushcfunction(state->to_L, fn);
124 break;
125 }
126 case LUA_TUSERDATA:
127 case LUA_TTHREAD:
128 default:
129 if (state->is_arg) {
130 return luaL_argerror(state->from_L, idx, "function/userdata/thread types un-supported.");
131 } else {
132 /* convert un-supported types to an error string. */
133 lua_pushfstring(state->to_L, "Un-supported value: %s: %p",
134 lua_typename(state->from_L, lua_type(state->from_L, idx)), lua_topointer(state->from_L, idx));
135 }
136 }
137
138 return 1;
139}
140
141static int llthread_copy_values(lua_State *from_L, lua_State *to_L, int idx, int top, int is_arg) {
142 llthread_copy_state state;
143 int nvalues = 0;
144 int n;
145
146 nvalues = (top - idx) + 1;
147 /* make sure there is room on the new state for the values. */
148 if(!lua_checkstack(to_L, nvalues + 1)) {
149 return luaL_error(from_L, "To stack overflow!");
150 }
151
152 /* setup copy state. */
153 state.from_L = from_L;
154 state.to_L = to_L;
155 state.is_arg = is_arg;
156 state.has_cache = 0; /* don't create cache table unless it is needed. */
157 lua_pushnil(to_L);
158 state.cache_idx = lua_gettop(to_L);
159
160 nvalues = 0;
161 for(n = idx; n <= top; n++) {
162 llthread_copy_value(&state, 0, n);
163 ++nvalues;
164 }
165
166 /* remove cache table. */
167 lua_remove(to_L, state.cache_idx);
168
169 return nvalues;
170}
diff --git a/src/llthread.c b/src/llthread.c
index f7a9b59..4f20840 100644
--- a/src/llthread.c
+++ b/src/llthread.c
@@ -58,7 +58,6 @@ typedef int join_timeout_t;
58typedef pthread_t os_thread_t; 58typedef pthread_t os_thread_t;
59#endif 59#endif
60 60
61
62LLTHREADS_EXPORT_API int luaopen_llthreads(lua_State *L); 61LLTHREADS_EXPORT_API int luaopen_llthreads(lua_State *L);
63 62
64#define LLTHREAD_T_NAME "LLThread" 63#define LLTHREAD_T_NAME "LLThread"
@@ -67,217 +66,13 @@ static const char *LLTHREAD_LOGGER_HOLDER = LLTHREAD_T_NAME " logger holder";
67 66
68//{ traceback 67//{ traceback
69 68
70#define ERROR_LEN 1024 69#include "traceback.inc"
71
72/******************************************************************************
73* traceback() function from Lua 5.1/5.2 source.
74* Copyright (C) 1994-2008 Lua.org, PUC-Rio. All rights reserved.
75*
76* Permission is hereby granted, free of charge, to any person obtaining
77* a copy of this software and associated documentation files (the
78* "Software"), to deal in the Software without restriction, including
79* without limitation the rights to use, copy, modify, merge, publish,
80* distribute, sublicense, and/or sell copies of the Software, and to
81* permit persons to whom the Software is furnished to do so, subject to
82* the following conditions:
83*
84* The above copyright notice and this permission notice shall be
85* included in all copies or substantial portions of the Software.
86*
87* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
88* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
89* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
90* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
91* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
92* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
93* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
94******************************************************************************/
95#if !defined(LUA_VERSION_NUM) || (LUA_VERSION_NUM == 501)
96/* from Lua 5.1 */
97static int traceback (lua_State *L) {
98 if (!lua_isstring(L, 1)) /* 'message' not a string? */
99 return 1; /* keep it intact */
100 lua_getglobal(L, "debug");
101 if (!lua_istable(L, -1)) {
102 lua_pop(L, 1);
103 return 1;
104 }
105 lua_getfield(L, -1, "traceback");
106 if (!lua_isfunction(L, -1)) {
107 lua_pop(L, 2);
108 return 1;
109 }
110 lua_pushvalue(L, 1); /* pass error message */
111 lua_pushinteger(L, 2); /* skip this function and traceback */
112 lua_call(L, 2, 1); /* call debug.traceback */
113 return 1;
114}
115#else
116/* from Lua 5.2 */
117static int traceback (lua_State *L) {
118 const char *msg = lua_tostring(L, 1);
119 if (msg)
120 luaL_traceback(L, L, msg, 1);
121 else if (!lua_isnoneornil(L, 1)) { /* is there an error object? */
122 if (!luaL_callmeta(L, 1, "__tostring")) /* try its 'tostring' metamethod */
123 lua_pushliteral(L, "(no error message)");
124 }
125 return 1;
126}
127#endif
128 70
129//} 71//}
130 72
131//{ copy values 73//{ copy values
132 74
133/* maximum recursive depth of table copies. */ 75#include "copy.inc"
134#define MAX_COPY_DEPTH 30
135
136typedef struct {
137 lua_State *from_L;
138 lua_State *to_L;
139 int has_cache;
140 int cache_idx;
141 int is_arg;
142} llthread_copy_state;
143
144static int llthread_copy_table_from_cache(llthread_copy_state *state, int idx) {
145 void *ptr;
146
147 /* convert table to pointer for lookup in cache. */
148 ptr = (void *)lua_topointer(state->from_L, idx);
149 if(ptr == NULL) return 0; /* can't convert to pointer. */
150
151 /* check if we need to create the cache. */
152 if(!state->has_cache) {
153 lua_newtable(state->to_L);
154 lua_replace(state->to_L, state->cache_idx);
155 state->has_cache = 1;
156 }
157
158 lua_pushlightuserdata(state->to_L, ptr);
159 lua_rawget(state->to_L, state->cache_idx);
160 if(lua_isnil(state->to_L, -1)) {
161 /* not in cache. */
162 lua_pop(state->to_L, 1);
163 /* create new table and add to cache. */
164 lua_newtable(state->to_L);
165 lua_pushlightuserdata(state->to_L, ptr);
166 lua_pushvalue(state->to_L, -2);
167 lua_rawset(state->to_L, state->cache_idx);
168 return 0;
169 }
170 /* found table in cache. */
171 return 1;
172}
173
174static int llthread_copy_value(llthread_copy_state *state, int depth, int idx) {
175 const char *str;
176 size_t str_len;
177 int kv_pos;
178
179 /* Maximum recursive depth */
180 if(++depth > MAX_COPY_DEPTH) {
181 return luaL_error(state->from_L, "Hit maximum copy depth (%d > %d).", depth, MAX_COPY_DEPTH);
182 }
183
184 /* only support string/number/boolean/nil/table/lightuserdata. */
185 switch(lua_type(state->from_L, idx)) {
186 case LUA_TNIL:
187 lua_pushnil(state->to_L);
188 break;
189 case LUA_TNUMBER:
190 lua_pushnumber(state->to_L, lua_tonumber(state->from_L, idx));
191 break;
192 case LUA_TBOOLEAN:
193 lua_pushboolean(state->to_L, lua_toboolean(state->from_L, idx));
194 break;
195 case LUA_TSTRING:
196 str = lua_tolstring(state->from_L, idx, &(str_len));
197 lua_pushlstring(state->to_L, str, str_len);
198 break;
199 case LUA_TLIGHTUSERDATA:
200 lua_pushlightuserdata(state->to_L, lua_touserdata(state->from_L, idx));
201 break;
202 case LUA_TTABLE:
203 /* make sure there is room on the new state for 3 values (table,key,value) */
204 if(!lua_checkstack(state->to_L, 3)) {
205 return luaL_error(state->from_L, "To stack overflow!");
206 }
207 /* make room on from stack for key/value pairs. */
208 luaL_checkstack(state->from_L, 2, "From stack overflow!");
209
210 /* check cache for table. */
211 if(llthread_copy_table_from_cache(state, idx)) {
212 /* found in cache don't need to copy table. */
213 break;
214 }
215 lua_pushnil(state->from_L);
216 while (lua_next(state->from_L, idx) != 0) {
217 /* key is at (top - 1), value at (top), but we need to normalize these
218 * to positive indices */
219 kv_pos = lua_gettop(state->from_L);
220 /* copy key */
221 llthread_copy_value(state, depth, kv_pos - 1);
222 /* copy value */
223 llthread_copy_value(state, depth, kv_pos);
224 /* Copied key and value are now at -2 and -1 in state->to_L. */
225 lua_settable(state->to_L, -3);
226 /* Pop value for next iteration */
227 lua_pop(state->from_L, 1);
228 }
229 break;
230 case LUA_TFUNCTION:
231 if(lua_iscfunction(state->from_L, idx)){
232 lua_CFunction fn = lua_tocfunction(state->from_L, idx);
233 lua_pushcfunction(state->to_L, fn);
234 break;
235 }
236 case LUA_TUSERDATA:
237 case LUA_TTHREAD:
238 default:
239 if (state->is_arg) {
240 return luaL_argerror(state->from_L, idx, "function/userdata/thread types un-supported.");
241 } else {
242 /* convert un-supported types to an error string. */
243 lua_pushfstring(state->to_L, "Un-supported value: %s: %p",
244 lua_typename(state->from_L, lua_type(state->from_L, idx)), lua_topointer(state->from_L, idx));
245 }
246 }
247
248 return 1;
249}
250
251static int llthread_copy_values(lua_State *from_L, lua_State *to_L, int idx, int top, int is_arg) {
252 llthread_copy_state state;
253 int nvalues = 0;
254 int n;
255
256 nvalues = (top - idx) + 1;
257 /* make sure there is room on the new state for the values. */
258 if(!lua_checkstack(to_L, nvalues + 1)) {
259 return luaL_error(from_L, "To stack overflow!");
260 }
261
262 /* setup copy state. */
263 state.from_L = from_L;
264 state.to_L = to_L;
265 state.is_arg = is_arg;
266 state.has_cache = 0; /* don't create cache table unless it is needed. */
267 lua_pushnil(to_L);
268 state.cache_idx = lua_gettop(to_L);
269
270 nvalues = 0;
271 for(n = idx; n <= top; n++) {
272 llthread_copy_value(&state, 0, n);
273 ++nvalues;
274 }
275
276 /* remove cache table. */
277 lua_remove(to_L, state.cache_idx);
278
279 return nvalues;
280}
281 76
282//} 77//}
283 78
@@ -287,20 +82,22 @@ static int fail(lua_State *L, const char *msg){
287 return 2; 82 return 2;
288} 83}
289 84
85#define ERROR_LEN 1024
86
290#define flags_t unsigned char 87#define flags_t unsigned char
291 88
292#define TSTATE_NONE (flags_t)0 89#define FLAG_NONE (flags_t)0
293#define TSTATE_STARTED (flags_t)1<<0 90#define FLAG_STARTED (flags_t)1<<0
294#define TSTATE_DETACHED (flags_t)1<<1 91#define FLAG_DETACHED (flags_t)1<<1
295#define TSTATE_JOINED (flags_t)1<<2 92#define FLAG_JOINED (flags_t)1<<2
296#define FLAG_JOIN_LUA (flags_t)1<<3 93#define FLAG_JOINABLE (flags_t)1<<3
297 94
298/*At least one flag*/ 95/*At least one flag*/
299#define FLAG_IS_SET(O, F) (O->flags & (flags_t)(F)) 96#define FLAG_IS_SET(O, F) (O->flags & (flags_t)(F))
300/*All flags*/
301#define FLAGS_IS_SET(O, F) ((F) == FLAG_IS_SET(O, F))
302#define FLAG_SET(O, F) O->flags |= (flags_t)(F) 97#define FLAG_SET(O, F) O->flags |= (flags_t)(F)
303#define FLAG_UNSET(O, F) O->flags &= ~((flags_t)(F)) 98#define FLAG_UNSET(O, F) O->flags &= ~((flags_t)(F))
99#define IS(O, F) FLAG_IS_SET(O, FLAG_##F)
100#define SET(O, F) FLAG_SET(O, FLAG_##F)
304 101
305#define ALLOC_STRUCT(S) (S*)calloc(1, sizeof(S)) 102#define ALLOC_STRUCT(S) (S*)calloc(1, sizeof(S))
306#define FREE_STRUCT(O) free(O) 103#define FREE_STRUCT(O) free(O)
@@ -422,7 +219,7 @@ static OS_THREAD_RETURN llthread_child_thread_run(void *arg) {
422 llthread_log(L, "Error from thread: ", lua_tostring(L, -1)); 219 llthread_log(L, "Error from thread: ", lua_tostring(L, -1));
423 } 220 }
424 221
425 if(FLAG_IS_SET(this, TSTATE_DETACHED) || !FLAG_IS_SET(this, FLAG_JOIN_LUA)) { 222 if(IS(this, DETACHED) || !IS(this, JOINABLE)) {
426 /* thread is detached, so it must clean-up the child state. */ 223 /* thread is detached, so it must clean-up the child state. */
427 llthread_child_destroy(this); 224 llthread_child_destroy(this);
428 this = NULL; 225 this = NULL;
@@ -450,15 +247,15 @@ static void llthread_validate(llthread_t *this){
450 /* describe valid state of llthread_t object 247 /* describe valid state of llthread_t object
451 * from after create and before destroy 248 * from after create and before destroy
452 */ 249 */
453 if(!FLAGS_IS_SET(this, TSTATE_STARTED)){ 250 if(!IS(this, STARTED)){
454 assert(!FLAGS_IS_SET(this, TSTATE_DETACHED)); 251 assert(!IS(this, DETACHED));
455 assert(!FLAGS_IS_SET(this, TSTATE_JOINED)); 252 assert(!IS(this, JOINED));
456 assert(!FLAGS_IS_SET(this, FLAG_JOIN_LUA)); 253 assert(!IS(this, JOINABLE));
457 return; 254 return;
458 } 255 }
459 256
460 if(FLAGS_IS_SET(this, TSTATE_DETACHED)){ 257 if(IS(this, DETACHED)){
461 if(!FLAGS_IS_SET(this, FLAG_JOIN_LUA)) assert(this->child == NULL); 258 if(!IS(this, JOINABLE)) assert(this->child == NULL);
462 else assert(this->child != NULL); 259 else assert(this->child != NULL);
463 } 260 }
464} 261}
@@ -471,7 +268,7 @@ static llthread_t *llthread_new() {
471 llthread_t *this = ALLOC_STRUCT(llthread_t); 268 llthread_t *this = ALLOC_STRUCT(llthread_t);
472 if(!this) return NULL; 269 if(!this) return NULL;
473 270
474 this->flags = TSTATE_NONE; 271 this->flags = FLAG_NONE;
475#ifndef USE_PTHREAD 272#ifndef USE_PTHREAD
476 this->thread = INVALID_THREAD; 273 this->thread = INVALID_THREAD;
477#endif 274#endif
@@ -493,28 +290,36 @@ static void llthread_cleanup_child(llthread_t *this) {
493 290
494static void llthread_destroy(llthread_t *this) { 291static void llthread_destroy(llthread_t *this) {
495 do{ 292 do{
496 if(!FLAG_IS_SET(this, TSTATE_STARTED)){ 293 /* thread not started */
294 if(!IS(this, STARTED)){
497 llthread_cleanup_child(this); 295 llthread_cleanup_child(this);
498 break; 296 break;
499 } 297 }
500 if(!FLAG_IS_SET(this, FLAG_JOIN_LUA)) break; 298
501 if( FLAG_IS_SET(this, TSTATE_DETACHED)){ 299 /* DETACHED */
502 llthread_detach(this); 300 if(IS(this, DETACHED)){
301 if(IS(this, JOINABLE)){
302 llthread_detach(this);
303 }
503 break; 304 break;
504 } 305 }
505 306
506 if(!FLAG_IS_SET(this, TSTATE_JOINED)){ 307 /* ATACHED */
507 /* @todo log warning about lost thread object for debug? */ 308 if(!IS(this, JOINED)){
508 llthread_child_t *child = this->child;
509 llthread_join(this, INFINITE_JOIN_TIMEOUT); 309 llthread_join(this, INFINITE_JOIN_TIMEOUT);
510 if(child && child->status != 0) { 310 if(!IS(this, JOINED)){
511 /*@fixme remove redundant log*/ 311 /* @todo use current lua state to logging */
512 llthread_log(child->L, "Error from non-joined thread: ", lua_tostring(child->L, -1)); 312 /*
313 * char buf[ERROR_LEN];
314 * strerror_r(errno, buf, ERROR_LEN);
315 * llthread_log(L, "Error can not join thread on gc: ", buf);
316 */
513 } 317 }
514 } 318 }
319 if(IS(this, JOINABLE)){
320 llthread_cleanup_child(this);
321 }
515 322
516 assert(FLAG_IS_SET(this, TSTATE_JOINED));
517 llthread_cleanup_child(this);
518 }while(0); 323 }while(0);
519 324
520 FREE_STRUCT(this); 325 FREE_STRUCT(this);
@@ -531,11 +336,11 @@ static int llthread_push_results(lua_State *L, llthread_child_t *child, int idx,
531static int llthread_detach(llthread_t *this){ 336static int llthread_detach(llthread_t *this){
532 int rc = 0; 337 int rc = 0;
533 338
534 assert(FLAGS_IS_SET(this, TSTATE_STARTED)); 339 assert(IS(this, STARTED));
535 assert(this->child != NULL); 340 assert(this->child != NULL);
536 341
537 /*we can not deatach joined thread*/ 342 /*we can not detach joined thread*/
538 if(FLAGS_IS_SET(this, TSTATE_JOINED)) 343 if(IS(this, JOINED))
539 return 0; 344 return 0;
540 345
541 this->child = NULL; 346 this->child = NULL;
@@ -549,24 +354,24 @@ static int llthread_detach(llthread_t *this){
549 return rc; 354 return rc;
550} 355}
551 356
552/* | detached | join_lua || return values | which thread | gc calls | detach on | 357/* | detached | joinable || join | which thread | gc | detach |
553 * | | || thread:join() | closes lua state | | | 358 * | | || return | destroy child | calls | on |
554 * ------------------------------------------------------------------------------------- 359 * ------------------------------------------------------------------------
555 * | false | falas || <NONE> | child | <NONE> | <NEVER> | 360 * | false | falas || <NONE> | child | join | <NEVER> |
556 * *| false | true || Lua values | parent | join | <NEVER> | 361 * *| false | true || Lua values | parent | join | <NEVER> |
557 * *| true | false || <ERROR> | child | <NONE> | start | 362 * *| true | false || <ERROR> | child | <NONE> | start |
558 * | true | true || <NONE> | child | detach | gc | 363 * | true | true || <NONE> | child | detach | gc |
559 * ------------------------------------------------------------------------------------- 364 * ------------------------------------------------------------------------
560 * * llthread behavior. 365 * * llthread behavior.
561 */ 366 */
562static int llthread_start(llthread_t *this, int start_detached, int join_lua) { 367static int llthread_start(llthread_t *this, int start_detached, int joinable) {
563 llthread_child_t *child = this->child; 368 llthread_child_t *child = this->child;
564 int rc = 0; 369 int rc = 0;
565 370
566 llthread_validate(this); 371 llthread_validate(this);
567 372
568 if(join_lua) FLAG_SET(child, FLAG_JOIN_LUA); 373 if(joinable) SET(child, JOINABLE);
569 if(start_detached) FLAG_SET(child, TSTATE_DETACHED); 374 if(start_detached) SET(child, DETACHED);
570 375
571#ifndef USE_PTHREAD 376#ifndef USE_PTHREAD
572 this->thread = (HANDLE)_beginthreadex(NULL, 0, llthread_child_thread_run, child, 0, NULL); 377 this->thread = (HANDLE)_beginthreadex(NULL, 0, llthread_child_thread_run, child, 0, NULL);
@@ -578,10 +383,10 @@ static int llthread_start(llthread_t *this, int start_detached, int join_lua) {
578#endif 383#endif
579 384
580 if(rc == 0) { 385 if(rc == 0) {
581 FLAG_SET(this, TSTATE_STARTED); 386 SET(this, STARTED);
582 if(join_lua) FLAG_SET(this, FLAG_JOIN_LUA); 387 if(joinable) SET(this, JOINABLE);
583 if(start_detached) FLAG_SET(this, TSTATE_DETACHED); 388 if(start_detached) SET(this, DETACHED);
584 if((start_detached)&&(!join_lua)){ 389 if((start_detached)&&(!joinable)){
585 rc = llthread_detach(this); 390 rc = llthread_detach(this);
586 } 391 }
587 } 392 }
@@ -594,7 +399,7 @@ static int llthread_start(llthread_t *this, int start_detached, int join_lua) {
594static int llthread_join(llthread_t *this, join_timeout_t timeout) { 399static int llthread_join(llthread_t *this, join_timeout_t timeout) {
595 llthread_validate(this); 400 llthread_validate(this);
596 401
597 if(FLAG_IS_SET(this, TSTATE_JOINED)){ 402 if(IS(this, JOINED)){
598 return JOIN_OK; 403 return JOIN_OK;
599 } else{ 404 } else{
600#ifndef USE_PTHREAD 405#ifndef USE_PTHREAD
@@ -604,7 +409,7 @@ static int llthread_join(llthread_t *this, join_timeout_t timeout) {
604 if( ret == WAIT_OBJECT_0){ /* Destroy the thread object. */ 409 if( ret == WAIT_OBJECT_0){ /* Destroy the thread object. */
605 CloseHandle( this->thread ); 410 CloseHandle( this->thread );
606 this->thread = INVALID_THREAD; 411 this->thread = INVALID_THREAD;
607 FLAG_SET(this, TSTATE_JOINED); 412 SET(this, JOINED);
608 413
609 llthread_validate(this); 414 llthread_validate(this);
610 415
@@ -635,7 +440,7 @@ static int llthread_join(llthread_t *this, join_timeout_t timeout) {
635 /* then join the thread. */ 440 /* then join the thread. */
636 rc = pthread_join(this->thread, NULL); 441 rc = pthread_join(this->thread, NULL);
637 if((rc == 0) || (rc == ESRCH)) { 442 if((rc == 0) || (rc == ESRCH)) {
638 FLAG_SET(this, TSTATE_JOINED); 443 SET(this, JOINED);
639 rc = JOIN_OK; 444 rc = JOIN_OK;
640 } 445 }
641 446
@@ -700,16 +505,16 @@ static int l_llthread_delete(lua_State *L) {
700static int l_llthread_start(lua_State *L) { 505static int l_llthread_start(lua_State *L) {
701 llthread_t *this = l_llthread_at(L, 1); 506 llthread_t *this = l_llthread_at(L, 1);
702 int start_detached = lua_toboolean(L, 2); 507 int start_detached = lua_toboolean(L, 2);
703 int join_lua, rc; 508 int joinable, rc;
704 509
705 if(!lua_isnone(L, 3)) join_lua = lua_toboolean(L, 3); 510 if(!lua_isnone(L, 3)) joinable = lua_toboolean(L, 3);
706 else join_lua = start_detached ? 0 : 1; 511 else joinable = start_detached ? 0 : 1;
707 512
708 if(FLAG_IS_SET(this, TSTATE_STARTED)) { 513 if(IS(this, STARTED)) {
709 return fail(L, "Thread already started."); 514 return fail(L, "Thread already started.");
710 } 515 }
711 516
712 rc = llthread_start(this, start_detached, join_lua); 517 rc = llthread_start(this, start_detached, joinable);
713 if(rc != 0) { 518 if(rc != 0) {
714 char buf[ERROR_LEN]; 519 char buf[ERROR_LEN];
715 strerror_r(errno, buf, ERROR_LEN); 520 strerror_r(errno, buf, ERROR_LEN);
@@ -725,27 +530,28 @@ static int l_llthread_join(lua_State *L) {
725 llthread_child_t *child = this->child; 530 llthread_child_t *child = this->child;
726 int rc; 531 int rc;
727 532
728 if(!FLAG_IS_SET(this, TSTATE_STARTED )) { 533 if(!IS(this, STARTED )) {
729 return fail(L, "Can't join a thread that hasn't be started."); 534 return fail(L, "Can't join a thread that hasn't be started.");
730 } 535 }
731 if( FLAG_IS_SET(this, TSTATE_DETACHED) && !FLAG_IS_SET(this, FLAG_JOIN_LUA)) { 536 if( IS(this, DETACHED) && !IS(this, JOINABLE)) {
732 return fail(L, "Can't join a thread that has been detached."); 537 return fail(L, "Can't join a thread that has been detached.");
733 } 538 }
734 if( FLAG_IS_SET(this, TSTATE_JOINED )) { 539 if( IS(this, JOINED )) {
735 return fail(L, "Can't join a thread that has already been joined."); 540 return fail(L, "Can't join a thread that has already been joined.");
736 } 541 }
737 542
738 /* join the thread. */ 543 /* join the thread. */
739 rc = llthread_join(this, luaL_optint(L, 2, INFINITE_JOIN_TIMEOUT)); 544 rc = llthread_join(this, luaL_optint(L, 2, INFINITE_JOIN_TIMEOUT));
740 545
741 if(child && FLAG_IS_SET(this, TSTATE_JOINED)) { 546 if(child && IS(this, JOINED)) {
742 int top; 547 int top;
743 548
744 if(FLAG_IS_SET(this, TSTATE_DETACHED) || !FLAG_IS_SET(this, FLAG_JOIN_LUA)){ 549 if(IS(this, DETACHED) || !IS(this, JOINABLE)){
745 /*child lua state has been destroyed by child thread*/ 550 /*child lua state has been destroyed by child thread*/
746 /*@todo return thread exit code*/ 551 /*@todo return thread exit code*/
552 lua_pushboolean(L, 1);
747 lua_pushnumber(L, 0); 553 lua_pushnumber(L, 0);
748 return 1; 554 return 2;
749 } 555 }
750 556
751 /* copy values from child lua state */ 557 /* copy values from child lua state */
diff --git a/src/traceback.inc b/src/traceback.inc
new file mode 100644
index 0000000..af2f5a1
--- /dev/null
+++ b/src/traceback.inc
@@ -0,0 +1,56 @@
1/******************************************************************************
2* traceback() function from Lua 5.1/5.2 source.
3* Copyright (C) 1994-2008 Lua.org, PUC-Rio. All rights reserved.
4*
5* Permission is hereby granted, free of charge, to any person obtaining
6* a copy of this software and associated documentation files (the
7* "Software"), to deal in the Software without restriction, including
8* without limitation the rights to use, copy, modify, merge, publish,
9* distribute, sublicense, and/or sell copies of the Software, and to
10* permit persons to whom the Software is furnished to do so, subject to
11* the following conditions:
12*
13* The above copyright notice and this permission notice shall be
14* included in all copies or substantial portions of the Software.
15*
16* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
19* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
20* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
21* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
22* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
23******************************************************************************/
24#if !defined(LUA_VERSION_NUM) || (LUA_VERSION_NUM == 501)
25/* from Lua 5.1 */
26static int traceback (lua_State *L) {
27 if (!lua_isstring(L, 1)) /* 'message' not a string? */
28 return 1; /* keep it intact */
29 lua_getglobal(L, "debug");
30 if (!lua_istable(L, -1)) {
31 lua_pop(L, 1);
32 return 1;
33 }
34 lua_getfield(L, -1, "traceback");
35 if (!lua_isfunction(L, -1)) {
36 lua_pop(L, 2);
37 return 1;
38 }
39 lua_pushvalue(L, 1); /* pass error message */
40 lua_pushinteger(L, 2); /* skip this function and traceback */
41 lua_call(L, 2, 1); /* call debug.traceback */
42 return 1;
43}
44#else
45/* from Lua 5.2 */
46static int traceback (lua_State *L) {
47 const char *msg = lua_tostring(L, 1);
48 if (msg)
49 luaL_traceback(L, L, msg, 1);
50 else if (!lua_isnoneornil(L, 1)) { /* is there an error object? */
51 if (!luaL_callmeta(L, 1, "__tostring")) /* try its 'tostring' metamethod */
52 lua_pushliteral(L, "(no error message)");
53 }
54 return 1;
55}
56#endif