summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authormoteus <mimir@newmail.ru>2013-12-26 12:00:41 +0400
committermoteus <mimir@newmail.ru>2013-12-26 12:00:41 +0400
commitfebf9da1af8ba4cf0f8cc64b6af2adb0dcf9b354 (patch)
tree735ef634f8ebeab2101aa21897fd4e4e2f826f32 /src
parent46ed59584e5407c49a02f1ea6bede6487259a92e (diff)
downloadlua-llthreads2-febf9da1af8ba4cf0f8cc64b6af2adb0dcf9b354.tar.gz
lua-llthreads2-febf9da1af8ba4cf0f8cc64b6af2adb0dcf9b354.tar.bz2
lua-llthreads2-febf9da1af8ba4cf0f8cc64b6af2adb0dcf9b354.zip
First commit.
Diffstat (limited to 'src')
-rw-r--r--src/l52util.c117
-rw-r--r--src/l52util.h46
-rw-r--r--src/llthread.c669
3 files changed, 832 insertions, 0 deletions
diff --git a/src/l52util.c b/src/l52util.c
new file mode 100644
index 0000000..592c1d1
--- /dev/null
+++ b/src/l52util.c
@@ -0,0 +1,117 @@
1#include "l52util.h"
2
3#include <memory.h>
4#include <assert.h>
5
6#if LUA_VERSION_NUM >= 502
7
8int luaL_typerror (lua_State *L, int narg, const char *tname) {
9 const char *msg = lua_pushfstring(L, "%s expected, got %s", tname,
10 luaL_typename(L, narg));
11 return luaL_argerror(L, narg, msg);
12}
13
14void luaL_register (lua_State *L, const char *libname, const luaL_Reg *l){
15 if(libname) lua_newtable(L);
16 luaL_setfuncs(L, l, 0);
17}
18
19#else
20
21void luaL_setfuncs (lua_State *L, const luaL_Reg *l, int nup){
22 luaL_checkstack(L, nup, "too many upvalues");
23 for (; l->name != NULL; l++) { /* fill the table with given functions */
24 int i;
25 for (i = 0; i < nup; i++) /* copy upvalues to the top */
26 lua_pushvalue(L, -nup);
27 lua_pushcclosure(L, l->func, nup); /* closure with those upvalues */
28 lua_setfield(L, -(nup + 2), l->name);
29 }
30 lua_pop(L, nup); /* remove upvalues */
31}
32
33void lua_rawgetp(lua_State *L, int index, const void *p){
34 index = lua_absindex(L, index);
35 lua_pushlightuserdata(L, (void *)p);
36 lua_rawget(L, index);
37}
38
39void lua_rawsetp (lua_State *L, int index, const void *p){
40 index = lua_absindex(L, index);
41 lua_pushlightuserdata(L, (void *)p);
42 lua_insert(L, -2);
43 lua_rawset(L, index);
44}
45
46#endif
47
48int lutil_newmetatablep (lua_State *L, const void *p) {
49 lua_rawgetp(L, LUA_REGISTRYINDEX, p);
50 if (!lua_isnil(L, -1)) /* name already in use? */
51 return 0; /* leave previous value on top, but return 0 */
52 lua_pop(L, 1);
53
54 lua_newtable(L); /* create metatable */
55 lua_pushvalue(L, -1); /* duplicate metatable to set*/
56 lua_rawsetp(L, LUA_REGISTRYINDEX, p);
57
58 return 1;
59}
60
61void lutil_getmetatablep (lua_State *L, const void *p) {
62 lua_rawgetp(L, LUA_REGISTRYINDEX, p);
63}
64
65void lutil_setmetatablep (lua_State *L, const void *p) {
66 lutil_getmetatablep(L, p);
67 assert(lua_istable(L,-1));
68 lua_setmetatable (L, -2);
69}
70
71int lutil_isudatap (lua_State *L, int ud, const void *p) {
72 if (lua_isuserdata(L, ud)){
73 if (lua_getmetatable(L, ud)) { /* does it have a metatable? */
74 int res;
75 lutil_getmetatablep(L,p); /* get correct metatable */
76 res = lua_rawequal(L, -1, -2); /* does it have the correct mt? */
77 lua_pop(L, 2); /* remove both metatables */
78 return res;
79 }
80 }
81 return 0;
82}
83
84void *lutil_checkudatap (lua_State *L, int ud, const void *p) {
85 void *up = lua_touserdata(L, ud);
86 if (up != NULL) { /* value is a userdata? */
87 if (lua_getmetatable(L, ud)) { /* does it have a metatable? */
88 lutil_getmetatablep(L,p); /* get correct metatable */
89 if (lua_rawequal(L, -1, -2)) { /* does it have the correct mt? */
90 lua_pop(L, 2); /* remove both metatables */
91 return up;
92 }
93 }
94 }
95 luaL_typerror(L, ud, p); /* else error */
96 return NULL; /* to avoid warnings */
97}
98
99int lutil_createmetap (lua_State *L, const void *p, const luaL_Reg *methods, int nup) {
100 if (!lutil_newmetatablep(L, p))
101 return 0;
102
103 lua_insert(L, -1 - nup); /* move mt prior upvalues */
104 luaL_setfuncs (L, methods, nup); /* define methods */
105 lua_pushliteral (L, "__index"); /* define metamethods */
106 lua_pushvalue (L, -2);
107 lua_settable (L, -3);
108
109 return 1;
110}
111
112void *lutil_newudatap_impl(lua_State *L, size_t size, const void *p){
113 void *obj = lua_newuserdata (L, size);
114 memset(obj, 0, size);
115 lutil_setmetatablep(L, p);
116 return obj;
117}
diff --git a/src/l52util.h b/src/l52util.h
new file mode 100644
index 0000000..f4f0497
--- /dev/null
+++ b/src/l52util.h
@@ -0,0 +1,46 @@
1#ifndef _LZUTILS_H_9B43D914_9652_4E22_9A43_8073502BF3F4_
2#define _LZUTILS_H_9B43D914_9652_4E22_9A43_8073502BF3F4_
3
4#include "lua.h"
5#include "lauxlib.h"
6
7#if LUA_VERSION_NUM >= 502 // lua 5.2
8
9// lua_rawgetp
10// lua_rawsetp
11// luaL_setfuncs
12// lua_absindex
13
14
15#define lua_objlen lua_rawlen
16
17int luaL_typerror (lua_State *L, int narg, const char *tname);
18
19void luaL_register (lua_State *L, const char *libname, const luaL_Reg *l);
20
21#else // lua 5.1
22
23// functions form lua 5.2
24
25# define lua_absindex(L, i) (((i)>0)?(i):((i)<=LUA_REGISTRYINDEX?(i):(lua_gettop(L)+(i)+1)))
26# define lua_rawlen lua_objlen
27
28void lua_rawgetp (lua_State *L, int index, const void *p);
29void lua_rawsetp (lua_State *L, int index, const void *p);
30void luaL_setfuncs (lua_State *L, const luaL_Reg *l, int nup);
31
32#endif
33
34int lutil_newmetatablep (lua_State *L, const void *p);
35void lutil_getmetatablep (lua_State *L, const void *p);
36void lutil_setmetatablep (lua_State *L, const void *p);
37
38#define lutil_newudatap(L, TTYPE, TNAME) (TTYPE *)lutil_newudatap_impl(L, sizeof(TTYPE), TNAME)
39int lutil_isudatap (lua_State *L, int ud, const void *p);
40void *lutil_checkudatap (lua_State *L, int ud, const void *p);
41int lutil_createmetap (lua_State *L, const void *p, const luaL_Reg *methods, int nup);
42
43void *lutil_newudatap_impl (lua_State *L, size_t size, const void *p);
44
45#endif
46
diff --git a/src/llthread.c b/src/llthread.c
new file mode 100644
index 0000000..a11ad18
--- /dev/null
+++ b/src/llthread.c
@@ -0,0 +1,669 @@
1#if !defined(_WIN32) && !defined(USE_PTHREAD)
2# define USE_PTHREAD
3#endif
4
5#ifndef USE_PTHREAD
6# include <windows.h>
7# include <stdio.h>
8# include <process.h>
9#else
10# include <pthread.h>
11# include <stdio.h>
12#endif
13
14/*export*/
15#ifdef _WIN32
16# define LLTHREADS_EXPORT_API __declspec(dllexport)
17#else
18# define LLTHREADS_EXPORT_API LUALIB_API
19#endif
20
21/* wrap strerror_s(). */
22#ifdef _WIN32
23# ifdef __GNUC__
24# ifndef strerror_r
25# define strerror_r(errno, buf, buflen) do { \
26 strncpy((buf), strerror(errno), (buflen)-1); \
27 (buf)[(buflen)-1] = '\0'; \
28 } while(0)
29# endif
30# else
31# ifndef strerror_r
32# define strerror_r(errno, buf, buflen) strerror_s((buf), (buflen), (errno))
33# endif
34# endif
35#endif
36
37#ifndef USE_PTHREAD
38# define OS_THREAD_RETURT unsigned int __stdcall
39# define INVALID_THREAD INVALID_HANDLE_VALUE
40# define INFINITE_JOIN_TIMEOUT INFINITE
41typedef DWORD join_timeout_t;
42typedef HANDLE os_thread_t;
43#else
44# define OS_THREAD_RETURT void *
45# define INVALID_THREAD 0
46# define INFINITE_JOIN_TIMEOUT -1
47typedef int join_timeout_t;
48typedef pthread_t os_thread_t;
49#endif
50
51#include "l52util.h"
52#include <lualib.h>
53
54LLTHREADS_EXPORT_API int luaopen_llthreads(lua_State *L);
55
56//{ traceback
57
58#define ERROR_LEN 1024
59
60/******************************************************************************
61* traceback() function from Lua 5.1/5.2 source.
62* Copyright (C) 1994-2008 Lua.org, PUC-Rio. All rights reserved.
63*
64* Permission is hereby granted, free of charge, to any person obtaining
65* a copy of this software and associated documentation files (the
66* "Software"), to deal in the Software without restriction, including
67* without limitation the rights to use, copy, modify, merge, publish,
68* distribute, sublicense, and/or sell copies of the Software, and to
69* permit persons to whom the Software is furnished to do so, subject to
70* the following conditions:
71*
72* The above copyright notice and this permission notice shall be
73* included in all copies or substantial portions of the Software.
74*
75* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
76* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
77* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
78* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
79* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
80* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
81* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
82******************************************************************************/
83#if !defined(LUA_VERSION_NUM) || (LUA_VERSION_NUM == 501)
84/* from Lua 5.1 */
85static int traceback (lua_State *L) {
86 if (!lua_isstring(L, 1)) /* 'message' not a string? */
87 return 1; /* keep it intact */
88 lua_getglobal(L, "debug");
89 if (!lua_istable(L, -1)) {
90 lua_pop(L, 1);
91 return 1;
92 }
93 lua_getfield(L, -1, "traceback");
94 if (!lua_isfunction(L, -1)) {
95 lua_pop(L, 2);
96 return 1;
97 }
98 lua_pushvalue(L, 1); /* pass error message */
99 lua_pushinteger(L, 2); /* skip this function and traceback */
100 lua_call(L, 2, 1); /* call debug.traceback */
101 return 1;
102}
103#else
104/* from Lua 5.2 */
105static int traceback (lua_State *L) {
106 const char *msg = lua_tostring(L, 1);
107 if (msg)
108 luaL_traceback(L, L, msg, 1);
109 else if (!lua_isnoneornil(L, 1)) { /* is there an error object? */
110 if (!luaL_callmeta(L, 1, "__tostring")) /* try its 'tostring' metamethod */
111 lua_pushliteral(L, "(no error message)");
112 }
113 return 1;
114}
115#endif
116
117//}
118
119//{ copy values
120
121/* maximum recursive depth of table copies. */
122#define MAX_COPY_DEPTH 30
123
124typedef struct {
125 lua_State *from_L;
126 lua_State *to_L;
127 int has_cache;
128 int cache_idx;
129 int is_arg;
130} llthread_copy_state;
131
132static int llthread_copy_table_from_cache(llthread_copy_state *state, int idx) {
133 void *ptr;
134
135 /* convert table to pointer for lookup in cache. */
136 ptr = (void *)lua_topointer(state->from_L, idx);
137 if(ptr == NULL) return 0; /* can't convert to pointer. */
138
139 /* check if we need to create the cache. */
140 if(!state->has_cache) {
141 lua_newtable(state->to_L);
142 lua_replace(state->to_L, state->cache_idx);
143 state->has_cache = 1;
144 }
145
146 lua_pushlightuserdata(state->to_L, ptr);
147 lua_rawget(state->to_L, state->cache_idx);
148 if(lua_isnil(state->to_L, -1)) {
149 /* not in cache. */
150 lua_pop(state->to_L, 1);
151 /* create new table and add to cache. */
152 lua_newtable(state->to_L);
153 lua_pushlightuserdata(state->to_L, ptr);
154 lua_pushvalue(state->to_L, -2);
155 lua_rawset(state->to_L, state->cache_idx);
156 return 0;
157 }
158 /* found table in cache. */
159 return 1;
160}
161
162static int llthread_copy_value(llthread_copy_state *state, int depth, int idx) {
163 const char *str;
164 size_t str_len;
165 int kv_pos;
166
167 /* Maximum recursive depth */
168 if(++depth > MAX_COPY_DEPTH) {
169 return luaL_error(state->from_L, "Hit maximum copy depth (%d > %d).", depth, MAX_COPY_DEPTH);
170 }
171
172 /* only support string/number/boolean/nil/table/lightuserdata. */
173 switch(lua_type(state->from_L, idx)) {
174 case LUA_TNIL:
175 lua_pushnil(state->to_L);
176 break;
177 case LUA_TNUMBER:
178 lua_pushnumber(state->to_L, lua_tonumber(state->from_L, idx));
179 break;
180 case LUA_TBOOLEAN:
181 lua_pushboolean(state->to_L, lua_toboolean(state->from_L, idx));
182 break;
183 case LUA_TSTRING:
184 str = lua_tolstring(state->from_L, idx, &(str_len));
185 lua_pushlstring(state->to_L, str, str_len);
186 break;
187 case LUA_TLIGHTUSERDATA:
188 lua_pushlightuserdata(state->to_L, lua_touserdata(state->from_L, idx));
189 break;
190 case LUA_TTABLE:
191 /* make sure there is room on the new state for 3 values (table,key,value) */
192 if(!lua_checkstack(state->to_L, 3)) {
193 return luaL_error(state->from_L, "To stack overflow!");
194 }
195 /* make room on from stack for key/value pairs. */
196 luaL_checkstack(state->from_L, 2, "From stack overflow!");
197
198 /* check cache for table. */
199 if(llthread_copy_table_from_cache(state, idx)) {
200 /* found in cache don't need to copy table. */
201 break;
202 }
203 lua_pushnil(state->from_L);
204 while (lua_next(state->from_L, idx) != 0) {
205 /* key is at (top - 1), value at (top), but we need to normalize these
206 * to positive indices */
207 kv_pos = lua_gettop(state->from_L);
208 /* copy key */
209 llthread_copy_value(state, depth, kv_pos - 1);
210 /* copy value */
211 llthread_copy_value(state, depth, kv_pos);
212 /* Copied key and value are now at -2 and -1 in state->to_L. */
213 lua_settable(state->to_L, -3);
214 /* Pop value for next iteration */
215 lua_pop(state->from_L, 1);
216 }
217 break;
218 case LUA_TFUNCTION:
219 case LUA_TUSERDATA:
220 case LUA_TTHREAD:
221 default:
222 if (state->is_arg) {
223 return luaL_argerror(state->from_L, idx, "function/userdata/thread types un-supported.");
224 } else {
225 /* convert un-supported types to an error string. */
226 lua_pushfstring(state->to_L, "Un-supported value: %s: %p",
227 lua_typename(state->from_L, lua_type(state->from_L, idx)), lua_topointer(state->from_L, idx));
228 }
229 }
230
231 return 1;
232}
233
234static int llthread_copy_values(lua_State *from_L, lua_State *to_L, int idx, int top, int is_arg) {
235 llthread_copy_state state;
236 int nvalues = 0;
237 int n;
238
239 nvalues = (top - idx) + 1;
240 /* make sure there is room on the new state for the values. */
241 if(!lua_checkstack(to_L, nvalues + 1)) {
242 return luaL_error(from_L, "To stack overflow!");
243 }
244
245 /* setup copy state. */
246 state.from_L = from_L;
247 state.to_L = to_L;
248 state.is_arg = is_arg;
249 state.has_cache = 0; /* don't create cache table unless it is needed. */
250 lua_pushnil(to_L);
251 state.cache_idx = lua_gettop(to_L);
252
253 nvalues = 0;
254 for(n = idx; n <= top; n++) {
255 llthread_copy_value(&state, 0, n);
256 ++nvalues;
257 }
258
259 /* remove cache table. */
260 lua_remove(to_L, state.cache_idx);
261
262 return nvalues;
263}
264
265//}
266
267static int fail(lua_State *L, const char *msg){
268 lua_pushnil(L);
269 lua_pushstring(L, msg);
270 return 2;
271}
272
273#define flags_t unsigned char
274
275#define TSTATE_NONE (flags_t)0
276#define TSTATE_STARTED (flags_t)1<<0
277#define TSTATE_DETACHED (flags_t)1<<1
278#define TSTATE_JOINED (flags_t)1<<2
279
280/*At leas one flag*/
281#define FLAG_IS_SET(O, F) (O->flags & (flags_t)(F))
282/*All flags*/
283#define FLAGS_IS_SET(O, F) ((F) == FLAG_IS_SET(O, F))
284#define FLAG_SET(O, F) O->flags |= (flags_t)(F)
285#define FLAG_UNSET(O, F) O->flags &= ~((flags_t)(F))
286
287#define ALLOC_STRUCT(S) (S*)calloc(1, sizeof(S))
288#define FREE_STRUCT(O) free(O)
289
290typedef struct llthread_child_t {
291 lua_State *L;
292 int status;
293 flags_t flags;
294} llthread_child_t;
295
296typedef struct llthread_t {
297 llthread_child_t *child;
298 os_thread_t thread;
299 flags_t flags;
300} llthread_t;
301
302//{ llthread_child
303
304static void open_thread_libs(lua_State *L){
305#ifdef LLTHREAD_REGISTER_STD_LIBRARY
306# define L_REGLIB(L, name) lua_pushcfunction(L, luaopen_##name); lua_setfield(L, -2, #name)
307#else
308# define L_REGLIB(L, name) lua_cpcall(L, luaopen_##name, 0)
309#endif
310
311 int top = lua_gettop(L);
312 lua_cpcall(L, luaopen_base, 0);
313 lua_cpcall(L, luaopen_package, 0);
314 lua_settop(L, top);
315
316 /* get package.preload */
317 lua_getglobal(L, "package"); lua_getfield(L, -1, "preload"); lua_remove(L, -2);
318
319 L_REGLIB(L, io );
320 L_REGLIB(L, os );
321 L_REGLIB(L, math );
322 L_REGLIB(L, table );
323 L_REGLIB(L, debug );
324 L_REGLIB(L, string );
325 L_REGLIB(L, llthreads );
326
327 lua_settop(L, top);
328#undef L_REGLIB
329}
330
331static llthread_child_t *llthread_child_new() {
332 llthread_child_t *this = ALLOC_STRUCT(llthread_child_t);
333 if(!this) return NULL;
334
335 memset(this, 0, sizeof(llthread_child_t));
336
337 /* create new lua_State for the thread. */
338 /* open standard libraries. */
339 /* push traceback function as first value on stack. */
340 this->L = luaL_newstate();
341 open_thread_libs(this->L);
342 lua_pushcfunction(this->L, traceback);
343
344 return this;
345}
346
347static void llthread_child_destroy(llthread_child_t *this) {
348 lua_close(this->L);
349 FREE_STRUCT(this);
350}
351
352static OS_THREAD_RETURT llthread_child_thread_run(void *arg) {
353 llthread_child_t *this = (llthread_child_t *)arg;
354 lua_State *L = this->L;
355 int nargs = lua_gettop(L) - 2;
356
357 this->status = lua_pcall(L, nargs, LUA_MULTRET, 1);
358
359 /* alwasy print errors here, helps with debugging bad code. */
360 if(this->status != 0) {
361 const char *err_msg = lua_tostring(L, -1);
362 fprintf(stderr, "Error from thread: %s\n", err_msg);
363 fflush(stderr);
364 }
365
366 /* if thread is detached, then destroy the child state. */
367 if(FLAG_IS_SET(this, TSTATE_DETACHED)) {
368 /* thread is detached, so it must clean-up the child state. */
369 llthread_child_destroy(this);
370 this = NULL;
371 }
372
373#ifndef USE_PTHREAD
374 if(this) {
375 /* attached thread, don't close thread handle. */
376 _endthreadex(0);
377 } else {
378 /* detached thread, close thread handle. */
379 _endthread();
380 }
381 return 0;
382#else
383 return this;
384#endif
385}
386
387//}
388
389//{ llthread
390
391static llthread_t *llthread_new() {
392 llthread_t *this = ALLOC_STRUCT(llthread_t);
393 if(!this) return NULL;
394
395 this->flags = TSTATE_NONE;
396 this->thread = INVALID_THREAD;
397 this->child = llthread_child_new();
398 if(!this->child){
399 FREE_STRUCT(this);
400 return NULL;
401 }
402
403 return this;
404}
405
406static void llthread_cleanup_child(llthread_t *this) {
407 if(this->child) {
408 llthread_child_destroy(this->child);
409 this->child = NULL;
410 }
411}
412
413static void llthread_destroy(llthread_t *this) {
414 /* We still own the child thread object iff the thread was not started or
415 * we have joined the thread.
416 */
417 if(FLAG_IS_SET(this, TSTATE_JOINED)||(this->flags == TSTATE_NONE)) {
418 llthread_cleanup_child(this);
419 }
420 FREE_STRUCT(this);
421}
422
423static int llthread_push_args(lua_State *L, llthread_child_t *child, int idx, int top) {
424 return llthread_copy_values(L, child->L, idx, top, 1 /* is_arg */);
425}
426
427static int llthread_push_results(lua_State *L, llthread_child_t *child, int idx, int top) {
428 return llthread_copy_values(child->L, L, idx, top, 0 /* is_arg */);
429}
430
431static int llthread_start(llthread_t *this, int start_detached) {
432 llthread_child_t *child = this->child;
433 int rc = 0;
434
435 if(start_detached){
436 FLAG_SET(child, TSTATE_DETACHED);
437 }
438
439#ifndef USE_PTHREAD
440 this->thread = (HANDLE)_beginthreadex(NULL, 0, llthread_child_thread_run, child, 0, NULL);
441#else
442 rc = pthread_create(&(this->thread), NULL, llthread_child_thread_run, child);
443 if(rc == 0){
444 this->thread = INVALID_THREAD
445 }
446#endif
447
448 if(this->thread != INVALID_THREAD) {
449 FLAG_SET(this, TSTATE_STARTED);
450 if(start_detached) {
451 FLAG_SET(this, TSTATE_DETACHED);
452 this->child = NULL;
453#ifdef USE_PTHREAD
454 rc = pthread_detach(this->thread);
455#endif
456 }
457 }
458
459 return rc;
460}
461
462static int llthread_join(llthread_t *this, join_timeout_t timeout) {
463#ifndef USE_PTHREAD
464 DWORD ret = 0;
465 if(INVALID_THREAD == this->thread) return 0;
466 ret = WaitForSingleObject( this->thread, timeout );
467 if( ret == WAIT_OBJECT_0){ /* Destroy the thread object. */
468 CloseHandle( this->thread );
469 this->thread = INVALID_THREAD;
470 FLAG_SET(this, TSTATE_JOINED);
471 return 0;
472 }
473 else if( ret == WAIT_TIMEOUT ){
474 return 1;
475 }
476 return 2;
477#else
478 /* then join the thread. */
479 int rc = pthread_join(this->thread, NULL);
480 if(rc == 0) {
481 FLAG_SET(this, TSTATE_JOINED);
482 }
483 return rc;
484#endif
485}
486
487static llthread_t *llthread_create(lua_State *L, const char *code, size_t code_len) {
488 llthread_t *this = llthread_new();
489 llthread_child_t *child = this->child;
490
491 /* load Lua code into child state. */
492 int rc = luaL_loadbuffer(child->L, code, code_len, code);
493 if(rc != 0) {
494 /* copy error message to parent state. */
495 size_t len; const char *str = lua_tolstring(child->L, -1, &len);
496 if(str != NULL) {
497 lua_pushlstring(L, str, len);
498 } else {
499 /* non-string error message. */
500 lua_pushfstring(L, "luaL_loadbuffer() failed to load Lua code: rc=%d", rc);
501 }
502 llthread_destroy(this);
503 lua_error(L);
504 return NULL;
505 }
506
507 /* copy extra args from main state to child state. */
508 /* Push all args after the Lua code. */
509 llthread_push_args(L, child, 3, lua_gettop(L));
510
511 return this;
512}
513
514//}
515
516//{ Lua interface to llthread
517
518#define LLTHREAD_T_NAME "LLThread"
519static const char *LLTHREAD_T = LLTHREAD_T_NAME;
520
521static llthread_t *l_llthread_at (lua_State *L, int i) {
522 llthread_t **this = (llthread_t **)lutil_checkudatap (L, i, LLTHREAD_T);
523 luaL_argcheck (L, this != NULL, i, "thread expected");
524 luaL_argcheck (L, *this != NULL, i, "thread expected");
525 // luaL_argcheck (L, !(counter->flags & FLAG_DESTROYED), 1, "PDH Counter is destroyed");
526 return *this;
527}
528
529static int l_llthread_delete(lua_State *L) {
530 llthread_t **pthis = (llthread_t **)lutil_checkudatap (L, 1, LLTHREAD_T);
531 llthread_t *this;
532 luaL_argcheck (L, pthis != NULL, 1, "thread expected");
533 this = *pthis;
534
535 /*already exists*/
536 if(this == NULL) return 0;
537
538 /* if the thread has been started and has not been detached/joined. */
539 if( FLAG_IS_SET(this, TSTATE_STARTED) &&
540 !FLAG_IS_SET(this, (TSTATE_DETACHED|TSTATE_JOINED))
541 ){
542 /* then join the thread. */
543 llthread_child_t *child = this->child;
544 llthread_join(this, INFINITE_JOIN_TIMEOUT);
545 if(child && child->status != 0) {
546 const char *err_msg = lua_tostring(child->L, -1);
547 fprintf(stderr, "Error from non-joined thread: %s\n", err_msg);
548 fflush(stderr);
549 }
550 }
551
552 llthread_destroy(this);
553 *pthis = NULL;
554
555 return 0;
556}
557
558static int l_llthread_start(lua_State *L) {
559 llthread_t *this = l_llthread_at(L, 1);
560 int start_detached = lua_toboolean(L, 2);
561 int rc;
562
563 if(this->flags != TSTATE_NONE) {
564 return fail(L, "Thread already started.");
565 }
566
567 rc = llthread_start(this, start_detached);
568 if(rc != 0) {
569 char buf[ERROR_LEN];
570 strerror_r(errno, buf, ERROR_LEN);
571 return fail(L, buf);
572 }
573
574 lua_settop(L, 1); // return this
575 return 1;
576}
577
578static int l_llthread_join(lua_State *L) {
579 llthread_t *this = l_llthread_at(L, 1);
580 llthread_child_t *child = this->child;
581 int rc;
582
583 if(!FLAG_IS_SET(this, TSTATE_STARTED )) {
584 return fail(L, "Can't join a thread that hasn't be started.");
585 }
586 if( FLAG_IS_SET(this, TSTATE_DETACHED)) {
587 return fail(L, "Can't join a thread that has been detached.");
588 }
589 if( FLAG_IS_SET(this, TSTATE_JOINED )) {
590 return fail(L, "Can't join a thread that has already been joined.");
591 }
592
593 /* join the thread. */
594 rc = llthread_join(this, INFINITE_JOIN_TIMEOUT);
595
596 /* Push all results after the Lua code. */
597 if(child && FLAG_IS_SET(this, TSTATE_JOINED)) {
598 int top;
599 if(child->status != 0) {
600 const char *err_msg = lua_tostring(child->L, -1);
601 lua_pushboolean(L, 0);
602 lua_pushfstring(L, "Error from child thread: %s", err_msg);
603 top = 2;
604 } else {
605 lua_pushboolean(L, 1);
606 top = lua_gettop(child->L);
607 /* return results to parent thread. */
608 llthread_push_results(L, child, 2, top);
609 }
610 llthread_cleanup_child(this);
611 return top;
612 }
613
614#ifndef USE_PTHREAD
615 if( rc == 1 ){
616 lua_pushboolean(L, 0);
617 lua_pushstring(L, "timeout");
618 return 2;
619 }
620#endif
621
622 {
623 char buf[ERROR_LEN];
624 strerror_r(errno, buf, ERROR_LEN);
625
626 llthread_cleanup_child(this);
627
628 lua_pushboolean(L, 0);
629 lua_pushstring(L, buf);
630 return 2;
631 }
632
633}
634
635static int l_llthread_new(lua_State *L) {
636 size_t lua_code_len; const char *lua_code = luaL_checklstring(L, 1, &lua_code_len);
637 llthread_t **this = lutil_newudatap(L, llthread_t*, LLTHREAD_T);
638 lua_insert(L, 2); /*move self prior args*/
639 *this = llthread_create(L, lua_code, lua_code_len);
640
641 lua_settop(L, 2);
642 return 1;
643}
644
645static const struct luaL_Reg l_llthread_meth[] = {
646 {"start", l_llthread_start },
647 {"join", l_llthread_join },
648 {"__gc", l_llthread_delete },
649
650 {NULL, NULL}
651};
652
653//}
654
655static const struct luaL_Reg l_llthreads_lib[] = {
656 {"new", l_llthread_new },
657
658 {NULL, NULL}
659};
660
661LLTHREADS_EXPORT_API int luaopen_llthreads(lua_State *L) {
662 int top = lua_gettop(L);
663 lutil_createmetap(L, LLTHREAD_T, l_llthread_meth, 0);
664 lua_settop(L, top);
665
666 lua_newtable(L);
667 luaL_setfuncs(L, l_llthreads_lib, 0);
668 return 1;
669}