aboutsummaryrefslogtreecommitdiff
path: root/llthreads2/src
diff options
context:
space:
mode:
authorAlexey Melnichuk <mimir@newmail.ru>2014-02-11 17:32:59 +0400
committerAlexey Melnichuk <mimir@newmail.ru>2014-02-11 17:34:14 +0400
commitd13929706a3b45bd64e0a87e0afc3d45625e888d (patch)
treed1c846805f127736d1dafb3b3094eaf78d032859 /llthreads2/src
parent46ed59584e5407c49a02f1ea6bede6487259a92e (diff)
downloadlua-llthreads2-d13929706a3b45bd64e0a87e0afc3d45625e888d.tar.gz
lua-llthreads2-d13929706a3b45bd64e0a87e0afc3d45625e888d.tar.bz2
lua-llthreads2-d13929706a3b45bd64e0a87e0afc3d45625e888d.zip
Init LuaDist for llthreads2 module.
Diffstat (limited to 'llthreads2/src')
-rw-r--r--llthreads2/src/copy.inc170
-rw-r--r--llthreads2/src/l52util.c126
-rw-r--r--llthreads2/src/l52util.h57
-rw-r--r--llthreads2/src/llthread.c746
-rw-r--r--llthreads2/src/traceback.inc56
5 files changed, 1155 insertions, 0 deletions
diff --git a/llthreads2/src/copy.inc b/llthreads2/src/copy.inc
new file mode 100644
index 0000000..21ba261
--- /dev/null
+++ b/llthreads2/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/llthreads2/src/l52util.c b/llthreads2/src/l52util.c
new file mode 100644
index 0000000..9d44a40
--- /dev/null
+++ b/llthreads2/src/l52util.c
@@ -0,0 +1,126 @@
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
14#ifndef luaL_register
15
16void luaL_register (lua_State *L, const char *libname, const luaL_Reg *l){
17 if(libname) lua_newtable(L);
18 luaL_setfuncs(L, l, 0);
19}
20
21#endif
22
23#else
24
25void luaL_setfuncs (lua_State *L, const luaL_Reg *l, int nup){
26 luaL_checkstack(L, nup, "too many upvalues");
27 for (; l->name != NULL; l++) { /* fill the table with given functions */
28 int i;
29 for (i = 0; i < nup; i++) /* copy upvalues to the top */
30 lua_pushvalue(L, -nup);
31 lua_pushcclosure(L, l->func, nup); /* closure with those upvalues */
32 lua_setfield(L, -(nup + 2), l->name);
33 }
34 lua_pop(L, nup); /* remove upvalues */
35}
36
37void lua_rawgetp(lua_State *L, int index, const void *p){
38 index = lua_absindex(L, index);
39 lua_pushlightuserdata(L, (void *)p);
40 lua_rawget(L, index);
41}
42
43void lua_rawsetp (lua_State *L, int index, const void *p){
44 index = lua_absindex(L, index);
45 lua_pushlightuserdata(L, (void *)p);
46 lua_insert(L, -2);
47 lua_rawset(L, index);
48}
49
50void lutil_require(lua_State *L, const char* name, lua_CFunction fn, int glb) {
51 // @fixme generate error if we can not load module
52 lua_cpcall(L, fn, NULL);
53}
54
55#endif
56
57int lutil_newmetatablep (lua_State *L, const void *p) {
58 lua_rawgetp(L, LUA_REGISTRYINDEX, p);
59 if (!lua_isnil(L, -1)) /* name already in use? */
60 return 0; /* leave previous value on top, but return 0 */
61 lua_pop(L, 1);
62
63 lua_newtable(L); /* create metatable */
64 lua_pushvalue(L, -1); /* duplicate metatable to set*/
65 lua_rawsetp(L, LUA_REGISTRYINDEX, p);
66
67 return 1;
68}
69
70void lutil_getmetatablep (lua_State *L, const void *p) {
71 lua_rawgetp(L, LUA_REGISTRYINDEX, p);
72}
73
74void lutil_setmetatablep (lua_State *L, const void *p) {
75 lutil_getmetatablep(L, p);
76 assert(lua_istable(L,-1));
77 lua_setmetatable (L, -2);
78}
79
80int lutil_isudatap (lua_State *L, int ud, const void *p) {
81 if (lua_isuserdata(L, ud)){
82 if (lua_getmetatable(L, ud)) { /* does it have a metatable? */
83 int res;
84 lutil_getmetatablep(L,p); /* get correct metatable */
85 res = lua_rawequal(L, -1, -2); /* does it have the correct mt? */
86 lua_pop(L, 2); /* remove both metatables */
87 return res;
88 }
89 }
90 return 0;
91}
92
93void *lutil_checkudatap (lua_State *L, int ud, const void *p) {
94 void *up = lua_touserdata(L, ud);
95 if (up != NULL) { /* value is a userdata? */
96 if (lua_getmetatable(L, ud)) { /* does it have a metatable? */
97 lutil_getmetatablep(L,p); /* get correct metatable */
98 if (lua_rawequal(L, -1, -2)) { /* does it have the correct mt? */
99 lua_pop(L, 2); /* remove both metatables */
100 return up;
101 }
102 }
103 }
104 luaL_typerror(L, ud, p); /* else error */
105 return NULL; /* to avoid warnings */
106}
107
108int lutil_createmetap (lua_State *L, const void *p, const luaL_Reg *methods, int nup) {
109 if (!lutil_newmetatablep(L, p))
110 return 0;
111
112 lua_insert(L, -1 - nup); /* move mt prior upvalues */
113 luaL_setfuncs (L, methods, nup); /* define methods */
114 lua_pushliteral (L, "__index"); /* define metamethods */
115 lua_pushvalue (L, -2);
116 lua_settable (L, -3);
117
118 return 1;
119}
120
121void *lutil_newudatap_impl(lua_State *L, size_t size, const void *p){
122 void *obj = lua_newuserdata (L, size);
123 memset(obj, 0, size);
124 lutil_setmetatablep(L, p);
125 return obj;
126}
diff --git a/llthreads2/src/l52util.h b/llthreads2/src/l52util.h
new file mode 100644
index 0000000..38a87db
--- /dev/null
+++ b/llthreads2/src/l52util.h
@@ -0,0 +1,57 @@
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#ifndef lua_objlen
15
16#define lua_objlen lua_rawlen
17
18#endif
19
20int luaL_typerror (lua_State *L, int narg, const char *tname);
21
22#ifndef luaL_register
23
24void luaL_register (lua_State *L, const char *libname, const luaL_Reg *l);
25
26#endif
27
28#define lutil_require luaL_requiref
29
30#else // lua 5.1
31
32// functions form lua 5.2
33
34# define lua_absindex(L, i) (((i)>0)?(i):((i)<=LUA_REGISTRYINDEX?(i):(lua_gettop(L)+(i)+1)))
35# define lua_rawlen lua_objlen
36
37void lua_rawgetp (lua_State *L, int index, const void *p);
38void lua_rawsetp (lua_State *L, int index, const void *p);
39void luaL_setfuncs (lua_State *L, const luaL_Reg *l, int nup);
40
41void lutil_require(lua_State *L, const char* name, lua_CFunction fn, int glb);
42
43#endif
44
45int lutil_newmetatablep (lua_State *L, const void *p);
46void lutil_getmetatablep (lua_State *L, const void *p);
47void lutil_setmetatablep (lua_State *L, const void *p);
48
49#define lutil_newudatap(L, TTYPE, TNAME) (TTYPE *)lutil_newudatap_impl(L, sizeof(TTYPE), TNAME)
50int lutil_isudatap (lua_State *L, int ud, const void *p);
51void *lutil_checkudatap (lua_State *L, int ud, const void *p);
52int lutil_createmetap (lua_State *L, const void *p, const luaL_Reg *methods, int nup);
53
54void *lutil_newudatap_impl (lua_State *L, size_t size, const void *p);
55
56#endif
57
diff --git a/llthreads2/src/llthread.c b/llthreads2/src/llthread.c
new file mode 100644
index 0000000..0ffa727
--- /dev/null
+++ b/llthreads2/src/llthread.c
@@ -0,0 +1,746 @@
1#if !defined(_WIN32) && !defined(USE_PTHREAD)
2# define USE_PTHREAD
3#endif
4
5#define LLTHREAD_VERSION_MAJOR 0
6#define LLTHREAD_VERSION_MINOR 1
7#define LLTHREAD_VERSION_PATCH 0
8#define LLTHREAD_VERSION_COMMENT ""
9
10#ifndef USE_PTHREAD
11# include <windows.h>
12# include <process.h>
13#else
14# include <pthread.h>
15#endif
16
17#include <stdlib.h>
18#include <stdio.h>
19#include <memory.h>
20#include <assert.h>
21#include <errno.h>
22#include <lualib.h>
23#include "l52util.h"
24#include "traceback.inc"
25#include "copy.inc"
26
27/*export*/
28#ifdef _WIN32
29# define LLTHREADS_EXPORT_API __declspec(dllexport)
30#else
31# define LLTHREADS_EXPORT_API LUALIB_API
32#endif
33
34/* wrap strerror_s(). */
35#ifdef _WIN32
36# ifdef __GNUC__
37# ifndef strerror_r
38# define strerror_r(errno, buf, buflen) do { \
39 strncpy((buf), strerror(errno), (buflen)-1); \
40 (buf)[(buflen)-1] = '\0'; \
41 } while(0)
42# endif
43# else
44# ifndef strerror_r
45# define strerror_r(errno, buf, buflen) strerror_s((buf), (buflen), (errno))
46# endif
47# endif
48#endif
49
50#ifndef USE_PTHREAD
51# define OS_THREAD_RETURN unsigned int __stdcall
52# define INVALID_THREAD INVALID_HANDLE_VALUE
53# define INFINITE_JOIN_TIMEOUT INFINITE
54# define JOIN_OK 0
55# define JOIN_ETIMEDOUT 1
56# define JOIN_FAIL 2
57typedef DWORD join_timeout_t;
58typedef HANDLE os_thread_t;
59#else
60# define OS_THREAD_RETURN void *
61# define INFINITE_JOIN_TIMEOUT -1
62# define JOIN_OK 0
63# define JOIN_ETIMEDOUT ETIMEDOUT
64typedef int join_timeout_t;
65typedef pthread_t os_thread_t;
66#endif
67
68#define ERROR_LEN 1024
69
70#define flags_t unsigned char
71
72#define FLAG_NONE (flags_t)0
73#define FLAG_STARTED (flags_t)1<<0
74#define FLAG_DETACHED (flags_t)1<<1
75#define FLAG_JOINED (flags_t)1<<2
76#define FLAG_JOINABLE (flags_t)1<<3
77
78/*At least one flag*/
79#define FLAG_IS_SET(O, F) (O->flags & (flags_t)(F))
80#define FLAG_SET(O, F) O->flags |= (flags_t)(F)
81#define FLAG_UNSET(O, F) O->flags &= ~((flags_t)(F))
82#define IS(O, F) FLAG_IS_SET(O, FLAG_##F)
83#define SET(O, F) FLAG_SET(O, FLAG_##F)
84
85#define ALLOC_STRUCT(S) (S*)calloc(1, sizeof(S))
86#define FREE_STRUCT(O) free(O)
87
88#ifndef LLTHREAD_MODULE_NAME
89# define LLTHREAD_MODULE_NAME llthreads
90#endif
91
92#define CAT(S1,S2) S1##S2
93
94#define LLTHREAD_OPEN_NAME_IMPL(NAME) CAT(luaopen_, NAME)
95
96#define LLTHREAD_OPEN_NAME LLTHREAD_OPEN_NAME_IMPL(LLTHREAD_MODULE_NAME)
97
98LLTHREADS_EXPORT_API int LLTHREAD_OPEN_NAME(lua_State *L);
99
100#define LLTHREAD_NAME "LLThread"
101static const char *LLTHREAD_TAG = LLTHREAD_NAME;
102static const char *LLTHREAD_LOGGER_HOLDER = LLTHREAD_NAME " logger holder";
103
104typedef struct llthread_child_t {
105 lua_State *L;
106 int status;
107 flags_t flags;
108} llthread_child_t;
109
110typedef struct llthread_t {
111 llthread_child_t *child;
112 os_thread_t thread;
113 flags_t flags;
114} llthread_t;
115
116static int fail(lua_State *L, const char *msg){
117 lua_pushnil(L);
118 lua_pushstring(L, msg);
119 return 2;
120}
121
122//{ logger interface
123void llthread_log(lua_State *L, const char *hdr, const char *msg){
124 int top = lua_gettop(L);
125 lua_rawgetp(L, LUA_REGISTRYINDEX, LLTHREAD_LOGGER_HOLDER);
126 if(lua_isnil(L, -1)){
127 lua_pop(L, 1);
128 fputs(hdr, stderr);
129 fputs(msg, stderr);
130 fputc('\n', stderr);
131 fflush(stderr);
132 return;
133 }
134 lua_pushstring(L, hdr);
135 lua_pushstring(L, msg);
136 lua_concat(L, 2);
137 lua_pcall(L, 1, 0, 0);
138 lua_settop(L, top);
139}
140//}
141
142//{ llthread_child
143
144static void open_thread_libs(lua_State *L){
145#define L_REGLIB(L, name) lua_pushcfunction(L, luaopen_##name); lua_setfield(L, -2)
146
147 int top = lua_gettop(L);
148
149#ifndef LLTHREAD_REGISTER_STD_LIBRARY
150
151 luaL_openlibs(L);
152 lua_getglobal(L, "package"); lua_getfield(L, -1, "preload"); lua_remove(L, -2);
153
154#else
155
156 lutil_require(L, "_G", luaopen_base, 1);
157 lutil_require(L, "package", luaopen_package, 1);
158 lua_settop(L, top);
159
160 /* get package.preload */
161 lua_getglobal(L, "package"); lua_getfield(L, -1, "preload"); lua_remove(L, -2);
162 L_REGLIB(L, io, 1);
163 L_REGLIB(L, os, 1);
164 L_REGLIB(L, math, 1);
165 L_REGLIB(L, table, 1);
166 L_REGLIB(L, string, 1);
167
168#ifdef LUA_DBLIBNAME
169 L_REGLIB(L, debug, 1);
170#endif
171
172 /* @fixme find out luaopen_XXX at runtime */
173#ifdef LUA_JITLIBNAME
174 L_REGLIB(L, bit, 1);
175 L_REGLIB(L, jit, 1);
176 L_REGLIB(L, ffi, 1);
177#elif defined LUA_BITLIBNAME
178 L_REGLIB(L, bit32, 1);
179#endif
180
181#endif
182
183#ifdef LLTHREAD_REGISTER_THREAD_LIBRARY
184 L_REGLIB(L, llthreads, 0);
185#endif
186
187 lua_settop(L, top);
188
189#undef L_REGLIB
190}
191
192static llthread_child_t *llthread_child_new() {
193 llthread_child_t *this = ALLOC_STRUCT(llthread_child_t);
194 if(!this) return NULL;
195
196 memset(this, 0, sizeof(llthread_child_t));
197
198 /* create new lua_State for the thread. */
199 /* open standard libraries. */
200 this->L = luaL_newstate();
201 open_thread_libs(this->L);
202
203 return this;
204}
205
206static void llthread_child_destroy(llthread_child_t *this) {
207 lua_close(this->L);
208 FREE_STRUCT(this);
209}
210
211static OS_THREAD_RETURN llthread_child_thread_run(void *arg) {
212 llthread_child_t *this = (llthread_child_t *)arg;
213 lua_State *L = this->L;
214 int nargs = lua_gettop(L) - 1;
215
216 /* push traceback function as first value on stack. */
217 lua_pushcfunction(this->L, traceback);
218 lua_insert(L, 1);
219
220 this->status = lua_pcall(L, nargs, LUA_MULTRET, 1);
221
222 /* alwasy print errors here, helps with debugging bad code. */
223 if(this->status != 0) {
224 llthread_log(L, "Error from thread: ", lua_tostring(L, -1));
225 }
226
227 if(IS(this, DETACHED) || !IS(this, JOINABLE)) {
228 /* thread is detached, so it must clean-up the child state. */
229 llthread_child_destroy(this);
230 this = NULL;
231 }
232
233#ifndef USE_PTHREAD
234 if(this) {
235 /* attached thread, don't close thread handle. */
236 _endthreadex(0);
237 } else {
238 /* detached thread, close thread handle. */
239 _endthread();
240 }
241 return 0;
242#else
243 return this;
244#endif
245}
246
247//}
248
249//{ llthread
250
251static void llthread_validate(llthread_t *this){
252 /* describe valid state of llthread_t object
253 * from after create and before destroy
254 */
255 if(!IS(this, STARTED)){
256 assert(!IS(this, DETACHED));
257 assert(!IS(this, JOINED));
258 assert(!IS(this, JOINABLE));
259 return;
260 }
261
262 if(IS(this, DETACHED)){
263 if(!IS(this, JOINABLE)) assert(this->child == NULL);
264 else assert(this->child != NULL);
265 }
266}
267
268static int llthread_detach(llthread_t *this);
269
270static int llthread_join(llthread_t *this, join_timeout_t timeout);
271
272static llthread_t *llthread_new() {
273 llthread_t *this = ALLOC_STRUCT(llthread_t);
274 if(!this) return NULL;
275
276 this->flags = FLAG_NONE;
277#ifndef USE_PTHREAD
278 this->thread = INVALID_THREAD;
279#endif
280 this->child = llthread_child_new();
281 if(!this->child){
282 FREE_STRUCT(this);
283 return NULL;
284 }
285
286 return this;
287}
288
289static void llthread_cleanup_child(llthread_t *this) {
290 if(this->child) {
291 llthread_child_destroy(this->child);
292 this->child = NULL;
293 }
294}
295
296static void llthread_destroy(llthread_t *this) {
297 do{
298 /* thread not started */
299 if(!IS(this, STARTED)){
300 llthread_cleanup_child(this);
301 break;
302 }
303
304 /* DETACHED */
305 if(IS(this, DETACHED)){
306 if(IS(this, JOINABLE)){
307 llthread_detach(this);
308 }
309 break;
310 }
311
312 /* ATTACHED */
313 if(!IS(this, JOINED)){
314 llthread_join(this, INFINITE_JOIN_TIMEOUT);
315 if(!IS(this, JOINED)){
316 /* @todo use current lua state to logging */
317 /*
318 * char buf[ERROR_LEN];
319 * strerror_r(errno, buf, ERROR_LEN);
320 * llthread_log(L, "Error can not join thread on gc: ", buf);
321 */
322 }
323 }
324 if(IS(this, JOINABLE)){
325 llthread_cleanup_child(this);
326 }
327
328 }while(0);
329
330 FREE_STRUCT(this);
331}
332
333static int llthread_push_args(lua_State *L, llthread_child_t *child, int idx, int top) {
334 return llthread_copy_values(L, child->L, idx, top, 1 /* is_arg */);
335}
336
337static int llthread_push_results(lua_State *L, llthread_child_t *child, int idx, int top) {
338 return llthread_copy_values(child->L, L, idx, top, 0 /* is_arg */);
339}
340
341static int llthread_detach(llthread_t *this){
342 int rc = 0;
343
344 assert(IS(this, STARTED));
345 assert(this->child != NULL);
346
347 this->child = NULL;
348
349 /*we can not detach joined thread*/
350 if(IS(this, JOINED))
351 return 0;
352
353#ifdef USE_PTHREAD
354 rc = pthread_detach(this->thread);
355#else
356 assert(this->thread != INVALID_THREAD);
357 CloseHandle(this->thread);
358 this->thread = INVALID_THREAD;
359#endif
360 return rc;
361}
362
363/* | detached | joinable || join | which thread | gc | detach |
364 * | | || return | destroy child | calls | on |
365 * ------------------------------------------------------------------------
366 * | false | falas || <NONE> | child | join | <NEVER> |
367 * *| false | true || Lua values | parent | join | <NEVER> |
368 * *| true | false || <ERROR> | child | <NONE> | start |
369 * | true | true || <NONE> | child | detach | gc |
370 * ------------------------------------------------------------------------
371 * * llthread behavior.
372 */
373static int llthread_start(llthread_t *this, int start_detached, int joinable) {
374 llthread_child_t *child = this->child;
375 int rc = 0;
376
377 llthread_validate(this);
378
379 if(joinable) SET(child, JOINABLE);
380 if(start_detached) SET(child, DETACHED);
381
382#ifndef USE_PTHREAD
383 this->thread = (HANDLE)_beginthreadex(NULL, 0, llthread_child_thread_run, child, 0, NULL);
384 if(INVALID_THREAD == this->thread){
385 rc = -1;
386 }
387#else
388 rc = pthread_create(&(this->thread), NULL, llthread_child_thread_run, child);
389#endif
390
391 if(rc == 0) {
392 SET(this, STARTED);
393 if(joinable) SET(this, JOINABLE);
394 if(start_detached) SET(this, DETACHED);
395 if((start_detached)&&(!joinable)){
396 rc = llthread_detach(this);
397 }
398 }
399
400 llthread_validate(this);
401
402 return rc;
403}
404
405static int llthread_join(llthread_t *this, join_timeout_t timeout) {
406 llthread_validate(this);
407
408 if(IS(this, JOINED)){
409 return JOIN_OK;
410 } else{
411#ifndef USE_PTHREAD
412 DWORD ret = 0;
413 if(INVALID_THREAD == this->thread) return JOIN_OK;
414 ret = WaitForSingleObject( this->thread, timeout );
415 if( ret == WAIT_OBJECT_0){ /* Destroy the thread object. */
416 CloseHandle( this->thread );
417 this->thread = INVALID_THREAD;
418 SET(this, JOINED);
419
420 llthread_validate(this);
421
422 return JOIN_OK;
423 }
424 else if( ret == WAIT_TIMEOUT ){
425 return JOIN_ETIMEDOUT;
426 }
427 return JOIN_FAIL;
428#else
429 int rc;
430 if(timeout == 0){
431 rc = pthread_kill(this->thread, 0);
432 if(rc == 0){ /* still alive */
433 return JOIN_ETIMEDOUT;
434 }
435
436 if(rc != ESRCH){
437 /*@fixme what else it can be ?*/
438 return rc;
439 }
440
441 /*thread dead so we call join to free pthread_t struct */
442 }
443
444 /* @todo use pthread_tryjoin_np/pthread_timedjoin_np to support timeout */
445
446 /* then join the thread. */
447 rc = pthread_join(this->thread, NULL);
448 if((rc == 0) || (rc == ESRCH)) {
449 SET(this, JOINED);
450 rc = JOIN_OK;
451 }
452
453 llthread_validate(this);
454
455 return rc;
456#endif
457 }
458}
459
460static int llthread_alive(llthread_t *this) {
461 llthread_validate(this);
462
463 if(IS(this, JOINED)){
464 return JOIN_OK;
465 } else{
466#ifndef USE_PTHREAD
467 DWORD ret = 0;
468 if(INVALID_THREAD == this->thread) return JOIN_OK;
469 ret = WaitForSingleObject( this->thread, 0 );
470 if( ret == WAIT_OBJECT_0) return JOIN_OK;
471 if( ret == WAIT_TIMEOUT ) return JOIN_ETIMEDOUT;
472 return JOIN_FAIL;
473#else
474 int rc = pthread_kill(this->thread, 0);
475 if(rc == 0){ /* still alive */
476 return JOIN_ETIMEDOUT;
477 }
478
479 if(rc != ESRCH){
480 /*@fixme what else it can be ?*/
481 return rc;
482 }
483
484 return JOIN_OK;
485#endif
486 }
487}
488
489static llthread_t *llthread_create(lua_State *L, const char *code, size_t code_len) {
490 llthread_t *this = llthread_new();
491 llthread_child_t *child = this->child;
492
493 /* load Lua code into child state. */
494 int rc = luaL_loadbuffer(child->L, code, code_len, code);
495 if(rc != 0) {
496 /* copy error message to parent state. */
497 size_t len; const char *str = lua_tolstring(child->L, -1, &len);
498 if(str != NULL) {
499 lua_pushlstring(L, str, len);
500 } else {
501 /* non-string error message. */
502 lua_pushfstring(L, "luaL_loadbuffer() failed to load Lua code: rc=%d", rc);
503 }
504 llthread_destroy(this);
505 lua_error(L);
506 return NULL;
507 }
508
509 /* copy extra args from main state to child state. */
510 /* Push all args after the Lua code. */
511 llthread_push_args(L, child, 3, lua_gettop(L));
512
513 llthread_validate(this);
514
515 return this;
516}
517
518//}
519
520//{ Lua interface to llthread
521
522static llthread_t *l_llthread_at (lua_State *L, int i) {
523 llthread_t **this = (llthread_t **)lutil_checkudatap (L, i, LLTHREAD_TAG);
524 luaL_argcheck (L, this != NULL, i, "thread expected");
525 luaL_argcheck (L, *this != NULL, i, "thread expected");
526 // luaL_argcheck (L, !(counter->flags & FLAG_DESTROYED), 1, "PDH Counter is destroyed");
527 return *this;
528}
529
530static int l_llthread_delete(lua_State *L) {
531 llthread_t **pthis = (llthread_t **)lutil_checkudatap (L, 1, LLTHREAD_TAG);
532 luaL_argcheck (L, pthis != NULL, 1, "thread expected");
533 if(*pthis == NULL) return 0;
534 llthread_destroy(*pthis);
535 *pthis = NULL;
536
537 return 0;
538}
539
540static int l_llthread_start(lua_State *L) {
541 llthread_t *this = l_llthread_at(L, 1);
542 int start_detached = lua_toboolean(L, 2);
543 int joinable, rc;
544
545 if(!lua_isnone(L, 3)) joinable = lua_toboolean(L, 3);
546 else joinable = start_detached ? 0 : 1;
547
548 if(IS(this, STARTED)) {
549 return fail(L, "Thread already started.");
550 }
551
552 rc = llthread_start(this, start_detached, joinable);
553 if(rc != 0) {
554 char buf[ERROR_LEN];
555 strerror_r(errno, buf, ERROR_LEN);
556 return fail(L, buf);
557 }
558
559 lua_settop(L, 1); // return this
560 return 1;
561}
562
563static int l_llthread_join(lua_State *L) {
564 llthread_t *this = l_llthread_at(L, 1);
565 llthread_child_t *child = this->child;
566 int rc;
567
568 if(!IS(this, STARTED )) {
569 return fail(L, "Can't join a thread that hasn't be started.");
570 }
571 if( IS(this, DETACHED) && !IS(this, JOINABLE)) {
572 return fail(L, "Can't join a thread that has been detached.");
573 }
574 if( IS(this, JOINED )) {
575 return fail(L, "Can't join a thread that has already been joined.");
576 }
577
578 /* join the thread. */
579 rc = llthread_join(this, luaL_optint(L, 2, INFINITE_JOIN_TIMEOUT));
580
581 if(child && IS(this, JOINED)) {
582 int top;
583
584 if(IS(this, DETACHED) || !IS(this, JOINABLE)){
585 /*child lua state has been destroyed by child thread*/
586 /*@todo return thread exit code*/
587 lua_pushboolean(L, 1);
588 lua_pushnumber(L, 0);
589 return 2;
590 }
591
592 /* copy values from child lua state */
593 if(child->status != 0) {
594 const char *err_msg = lua_tostring(child->L, -1);
595 lua_pushboolean(L, 0);
596 lua_pushfstring(L, "Error from child thread: %s", err_msg);
597 top = 2;
598 } else {
599 lua_pushboolean(L, 1);
600 top = lua_gettop(child->L);
601 /* return results to parent thread. */
602 llthread_push_results(L, child, 2, top);
603 }
604
605 llthread_cleanup_child(this);
606 return top;
607 }
608
609 if( rc == JOIN_ETIMEDOUT ){
610 return fail(L, "timeout");
611 }
612
613 {
614 char buf[ERROR_LEN];
615 strerror_r(errno, buf, ERROR_LEN);
616
617 /* llthread_cleanup_child(this); */
618
619 return fail(L, buf);
620 }
621
622}
623
624static int l_llthread_alive(lua_State *L) {
625 llthread_t *this = l_llthread_at(L, 1);
626 llthread_child_t *child = this->child;
627 int rc;
628
629 if(!IS(this, STARTED )) {
630 return fail(L, "Can't join a thread that hasn't be started.");
631 }
632 if( IS(this, DETACHED) && !IS(this, JOINABLE)) {
633 return fail(L, "Can't join a thread that has been detached.");
634 }
635 if( IS(this, JOINED )) {
636 return fail(L, "Can't join a thread that has already been joined.");
637 }
638
639 /* join the thread. */
640 rc = llthread_alive(this);
641
642 if( rc == JOIN_ETIMEDOUT ){
643 lua_pushboolean(L, 1);
644 return 1;
645 }
646
647 if(rc == JOIN_OK){
648 lua_pushboolean(L, 0);
649 return 1;
650 }
651
652 {
653 char buf[ERROR_LEN];
654 strerror_r(errno, buf, ERROR_LEN);
655
656 /* llthread_cleanup_child(this); */
657
658 return fail(L, buf);
659 }
660
661}
662
663static int l_llthread_new(lua_State *L) {
664 size_t lua_code_len; const char *lua_code = luaL_checklstring(L, 1, &lua_code_len);
665 llthread_t **this = lutil_newudatap(L, llthread_t*, LLTHREAD_TAG);
666 lua_insert(L, 2); /*move self prior args*/
667 *this = llthread_create(L, lua_code, lua_code_len);
668
669 lua_settop(L, 2);
670 return 1;
671}
672
673static const struct luaL_Reg l_llthread_meth[] = {
674 {"start", l_llthread_start },
675 {"join", l_llthread_join },
676 {"alive", l_llthread_alive },
677 {"__gc", l_llthread_delete },
678
679 {NULL, NULL}
680};
681
682//}
683
684//{ version
685
686static int l_llthread_version(lua_State *L){
687 lua_pushnumber(L, LLTHREAD_VERSION_MAJOR);
688 lua_pushnumber(L, LLTHREAD_VERSION_MINOR);
689 lua_pushnumber(L, LLTHREAD_VERSION_PATCH);
690#ifdef LLTHREAD_VERSION_COMMENT
691 if(LLTHREAD_VERSION_COMMENT[0]){
692 lua_pushliteral(L, LLTHREAD_VERSION_COMMENT);
693 return 4;
694 }
695#endif
696 return 3;
697}
698
699static int l_llthread_push_version(lua_State *L){
700 lua_pushnumber(L, LLTHREAD_VERSION_MAJOR);
701 lua_pushliteral(L, ".");
702 lua_pushnumber(L, LLTHREAD_VERSION_MINOR);
703 lua_pushliteral(L, ".");
704 lua_pushnumber(L, LLTHREAD_VERSION_PATCH);
705#ifdef LLTHREAD_VERSION_COMMENT
706 if(LLTHREAD_VERSION_COMMENT[0]){
707 lua_pushliteral(L, "-"LLTHREAD_VERSION_COMMENT);
708 lua_concat(L, 6);
709 }
710 else
711#endif
712 lua_concat(L, 5);
713 return 1;
714}
715
716//}
717
718static int l_llthread_set_logger(lua_State *L){
719 lua_settop(L, 1);
720 luaL_argcheck(L, lua_isfunction(L, 1), 1, "function expected");
721 lua_rawsetp(L, LUA_REGISTRYINDEX, LLTHREAD_LOGGER_HOLDER);
722 return 0;
723}
724
725static const struct luaL_Reg l_llthreads_lib[] = {
726 {"new", l_llthread_new },
727 {"set_logger", l_llthread_set_logger },
728 {"version", l_llthread_version },
729
730 {NULL, NULL}
731};
732
733LLTHREADS_EXPORT_API int LLTHREAD_OPEN_NAME(lua_State *L) {
734 int top = lua_gettop(L);
735 lutil_createmetap(L, LLTHREAD_TAG, l_llthread_meth, 0);
736 lua_settop(L, top);
737
738 lua_newtable(L);
739 luaL_setfuncs(L, l_llthreads_lib, 0);
740
741 lua_pushliteral(L, "_VERSION");
742 l_llthread_push_version(L);
743 lua_rawset(L, -3);
744
745 return 1;
746}
diff --git a/llthreads2/src/traceback.inc b/llthreads2/src/traceback.inc
new file mode 100644
index 0000000..af2f5a1
--- /dev/null
+++ b/llthreads2/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