aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBenoit Germain <bnt.germain@gmail.com>2022-02-07 08:56:39 +0100
committerBenoit Germain <bnt.germain@gmail.com>2022-02-07 08:56:39 +0100
commit621fb024b5f887ef9e81e2f28bf087386f5300e1 (patch)
tree3ed681e2db07a0516904b2cda4e7144c714d19e0
parent00c84df3adc0b295ef20bc35bc8df9632e3b93e7 (diff)
downloadlanes-621fb024b5f887ef9e81e2f28bf087386f5300e1.tar.gz
lanes-621fb024b5f887ef9e81e2f28bf087386f5300e1.tar.bz2
lanes-621fb024b5f887ef9e81e2f28bf087386f5300e1.zip
Changed all indentations to all whitespaces
Tabs mess up alignment of stack contents comments, so I'm done with them.
-rw-r--r--src/cancel.c334
-rw-r--r--src/cancel.h32
-rw-r--r--src/compat.c102
-rw-r--r--src/deep.c654
-rw-r--r--src/deep.h24
-rw-r--r--src/keeper.c1208
-rw-r--r--src/keeper.h10
-rw-r--r--src/lanes.c2886
-rw-r--r--src/lanes.lua1380
-rw-r--r--src/lanes_private.h110
-rw-r--r--src/linda.c1380
-rw-r--r--src/macros_and_utils.h66
-rw-r--r--src/state.c554
-rw-r--r--src/threading.c910
-rw-r--r--src/threading.h67
-rw-r--r--src/tools.c3308
-rw-r--r--src/tools.h6
-rw-r--r--src/uniquekey.h2
-rw-r--r--src/universe.c34
-rw-r--r--src/universe.h64
20 files changed, 6564 insertions, 6567 deletions
diff --git a/src/cancel.c b/src/cancel.c
index cd930b5..0a5adb6 100644
--- a/src/cancel.c
+++ b/src/cancel.c
@@ -55,9 +55,9 @@ THE SOFTWARE.
55*/ 55*/
56static inline enum e_cancel_request cancel_test( lua_State* L) 56static inline enum e_cancel_request cancel_test( lua_State* L)
57{ 57{
58 Lane* const s = get_lane_from_registry( L); 58 Lane* const s = get_lane_from_registry( L);
59 // 's' is NULL for the original main state (and no-one can cancel that) 59 // 's' is NULL for the original main state (and no-one can cancel that)
60 return s ? s->cancel_request : CANCEL_NONE; 60 return s ? s->cancel_request : CANCEL_NONE;
61} 61}
62 62
63// ################################################################################################ 63// ################################################################################################
@@ -70,9 +70,9 @@ static inline enum e_cancel_request cancel_test( lua_State* L)
70// 70//
71LUAG_FUNC( cancel_test) 71LUAG_FUNC( cancel_test)
72{ 72{
73 enum e_cancel_request test = cancel_test( L); 73 enum e_cancel_request test = cancel_test( L);
74 lua_pushboolean( L, test != CANCEL_NONE); 74 lua_pushboolean( L, test != CANCEL_NONE);
75 return 1; 75 return 1;
76} 76}
77 77
78// ################################################################################################ 78// ################################################################################################
@@ -80,13 +80,13 @@ LUAG_FUNC( cancel_test)
80 80
81static void cancel_hook( lua_State* L, lua_Debug* ar) 81static void cancel_hook( lua_State* L, lua_Debug* ar)
82{ 82{
83 (void)ar; 83 (void)ar;
84 DEBUGSPEW_CODE( fprintf( stderr, "cancel_hook\n")); 84 DEBUGSPEW_CODE( fprintf( stderr, "cancel_hook\n"));
85 if( cancel_test( L) != CANCEL_NONE) 85 if( cancel_test( L) != CANCEL_NONE)
86 { 86 {
87 lua_sethook( L, NULL, 0, 0); 87 lua_sethook( L, NULL, 0, 0);
88 cancel_error( L); 88 cancel_error( L);
89 } 89 }
90} 90}
91 91
92// ################################################################################################ 92// ################################################################################################
@@ -114,90 +114,90 @@ static void cancel_hook( lua_State* L, lua_Debug* ar)
114 114
115static cancel_result thread_cancel_soft( Lane* s, double secs_, bool_t wake_lindas_) 115static cancel_result thread_cancel_soft( Lane* s, double secs_, bool_t wake_lindas_)
116{ 116{
117 s->cancel_request = CANCEL_SOFT; // it's now signaled to stop 117 s->cancel_request = CANCEL_SOFT; // it's now signaled to stop
118 // negative timeout: we don't want to truly abort the lane, we just want it to react to cancel_test() on its own 118 // negative timeout: we don't want to truly abort the lane, we just want it to react to cancel_test() on its own
119 if( wake_lindas_) // wake the thread so that execution returns from any pending linda operation if desired 119 if( wake_lindas_) // wake the thread so that execution returns from any pending linda operation if desired
120 { 120 {
121 SIGNAL_T *waiting_on = s->waiting_on; 121 SIGNAL_T *waiting_on = s->waiting_on;
122 if( s->status == WAITING && waiting_on != NULL) 122 if( s->status == WAITING && waiting_on != NULL)
123 { 123 {
124 SIGNAL_ALL( waiting_on); 124 SIGNAL_ALL( waiting_on);
125 } 125 }
126 } 126 }
127 127
128 return THREAD_WAIT( &s->thread, secs_, &s->done_signal, &s->done_lock, &s->status) ? CR_Cancelled : CR_Timeout; 128 return THREAD_WAIT( &s->thread, secs_, &s->done_signal, &s->done_lock, &s->status) ? CR_Cancelled : CR_Timeout;
129} 129}
130 130
131// ################################################################################################ 131// ################################################################################################
132 132
133static cancel_result thread_cancel_hard( lua_State* L, Lane* s, double secs_, bool_t force_, double waitkill_timeout_) 133static cancel_result thread_cancel_hard( lua_State* L, Lane* s, double secs_, bool_t force_, double waitkill_timeout_)
134{ 134{
135 cancel_result result; 135 cancel_result result;
136 136
137 s->cancel_request = CANCEL_HARD; // it's now signaled to stop 137 s->cancel_request = CANCEL_HARD; // it's now signaled to stop
138 { 138 {
139 SIGNAL_T *waiting_on = s->waiting_on; 139 SIGNAL_T *waiting_on = s->waiting_on;
140 if( s->status == WAITING && waiting_on != NULL) 140 if( s->status == WAITING && waiting_on != NULL)
141 { 141 {
142 SIGNAL_ALL( waiting_on); 142 SIGNAL_ALL( waiting_on);
143 } 143 }
144 } 144 }
145 145
146 result = THREAD_WAIT( &s->thread, secs_, &s->done_signal, &s->done_lock, &s->status) ? CR_Cancelled : CR_Timeout; 146 result = THREAD_WAIT( &s->thread, secs_, &s->done_signal, &s->done_lock, &s->status) ? CR_Cancelled : CR_Timeout;
147 147
148 if( (result == CR_Timeout) && force_) 148 if( (result == CR_Timeout) && force_)
149 { 149 {
150 // Killing is asynchronous; we _will_ wait for it to be done at 150 // Killing is asynchronous; we _will_ wait for it to be done at
151 // GC, to make sure the data structure can be released (alternative 151 // GC, to make sure the data structure can be released (alternative
152 // would be use of "cancellation cleanup handlers" that at least 152 // would be use of "cancellation cleanup handlers" that at least
153 // PThread seems to have). 153 // PThread seems to have).
154 // 154 //
155 THREAD_KILL( &s->thread); 155 THREAD_KILL( &s->thread);
156#if THREADAPI == THREADAPI_PTHREAD 156#if THREADAPI == THREADAPI_PTHREAD
157 // pthread: make sure the thread is really stopped! 157 // pthread: make sure the thread is really stopped!
158 // note that this may block forever if the lane doesn't call a cancellation point and pthread doesn't honor PTHREAD_CANCEL_ASYNCHRONOUS 158 // note that this may block forever if the lane doesn't call a cancellation point and pthread doesn't honor PTHREAD_CANCEL_ASYNCHRONOUS
159 result = THREAD_WAIT( &s->thread, waitkill_timeout_, &s->done_signal, &s->done_lock, &s->status); 159 result = THREAD_WAIT( &s->thread, waitkill_timeout_, &s->done_signal, &s->done_lock, &s->status);
160 if( result == CR_Timeout) 160 if( result == CR_Timeout)
161 { 161 {
162 return luaL_error( L, "force-killed lane failed to terminate within %f second%s", waitkill_timeout_, waitkill_timeout_ > 1 ? "s" : ""); 162 return luaL_error( L, "force-killed lane failed to terminate within %f second%s", waitkill_timeout_, waitkill_timeout_ > 1 ? "s" : "");
163 } 163 }
164#else 164#else
165 (void) waitkill_timeout_; // unused 165 (void) waitkill_timeout_; // unused
166 (void) L; // unused 166 (void) L; // unused
167#endif // THREADAPI == THREADAPI_PTHREAD 167#endif // THREADAPI == THREADAPI_PTHREAD
168 s->mstatus = KILLED; // mark 'gc' to wait for it 168 s->mstatus = KILLED; // mark 'gc' to wait for it
169 // note that s->status value must remain to whatever it was at the time of the kill 169 // note that s->status value must remain to whatever it was at the time of the kill
170 // because we need to know if we can lua_close() the Lua State or not. 170 // because we need to know if we can lua_close() the Lua State or not.
171 result = CR_Killed; 171 result = CR_Killed;
172 } 172 }
173 return result; 173 return result;
174} 174}
175 175
176// ################################################################################################ 176// ################################################################################################
177 177
178cancel_result thread_cancel( lua_State* L, Lane* s, CancelOp op_, double secs_, bool_t force_, double waitkill_timeout_) 178cancel_result thread_cancel( lua_State* L, Lane* s, CancelOp op_, double secs_, bool_t force_, double waitkill_timeout_)
179{ 179{
180 // remember that lanes are not transferable: only one thread can cancel a lane, so no multithreading issue here 180 // remember that lanes are not transferable: only one thread can cancel a lane, so no multithreading issue here
181 // We can read 's->status' without locks, but not wait for it (if Posix no PTHREAD_TIMEDJOIN) 181 // We can read 's->status' without locks, but not wait for it (if Posix no PTHREAD_TIMEDJOIN)
182 if( s->mstatus == KILLED) 182 if( s->mstatus == KILLED)
183 { 183 {
184 return CR_Killed; 184 return CR_Killed;
185 } 185 }
186 186
187 if( s->status >= DONE) 187 if( s->status >= DONE)
188 { 188 {
189 // say "ok" by default, including when lane is already done 189 // say "ok" by default, including when lane is already done
190 return CR_Cancelled; 190 return CR_Cancelled;
191 } 191 }
192 192
193 // signal the linda the wake up the thread so that it can react to the cancel query 193 // signal the linda the wake up the thread so that it can react to the cancel query
194 // let us hope we never land here with a pointer on a linda that has been destroyed... 194 // let us hope we never land here with a pointer on a linda that has been destroyed...
195 if( op_ == CO_Soft) 195 if( op_ == CO_Soft)
196 { 196 {
197 return thread_cancel_soft( s, secs_, force_); 197 return thread_cancel_soft( s, secs_, force_);
198 } 198 }
199 199
200 return thread_cancel_hard( L, s, secs_, force_, waitkill_timeout_); 200 return thread_cancel_hard( L, s, secs_, force_, waitkill_timeout_);
201} 201}
202 202
203// ################################################################################################ 203// ################################################################################################
@@ -208,95 +208,95 @@ cancel_result thread_cancel( lua_State* L, Lane* s, CancelOp op_, double secs_,
208// < 0: hard 208// < 0: hard
209static CancelOp which_op( lua_State* L, int idx_) 209static CancelOp which_op( lua_State* L, int idx_)
210{ 210{
211 if( lua_type( L, idx_) == LUA_TSTRING) 211 if( lua_type( L, idx_) == LUA_TSTRING)
212 { 212 {
213 CancelOp op = CO_Invalid; 213 CancelOp op = CO_Invalid;
214 char const* str = lua_tostring( L, idx_); 214 char const* str = lua_tostring( L, idx_);
215 if( strcmp( str, "soft") == 0) 215 if( strcmp( str, "soft") == 0)
216 { 216 {
217 op = CO_Soft; 217 op = CO_Soft;
218 } 218 }
219 else if( strcmp( str, "count") == 0) 219 else if( strcmp( str, "count") == 0)
220 { 220 {
221 op = CO_Count; 221 op = CO_Count;
222 } 222 }
223 else if( strcmp( str, "line") == 0) 223 else if( strcmp( str, "line") == 0)
224 { 224 {
225 op = CO_Line; 225 op = CO_Line;
226 } 226 }
227 else if( strcmp( str, "call") == 0) 227 else if( strcmp( str, "call") == 0)
228 { 228 {
229 op = CO_Call; 229 op = CO_Call;
230 } 230 }
231 else if( strcmp( str, "ret") == 0) 231 else if( strcmp( str, "ret") == 0)
232 { 232 {
233 op = CO_Ret; 233 op = CO_Ret;
234 } 234 }
235 else if( strcmp( str, "hard") == 0) 235 else if( strcmp( str, "hard") == 0)
236 { 236 {
237 op = CO_Hard; 237 op = CO_Hard;
238 } 238 }
239 lua_remove( L, idx_); // argument is processed, remove it 239 lua_remove( L, idx_); // argument is processed, remove it
240 if( op == CO_Invalid) 240 if( op == CO_Invalid)
241 { 241 {
242 luaL_error( L, "invalid hook option %s", str); 242 luaL_error( L, "invalid hook option %s", str);
243 } 243 }
244 return op; 244 return op;
245 } 245 }
246 return CO_Hard; 246 return CO_Hard;
247} 247}
248// ################################################################################################ 248// ################################################################################################
249 249
250// bool[,reason] = lane_h:cancel( [mode, hookcount] [, timeout] [, force [, forcekill_timeout]]) 250// bool[,reason] = lane_h:cancel( [mode, hookcount] [, timeout] [, force [, forcekill_timeout]])
251LUAG_FUNC( thread_cancel) 251LUAG_FUNC( thread_cancel)
252{ 252{
253 Lane* s = lua_toLane( L, 1); 253 Lane* s = lua_toLane( L, 1);
254 double secs = 0.0; 254 double secs = 0.0;
255 CancelOp op = which_op( L, 2); // this removes the op string from the stack 255 CancelOp op = which_op( L, 2); // this removes the op string from the stack
256 256
257 if( op > 0) // hook is requested 257 if( op > 0) // hook is requested
258 { 258 {
259 int hook_count = (int) lua_tointeger( L, 2); 259 int hook_count = (int) lua_tointeger( L, 2);
260 lua_remove( L, 2); // argument is processed, remove it 260 lua_remove( L, 2); // argument is processed, remove it
261 if( hook_count < 1) 261 if( hook_count < 1)
262 { 262 {
263 return luaL_error( L, "hook count cannot be < 1"); 263 return luaL_error( L, "hook count cannot be < 1");
264 } 264 }
265 lua_sethook( s->L, cancel_hook, op, hook_count); 265 lua_sethook( s->L, cancel_hook, op, hook_count);
266 } 266 }
267 267
268 if( lua_type( L, 2) == LUA_TNUMBER) 268 if( lua_type( L, 2) == LUA_TNUMBER)
269 { 269 {
270 secs = lua_tonumber( L, 2); 270 secs = lua_tonumber( L, 2);
271 lua_remove( L, 2); // argument is processed, remove it 271 lua_remove( L, 2); // argument is processed, remove it
272 if( secs < 0.0) 272 if( secs < 0.0)
273 { 273 {
274 return luaL_error( L, "cancel timeout cannot be < 0"); 274 return luaL_error( L, "cancel timeout cannot be < 0");
275 } 275 }
276 } 276 }
277 277
278 { 278 {
279 bool_t force = lua_toboolean( L, 2); // FALSE if nothing there 279 bool_t force = lua_toboolean( L, 2); // FALSE if nothing there
280 double forcekill_timeout = luaL_optnumber( L, 3, 0.0); 280 double forcekill_timeout = luaL_optnumber( L, 3, 0.0);
281 281
282 switch( thread_cancel( L, s, op, secs, force, forcekill_timeout)) 282 switch( thread_cancel( L, s, op, secs, force, forcekill_timeout))
283 { 283 {
284 case CR_Timeout: 284 case CR_Timeout:
285 lua_pushboolean( L, 0); 285 lua_pushboolean( L, 0);
286 lua_pushstring( L, "timeout"); 286 lua_pushstring( L, "timeout");
287 return 2; 287 return 2;
288 288
289 case CR_Cancelled: 289 case CR_Cancelled:
290 lua_pushboolean( L, 1); 290 lua_pushboolean( L, 1);
291 push_thread_status( L, s); 291 push_thread_status( L, s);
292 return 2; 292 return 2;
293 293
294 case CR_Killed: 294 case CR_Killed:
295 lua_pushboolean( L, 1); 295 lua_pushboolean( L, 1);
296 push_thread_status( L, s); 296 push_thread_status( L, s);
297 return 2; 297 return 2;
298 } 298 }
299 } 299 }
300 // should never happen, only here to prevent the compiler from complaining of "not all control paths returning a value" 300 // should never happen, only here to prevent the compiler from complaining of "not all control paths returning a value"
301 return 0; 301 return 0;
302} 302}
diff --git a/src/cancel.h b/src/cancel.h
index e7656ac..c7c5433 100644
--- a/src/cancel.h
+++ b/src/cancel.h
@@ -17,27 +17,27 @@ typedef struct s_Lane Lane; // forward
17 */ 17 */
18enum e_cancel_request 18enum e_cancel_request
19{ 19{
20 CANCEL_NONE, // no pending cancel request 20 CANCEL_NONE, // no pending cancel request
21 CANCEL_SOFT, // user wants the lane to cancel itself manually on cancel_test() 21 CANCEL_SOFT, // user wants the lane to cancel itself manually on cancel_test()
22 CANCEL_HARD // user wants the lane to be interrupted (meaning code won't return from those functions) from inside linda:send/receive calls 22 CANCEL_HARD // user wants the lane to be interrupted (meaning code won't return from those functions) from inside linda:send/receive calls
23}; 23};
24 24
25typedef enum 25typedef enum
26{ 26{
27 CR_Timeout, 27 CR_Timeout,
28 CR_Cancelled, 28 CR_Cancelled,
29 CR_Killed 29 CR_Killed
30} cancel_result; 30} cancel_result;
31 31
32typedef enum 32typedef enum
33{ 33{
34 CO_Invalid = -2, 34 CO_Invalid = -2,
35 CO_Hard = -1, 35 CO_Hard = -1,
36 CO_Soft = 0, 36 CO_Soft = 0,
37 CO_Count = LUA_MASKCOUNT, 37 CO_Count = LUA_MASKCOUNT,
38 CO_Line = LUA_MASKLINE, 38 CO_Line = LUA_MASKLINE,
39 CO_Call = LUA_MASKCALL, 39 CO_Call = LUA_MASKCALL,
40 CO_Ret = LUA_MASKRET, 40 CO_Ret = LUA_MASKRET,
41} CancelOp; 41} CancelOp;
42 42
43// crc64/we of string "CANCEL_ERROR" generated at http://www.nitrxgen.net/hashgen/ 43// crc64/we of string "CANCEL_ERROR" generated at http://www.nitrxgen.net/hashgen/
@@ -50,9 +50,9 @@ cancel_result thread_cancel( lua_State* L, Lane* s, CancelOp op_, double secs_,
50 50
51static inline int cancel_error( lua_State* L) 51static inline int cancel_error( lua_State* L)
52{ 52{
53 STACK_GROW( L, 1); 53 STACK_GROW( L, 1);
54 push_unique_key( L, CANCEL_ERROR); // special error value 54 push_unique_key( L, CANCEL_ERROR); // special error value
55 return lua_error( L); // doesn't return 55 return lua_error( L); // doesn't return
56} 56}
57 57
58// ################################################################################################ 58// ################################################################################################
diff --git a/src/compat.c b/src/compat.c
index d9bc3dd..19159a9 100644
--- a/src/compat.c
+++ b/src/compat.c
@@ -12,34 +12,34 @@
12#if LUA_VERSION_NUM == 501 12#if LUA_VERSION_NUM == 501
13static int luaL_getsubtable (lua_State *L, int idx, const char *fname) 13static int luaL_getsubtable (lua_State *L, int idx, const char *fname)
14{ 14{
15 lua_getfield(L, idx, fname); 15 lua_getfield(L, idx, fname);
16 if (lua_istable(L, -1)) 16 if (lua_istable(L, -1))
17 return 1; /* table already there */ 17 return 1; /* table already there */
18 else 18 else
19 { 19 {
20 lua_pop(L, 1); /* remove previous result */ 20 lua_pop(L, 1); /* remove previous result */
21 idx = lua_absindex(L, idx); 21 idx = lua_absindex(L, idx);
22 lua_newtable(L); 22 lua_newtable(L);
23 lua_pushvalue(L, -1); /* copy to be left at top */ 23 lua_pushvalue(L, -1); /* copy to be left at top */
24 lua_setfield(L, idx, fname); /* assign new table to field */ 24 lua_setfield(L, idx, fname); /* assign new table to field */
25 return 0; /* false, because did not find table there */ 25 return 0; /* false, because did not find table there */
26 } 26 }
27} 27}
28 28
29void luaL_requiref (lua_State *L, const char *modname, lua_CFunction openf, int glb) 29void luaL_requiref (lua_State *L, const char *modname, lua_CFunction openf, int glb)
30{ 30{
31 lua_pushcfunction(L, openf); 31 lua_pushcfunction(L, openf);
32 lua_pushstring(L, modname); /* argument to open function */ 32 lua_pushstring(L, modname); /* argument to open function */
33 lua_call(L, 1, 1); /* open module */ 33 lua_call(L, 1, 1); /* open module */
34 luaL_getsubtable(L, LUA_REGISTRYINDEX, LUA_LOADED_TABLE); 34 luaL_getsubtable(L, LUA_REGISTRYINDEX, LUA_LOADED_TABLE);
35 lua_pushvalue(L, -2); /* make copy of module (call result) */ 35 lua_pushvalue(L, -2); /* make copy of module (call result) */
36 lua_setfield(L, -2, modname); /* _LOADED[modname] = module */ 36 lua_setfield(L, -2, modname); /* _LOADED[modname] = module */
37 lua_pop(L, 1); /* remove _LOADED table */ 37 lua_pop(L, 1); /* remove _LOADED table */
38 if (glb) 38 if (glb)
39 { 39 {
40 lua_pushvalue(L, -1); /* copy of 'mod' */ 40 lua_pushvalue(L, -1); /* copy of 'mod' */
41 lua_setglobal(L, modname); /* _G[modname] = module */ 41 lua_setglobal(L, modname); /* _G[modname] = module */
42 } 42 }
43} 43}
44#endif // LUA_VERSION_NUM 44#endif // LUA_VERSION_NUM
45 45
@@ -47,49 +47,49 @@ void luaL_requiref (lua_State *L, const char *modname, lua_CFunction openf, int
47 47
48void* lua_newuserdatauv( lua_State* L, size_t sz, int nuvalue) 48void* lua_newuserdatauv( lua_State* L, size_t sz, int nuvalue)
49{ 49{
50 ASSERT_L( nuvalue <= 1); 50 ASSERT_L( nuvalue <= 1);
51 return lua_newuserdata( L, sz); 51 return lua_newuserdata( L, sz);
52} 52}
53 53
54int lua_getiuservalue( lua_State* L, int idx, int n) 54int lua_getiuservalue( lua_State* L, int idx, int n)
55{ 55{
56 if( n > 1) 56 if( n > 1)
57 { 57 {
58 lua_pushnil( L); 58 lua_pushnil( L);
59 return LUA_TNONE; 59 return LUA_TNONE;
60 } 60 }
61 lua_getuservalue( L, idx); 61 lua_getuservalue( L, idx);
62 62
63#if LUA_VERSION_NUM == 501 63#if LUA_VERSION_NUM == 501
64 /* default environment is not a nil (see lua_getfenv) */ 64 /* default environment is not a nil (see lua_getfenv) */
65 lua_getglobal(L, "package"); 65 lua_getglobal(L, "package");
66 if (lua_rawequal(L, -2, -1) || lua_rawequal(L, -2, LUA_GLOBALSINDEX)) 66 if (lua_rawequal(L, -2, -1) || lua_rawequal(L, -2, LUA_GLOBALSINDEX))
67 { 67 {
68 lua_pop(L, 2); 68 lua_pop(L, 2);
69 lua_pushnil( L); 69 lua_pushnil( L);
70 70
71 return LUA_TNONE; 71 return LUA_TNONE;
72 } 72 }
73 lua_pop(L, 1); /* remove package */ 73 lua_pop(L, 1); /* remove package */
74#endif 74#endif
75 75
76 return lua_type( L, -1); 76 return lua_type( L, -1);
77} 77}
78 78
79int lua_setiuservalue( lua_State* L, int idx, int n) 79int lua_setiuservalue( lua_State* L, int idx, int n)
80{ 80{
81 if( n > 1 81 if( n > 1
82#if LUA_VERSION_NUM == 501 82#if LUA_VERSION_NUM == 501
83 || lua_type( L, -1) != LUA_TTABLE 83 || lua_type( L, -1) != LUA_TTABLE
84#endif 84#endif
85 ) 85 )
86 { 86 {
87 lua_pop( L, 1); 87 lua_pop( L, 1);
88 return 0; 88 return 0;
89 } 89 }
90 90
91 (void) lua_setuservalue( L, idx); 91 (void) lua_setuservalue( L, idx);
92 return 1; // I guess anything non-0 is ok 92 return 1; // I guess anything non-0 is ok
93} 93}
94 94
95#endif // LUA_VERSION_NUM 95#endif // LUA_VERSION_NUM
diff --git a/src/deep.c b/src/deep.c
index 3c7680d..c475dc5 100644
--- a/src/deep.c
+++ b/src/deep.c
@@ -73,17 +73,17 @@ static DECLARE_CONST_UNIQUE_KEY( DEEP_PROXY_CACHE_KEY, 0x05773d6fc26be106);
73*/ 73*/
74static void set_deep_lookup( lua_State* L) 74static void set_deep_lookup( lua_State* L)
75{ 75{
76 STACK_GROW( L, 3); 76 STACK_GROW( L, 3);
77 STACK_CHECK( L, 2); // a b 77 STACK_CHECK( L, 2); // a b
78 push_registry_subtable( L, DEEP_LOOKUP_KEY); // a b {} 78 push_registry_subtable( L, DEEP_LOOKUP_KEY); // a b {}
79 STACK_MID( L, 3); 79 STACK_MID( L, 3);
80 lua_insert( L, -3); // {} a b 80 lua_insert( L, -3); // {} a b
81 lua_pushvalue( L, -1); // {} a b b 81 lua_pushvalue( L, -1); // {} a b b
82 lua_pushvalue( L,-3); // {} a b b a 82 lua_pushvalue( L,-3); // {} a b b a
83 lua_rawset( L, -5); // {} a b 83 lua_rawset( L, -5); // {} a b
84 lua_rawset( L, -3); // {} 84 lua_rawset( L, -3); // {}
85 lua_pop( L, 1); // 85 lua_pop( L, 1); //
86 STACK_END( L, 0); 86 STACK_END( L, 0);
87} 87}
88 88
89/* 89/*
@@ -92,16 +92,16 @@ static void set_deep_lookup( lua_State* L)
92*/ 92*/
93static void get_deep_lookup( lua_State* L) 93static void get_deep_lookup( lua_State* L)
94{ 94{
95 STACK_GROW( L, 1); 95 STACK_GROW( L, 1);
96 STACK_CHECK( L, 1); // a 96 STACK_CHECK( L, 1); // a
97 REGISTRY_GET( L, DEEP_LOOKUP_KEY); // a {} 97 REGISTRY_GET( L, DEEP_LOOKUP_KEY); // a {}
98 if( !lua_isnil( L, -1)) 98 if( !lua_isnil( L, -1))
99 { 99 {
100 lua_insert( L, -2); // {} a 100 lua_insert( L, -2); // {} a
101 lua_rawget( L, -2); // {} b 101 lua_rawget( L, -2); // {} b
102 } 102 }
103 lua_remove( L, -2); // a|b 103 lua_remove( L, -2); // a|b
104 STACK_END( L, 1); 104 STACK_END( L, 1);
105} 105}
106 106
107/* 107/*
@@ -110,44 +110,44 @@ static void get_deep_lookup( lua_State* L)
110*/ 110*/
111static inline luaG_IdFunction get_idfunc( lua_State* L, int index, LookupMode mode_) 111static inline luaG_IdFunction get_idfunc( lua_State* L, int index, LookupMode mode_)
112{ 112{
113 // when looking inside a keeper, we are 100% sure the object is a deep userdata 113 // when looking inside a keeper, we are 100% sure the object is a deep userdata
114 if( mode_ == eLM_FromKeeper) 114 if( mode_ == eLM_FromKeeper)
115 { 115 {
116 DeepPrelude** proxy = (DeepPrelude**) lua_touserdata( L, index); 116 DeepPrelude** proxy = (DeepPrelude**) lua_touserdata( L, index);
117 // we can (and must) cast and fetch the internally stored idfunc 117 // we can (and must) cast and fetch the internally stored idfunc
118 return (*proxy)->idfunc; 118 return (*proxy)->idfunc;
119 } 119 }
120 else 120 else
121 { 121 {
122 // essentially we are making sure that the metatable of the object we want to copy is stored in our metatable/idfunc database 122 // essentially we are making sure that the metatable of the object we want to copy is stored in our metatable/idfunc database
123 // it is the only way to ensure that the userdata is indeed a deep userdata! 123 // it is the only way to ensure that the userdata is indeed a deep userdata!
124 // of course, we could just trust the caller, but we won't 124 // of course, we could just trust the caller, but we won't
125 luaG_IdFunction ret; 125 luaG_IdFunction ret;
126 STACK_GROW( L, 1); 126 STACK_GROW( L, 1);
127 STACK_CHECK( L, 0); 127 STACK_CHECK( L, 0);
128 128
129 if( !lua_getmetatable( L, index)) // deep ... metatable? 129 if( !lua_getmetatable( L, index)) // deep ... metatable?
130 { 130 {
131 return NULL; // no metatable: can't be a deep userdata object! 131 return NULL; // no metatable: can't be a deep userdata object!
132 } 132 }
133 133
134 // replace metatable with the idfunc pointer, if it is actually a deep userdata 134 // replace metatable with the idfunc pointer, if it is actually a deep userdata
135 get_deep_lookup( L); // deep ... idfunc|nil 135 get_deep_lookup( L); // deep ... idfunc|nil
136 136
137 ret = (luaG_IdFunction) lua_touserdata( L, -1); // NULL if not a userdata 137 ret = (luaG_IdFunction) lua_touserdata( L, -1); // NULL if not a userdata
138 lua_pop( L, 1); 138 lua_pop( L, 1);
139 STACK_END( L, 0); 139 STACK_END( L, 0);
140 return ret; 140 return ret;
141 } 141 }
142} 142}
143 143
144 144
145void free_deep_prelude( lua_State* L, DeepPrelude* prelude_) 145void free_deep_prelude( lua_State* L, DeepPrelude* prelude_)
146{ 146{
147 // Call 'idfunc( "delete", deep_ptr )' to make deep cleanup 147 // Call 'idfunc( "delete", deep_ptr )' to make deep cleanup
148 lua_pushlightuserdata( L, prelude_); 148 lua_pushlightuserdata( L, prelude_);
149 ASSERT_L( prelude_->idfunc); 149 ASSERT_L( prelude_->idfunc);
150 prelude_->idfunc( L, eDO_delete); 150 prelude_->idfunc( L, eDO_delete);
151} 151}
152 152
153 153
@@ -159,38 +159,38 @@ void free_deep_prelude( lua_State* L, DeepPrelude* prelude_)
159 */ 159 */
160static int deep_userdata_gc( lua_State* L) 160static int deep_userdata_gc( lua_State* L)
161{ 161{
162 DeepPrelude** proxy = (DeepPrelude**) lua_touserdata( L, 1); 162 DeepPrelude** proxy = (DeepPrelude**) lua_touserdata( L, 1);
163 DeepPrelude* p = *proxy; 163 DeepPrelude* p = *proxy;
164 Universe* U = universe_get( L); 164 Universe* U = universe_get( L);
165 int v; 165 int v;
166 166
167 // can work without a universe if creating a deep userdata from some external C module when Lanes isn't loaded 167 // can work without a universe if creating a deep userdata from some external C module when Lanes isn't loaded
168 // in that case, we are not multithreaded and locking isn't necessary anyway 168 // in that case, we are not multithreaded and locking isn't necessary anyway
169 if( U) MUTEX_LOCK( &U->deep_lock); 169 if( U) MUTEX_LOCK( &U->deep_lock);
170 v = -- (p->refcount); 170 v = -- (p->refcount);
171 if (U) MUTEX_UNLOCK( &U->deep_lock); 171 if (U) MUTEX_UNLOCK( &U->deep_lock);
172 172
173 if( v == 0) 173 if( v == 0)
174 { 174 {
175 // retrieve wrapped __gc 175 // retrieve wrapped __gc
176 lua_pushvalue( L, lua_upvalueindex( 1)); // self __gc? 176 lua_pushvalue( L, lua_upvalueindex( 1)); // self __gc?
177 if( !lua_isnil( L, -1)) 177 if( !lua_isnil( L, -1))
178 { 178 {
179 lua_insert( L, -2); // __gc self 179 lua_insert( L, -2); // __gc self
180 lua_call( L, 1, 0); // 180 lua_call( L, 1, 0); //
181 } 181 }
182 // 'idfunc' expects a clean stack to work on 182 // 'idfunc' expects a clean stack to work on
183 lua_settop( L, 0); 183 lua_settop( L, 0);
184 free_deep_prelude( L, p); 184 free_deep_prelude( L, p);
185 185
186 // top was set to 0, then userdata was pushed. "delete" might want to pop the userdata (we don't care), but should not push anything! 186 // top was set to 0, then userdata was pushed. "delete" might want to pop the userdata (we don't care), but should not push anything!
187 if ( lua_gettop( L) > 1) 187 if ( lua_gettop( L) > 1)
188 { 188 {
189 luaL_error( L, "Bad idfunc(eDO_delete): should not push anything"); 189 luaL_error( L, "Bad idfunc(eDO_delete): should not push anything");
190 } 190 }
191 } 191 }
192 *proxy = NULL; // make sure we don't use it any more, just in case 192 *proxy = NULL; // make sure we don't use it any more, just in case
193 return 0; 193 return 0;
194} 194}
195 195
196 196
@@ -205,155 +205,155 @@ static int deep_userdata_gc( lua_State* L)
205 */ 205 */
206char const* push_deep_proxy( Universe* U, lua_State* L, DeepPrelude* prelude, int nuv_, LookupMode mode_) 206char const* push_deep_proxy( Universe* U, lua_State* L, DeepPrelude* prelude, int nuv_, LookupMode mode_)
207{ 207{
208 DeepPrelude** proxy; 208 DeepPrelude** proxy;
209 209
210 // Check if a proxy already exists 210 // Check if a proxy already exists
211 push_registry_subtable_mode( L, DEEP_PROXY_CACHE_KEY, "v"); // DPC 211 push_registry_subtable_mode( L, DEEP_PROXY_CACHE_KEY, "v"); // DPC
212 lua_pushlightuserdata( L, prelude); // DPC deep 212 lua_pushlightuserdata( L, prelude); // DPC deep
213 lua_rawget( L, -2); // DPC proxy 213 lua_rawget( L, -2); // DPC proxy
214 if ( !lua_isnil( L, -1)) 214 if ( !lua_isnil( L, -1))
215 { 215 {
216 lua_remove( L, -2); // proxy 216 lua_remove( L, -2); // proxy
217 return NULL; 217 return NULL;
218 } 218 }
219 else 219 else
220 { 220 {
221 lua_pop( L, 1); // DPC 221 lua_pop( L, 1); // DPC
222 } 222 }
223 223
224 // can work without a universe if creating a deep userdata from some external C module when Lanes isn't loaded 224 // can work without a universe if creating a deep userdata from some external C module when Lanes isn't loaded
225 // in that case, we are not multithreaded and locking isn't necessary anyway 225 // in that case, we are not multithreaded and locking isn't necessary anyway
226 if( U) MUTEX_LOCK( &U->deep_lock); 226 if( U) MUTEX_LOCK( &U->deep_lock);
227 ++ (prelude->refcount); // one more proxy pointing to this deep data 227 ++ (prelude->refcount); // one more proxy pointing to this deep data
228 if( U) MUTEX_UNLOCK( &U->deep_lock); 228 if( U) MUTEX_UNLOCK( &U->deep_lock);
229 229
230 STACK_GROW( L, 7); 230 STACK_GROW( L, 7);
231 STACK_CHECK( L, 0); 231 STACK_CHECK( L, 0);
232 232
233 // a new full userdata, fitted with the specified number of uservalue slots (always 1 for Lua < 5.4) 233 // a new full userdata, fitted with the specified number of uservalue slots (always 1 for Lua < 5.4)
234 proxy = lua_newuserdatauv( L, sizeof(DeepPrelude*), nuv_); // DPC proxy 234 proxy = lua_newuserdatauv( L, sizeof(DeepPrelude*), nuv_); // DPC proxy
235 ASSERT_L( proxy); 235 ASSERT_L( proxy);
236 *proxy = prelude; 236 *proxy = prelude;
237 237
238 // Get/create metatable for 'idfunc' (in this state) 238 // Get/create metatable for 'idfunc' (in this state)
239 lua_pushlightuserdata( L, (void*)(ptrdiff_t)(prelude->idfunc)); // DPC proxy idfunc 239 lua_pushlightuserdata( L, (void*)(ptrdiff_t)(prelude->idfunc)); // DPC proxy idfunc
240 get_deep_lookup( L); // DPC proxy metatable? 240 get_deep_lookup( L); // DPC proxy metatable?
241 241
242 if( lua_isnil( L, -1)) // // No metatable yet. 242 if( lua_isnil( L, -1)) // // No metatable yet.
243 { 243 {
244 char const* modname; 244 char const* modname;
245 int oldtop = lua_gettop( L); // DPC proxy nil 245 int oldtop = lua_gettop( L); // DPC proxy nil
246 lua_pop( L, 1); // DPC proxy 246 lua_pop( L, 1); // DPC proxy
247 // 1 - make one and register it 247 // 1 - make one and register it
248 if( mode_ != eLM_ToKeeper) 248 if( mode_ != eLM_ToKeeper)
249 { 249 {
250 (void) prelude->idfunc( L, eDO_metatable); // DPC proxy metatable 250 (void) prelude->idfunc( L, eDO_metatable); // DPC proxy metatable
251 if( lua_gettop( L) - oldtop != 0 || !lua_istable( L, -1)) 251 if( lua_gettop( L) - oldtop != 0 || !lua_istable( L, -1))
252 { 252 {
253 lua_settop( L, oldtop); // DPC proxy X 253 lua_settop( L, oldtop); // DPC proxy X
254 lua_pop( L, 3); // 254 lua_pop( L, 3); //
255 return "Bad idfunc(eOP_metatable): unexpected pushed value"; 255 return "Bad idfunc(eOP_metatable): unexpected pushed value";
256 } 256 }
257 // if the metatable contains a __gc, we will call it from our own 257 // if the metatable contains a __gc, we will call it from our own
258 lua_getfield( L, -1, "__gc"); // DPC proxy metatable __gc 258 lua_getfield( L, -1, "__gc"); // DPC proxy metatable __gc
259 } 259 }
260 else 260 else
261 { 261 {
262 // keepers need a minimal metatable that only contains our own __gc 262 // keepers need a minimal metatable that only contains our own __gc
263 lua_newtable( L); // DPC proxy metatable 263 lua_newtable( L); // DPC proxy metatable
264 lua_pushnil( L); // DPC proxy metatable nil 264 lua_pushnil( L); // DPC proxy metatable nil
265 } 265 }
266 if( lua_isnil( L, -1)) 266 if( lua_isnil( L, -1))
267 { 267 {
268 // Add our own '__gc' method 268 // Add our own '__gc' method
269 lua_pop( L, 1); // DPC proxy metatable 269 lua_pop( L, 1); // DPC proxy metatable
270 lua_pushcfunction( L, deep_userdata_gc); // DPC proxy metatable deep_userdata_gc 270 lua_pushcfunction( L, deep_userdata_gc); // DPC proxy metatable deep_userdata_gc
271 } 271 }
272 else 272 else
273 { 273 {
274 // Add our own '__gc' method wrapping the original 274 // Add our own '__gc' method wrapping the original
275 lua_pushcclosure( L, deep_userdata_gc, 1); // DPC proxy metatable deep_userdata_gc 275 lua_pushcclosure( L, deep_userdata_gc, 1); // DPC proxy metatable deep_userdata_gc
276 } 276 }
277 lua_setfield( L, -2, "__gc"); // DPC proxy metatable 277 lua_setfield( L, -2, "__gc"); // DPC proxy metatable
278 278
279 // Memorize for later rounds 279 // Memorize for later rounds
280 lua_pushvalue( L, -1); // DPC proxy metatable metatable 280 lua_pushvalue( L, -1); // DPC proxy metatable metatable
281 lua_pushlightuserdata( L, (void*)(ptrdiff_t)(prelude->idfunc)); // DPC proxy metatable metatable idfunc 281 lua_pushlightuserdata( L, (void*)(ptrdiff_t)(prelude->idfunc)); // DPC proxy metatable metatable idfunc
282 set_deep_lookup( L); // DPC proxy metatable 282 set_deep_lookup( L); // DPC proxy metatable
283 283
284 // 2 - cause the target state to require the module that exported the idfunc 284 // 2 - cause the target state to require the module that exported the idfunc
285 // this is needed because we must make sure the shared library is still loaded as long as we hold a pointer on the idfunc 285 // this is needed because we must make sure the shared library is still loaded as long as we hold a pointer on the idfunc
286 { 286 {
287 int oldtop_module = lua_gettop( L); 287 int oldtop_module = lua_gettop( L);
288 modname = (char const*) prelude->idfunc( L, eDO_module); // DPC proxy metatable 288 modname = (char const*) prelude->idfunc( L, eDO_module); // DPC proxy metatable
289 // make sure the function pushed nothing on the stack! 289 // make sure the function pushed nothing on the stack!
290 if( lua_gettop( L) - oldtop_module != 0) 290 if( lua_gettop( L) - oldtop_module != 0)
291 { 291 {
292 lua_pop( L, 3); // 292 lua_pop( L, 3); //
293 return "Bad idfunc(eOP_module): should not push anything"; 293 return "Bad idfunc(eOP_module): should not push anything";
294 } 294 }
295 } 295 }
296 if( NULL != modname) // we actually got a module name 296 if( NULL != modname) // we actually got a module name
297 { 297 {
298 // L.registry._LOADED exists without having registered the 'package' library. 298 // L.registry._LOADED exists without having registered the 'package' library.
299 lua_getglobal( L, "require"); // DPC proxy metatable require() 299 lua_getglobal( L, "require"); // DPC proxy metatable require()
300 // check that the module is already loaded (or being loaded, we are happy either way) 300 // check that the module is already loaded (or being loaded, we are happy either way)
301 if( lua_isfunction( L, -1)) 301 if( lua_isfunction( L, -1))
302 { 302 {
303 lua_pushstring( L, modname); // DPC proxy metatable require() "module" 303 lua_pushstring( L, modname); // DPC proxy metatable require() "module"
304 lua_getfield( L, LUA_REGISTRYINDEX, LUA_LOADED_TABLE); // DPC proxy metatable require() "module" _R._LOADED 304 lua_getfield( L, LUA_REGISTRYINDEX, LUA_LOADED_TABLE); // DPC proxy metatable require() "module" _R._LOADED
305 if( lua_istable( L, -1)) 305 if( lua_istable( L, -1))
306 { 306 {
307 bool_t alreadyloaded; 307 bool_t alreadyloaded;
308 lua_pushvalue( L, -2); // DPC proxy metatable require() "module" _R._LOADED "module" 308 lua_pushvalue( L, -2); // DPC proxy metatable require() "module" _R._LOADED "module"
309 lua_rawget( L, -2); // DPC proxy metatable require() "module" _R._LOADED module 309 lua_rawget( L, -2); // DPC proxy metatable require() "module" _R._LOADED module
310 alreadyloaded = lua_toboolean( L, -1); 310 alreadyloaded = lua_toboolean( L, -1);
311 if( !alreadyloaded) // not loaded 311 if( !alreadyloaded) // not loaded
312 { 312 {
313 int require_result; 313 int require_result;
314 lua_pop( L, 2); // DPC proxy metatable require() "module" 314 lua_pop( L, 2); // DPC proxy metatable require() "module"
315 // require "modname" 315 // require "modname"
316 require_result = lua_pcall( L, 1, 0, 0); // DPC proxy metatable error? 316 require_result = lua_pcall( L, 1, 0, 0); // DPC proxy metatable error?
317 if( require_result != LUA_OK) 317 if( require_result != LUA_OK)
318 { 318 {
319 // failed, return the error message 319 // failed, return the error message
320 lua_pushfstring( L, "error while requiring '%s' identified by idfunc(eOP_module): ", modname); 320 lua_pushfstring( L, "error while requiring '%s' identified by idfunc(eOP_module): ", modname);
321 lua_insert( L, -2); // DPC proxy metatable prefix error 321 lua_insert( L, -2); // DPC proxy metatable prefix error
322 lua_concat( L, 2); // DPC proxy metatable error 322 lua_concat( L, 2); // DPC proxy metatable error
323 return lua_tostring( L, -1); 323 return lua_tostring( L, -1);
324 } 324 }
325 } 325 }
326 else // already loaded, we are happy 326 else // already loaded, we are happy
327 { 327 {
328 lua_pop( L, 4); // DPC proxy metatable 328 lua_pop( L, 4); // DPC proxy metatable
329 } 329 }
330 } 330 }
331 else // no L.registry._LOADED; can this ever happen? 331 else // no L.registry._LOADED; can this ever happen?
332 { 332 {
333 lua_pop( L, 6); // 333 lua_pop( L, 6); //
334 return "unexpected error while requiring a module identified by idfunc(eOP_module)"; 334 return "unexpected error while requiring a module identified by idfunc(eOP_module)";
335 } 335 }
336 } 336 }
337 else // a module name, but no require() function :-( 337 else // a module name, but no require() function :-(
338 { 338 {
339 lua_pop( L, 4); // 339 lua_pop( L, 4); //
340 return "lanes receiving deep userdata should register the 'package' library"; 340 return "lanes receiving deep userdata should register the 'package' library";
341 } 341 }
342 } 342 }
343 } 343 }
344 STACK_MID( L, 2); // DPC proxy metatable 344 STACK_MID( L, 2); // DPC proxy metatable
345 ASSERT_L( lua_isuserdata( L, -2)); 345 ASSERT_L( lua_isuserdata( L, -2));
346 ASSERT_L( lua_istable( L, -1)); 346 ASSERT_L( lua_istable( L, -1));
347 lua_setmetatable( L, -2); // DPC proxy 347 lua_setmetatable( L, -2); // DPC proxy
348 348
349 // If we're here, we obviously had to create a new proxy, so cache it. 349 // If we're here, we obviously had to create a new proxy, so cache it.
350 lua_pushlightuserdata( L, prelude); // DPC proxy deep 350 lua_pushlightuserdata( L, prelude); // DPC proxy deep
351 lua_pushvalue( L, -2); // DPC proxy deep proxy 351 lua_pushvalue( L, -2); // DPC proxy deep proxy
352 lua_rawset( L, -4); // DPC proxy 352 lua_rawset( L, -4); // DPC proxy
353 lua_remove( L, -2); // proxy 353 lua_remove( L, -2); // proxy
354 ASSERT_L( lua_isuserdata( L, -1)); 354 ASSERT_L( lua_isuserdata( L, -1));
355 STACK_END( L, 0); 355 STACK_END( L, 0);
356 return NULL; 356 return NULL;
357} 357}
358 358
359/* 359/*
@@ -380,42 +380,42 @@ char const* push_deep_proxy( Universe* U, lua_State* L, DeepPrelude* prelude, in
380*/ 380*/
381int luaG_newdeepuserdata( lua_State* L, luaG_IdFunction idfunc, int nuv_) 381int luaG_newdeepuserdata( lua_State* L, luaG_IdFunction idfunc, int nuv_)
382{ 382{
383 char const* errmsg; 383 char const* errmsg;
384 384
385 STACK_GROW( L, 1); 385 STACK_GROW( L, 1);
386 STACK_CHECK( L, 0); 386 STACK_CHECK( L, 0);
387 { 387 {
388 int const oldtop = lua_gettop( L); 388 int const oldtop = lua_gettop( L);
389 DeepPrelude* prelude = idfunc( L, eDO_new); 389 DeepPrelude* prelude = idfunc( L, eDO_new);
390 if( prelude == NULL) 390 if( prelude == NULL)
391 { 391 {
392 luaL_error( L, "idfunc(eDO_new) failed to create deep userdata (out of memory)"); 392 luaL_error( L, "idfunc(eDO_new) failed to create deep userdata (out of memory)");
393 } 393 }
394 if( prelude->magic.value != DEEP_VERSION.value) 394 if( prelude->magic.value != DEEP_VERSION.value)
395 { 395 {
396 // just in case, don't leak the newly allocated deep userdata object 396 // just in case, don't leak the newly allocated deep userdata object
397 lua_pushlightuserdata( L, prelude); 397 lua_pushlightuserdata( L, prelude);
398 idfunc( L, eDO_delete); 398 idfunc( L, eDO_delete);
399 return luaL_error( L, "Bad idfunc(eDO_new): DEEP_VERSION is incorrect, rebuild your implementation with the latest deep implementation"); 399 return luaL_error( L, "Bad idfunc(eDO_new): DEEP_VERSION is incorrect, rebuild your implementation with the latest deep implementation");
400 } 400 }
401 prelude->refcount = 0; // 'push_deep_proxy' will lift it to 1 401 prelude->refcount = 0; // 'push_deep_proxy' will lift it to 1
402 prelude->idfunc = idfunc; 402 prelude->idfunc = idfunc;
403 403
404 if( lua_gettop( L) - oldtop != 0) 404 if( lua_gettop( L) - oldtop != 0)
405 { 405 {
406 // just in case, don't leak the newly allocated deep userdata object 406 // just in case, don't leak the newly allocated deep userdata object
407 lua_pushlightuserdata( L, prelude); 407 lua_pushlightuserdata( L, prelude);
408 idfunc( L, eDO_delete); 408 idfunc( L, eDO_delete);
409 return luaL_error( L, "Bad idfunc(eDO_new): should not push anything on the stack"); 409 return luaL_error( L, "Bad idfunc(eDO_new): should not push anything on the stack");
410 } 410 }
411 errmsg = push_deep_proxy( universe_get( L), L, prelude, nuv_, eLM_LaneBody); // proxy 411 errmsg = push_deep_proxy( universe_get( L), L, prelude, nuv_, eLM_LaneBody); // proxy
412 if( errmsg != NULL) 412 if( errmsg != NULL)
413 { 413 {
414 return luaL_error( L, errmsg); 414 return luaL_error( L, errmsg);
415 } 415 }
416 } 416 }
417 STACK_END( L, 1); 417 STACK_END( L, 1);
418 return 1; 418 return 1;
419} 419}
420 420
421 421
@@ -427,19 +427,19 @@ int luaG_newdeepuserdata( lua_State* L, luaG_IdFunction idfunc, int nuv_)
427*/ 427*/
428void* luaG_todeep( lua_State* L, luaG_IdFunction idfunc, int index) 428void* luaG_todeep( lua_State* L, luaG_IdFunction idfunc, int index)
429{ 429{
430 DeepPrelude** proxy; 430 DeepPrelude** proxy;
431 431
432 STACK_CHECK( L, 0); 432 STACK_CHECK( L, 0);
433 // ensure it is actually a deep userdata 433 // ensure it is actually a deep userdata
434 if( get_idfunc( L, index, eLM_LaneBody) != idfunc) 434 if( get_idfunc( L, index, eLM_LaneBody) != idfunc)
435 { 435 {
436 return NULL; // no metatable, or wrong kind 436 return NULL; // no metatable, or wrong kind
437 } 437 }
438 438
439 proxy = (DeepPrelude**) lua_touserdata( L, index); 439 proxy = (DeepPrelude**) lua_touserdata( L, index);
440 STACK_END( L, 0); 440 STACK_END( L, 0);
441 441
442 return *proxy; 442 return *proxy;
443} 443}
444 444
445 445
@@ -452,50 +452,50 @@ void* luaG_todeep( lua_State* L, luaG_IdFunction idfunc, int index)
452 */ 452 */
453bool_t copydeep( Universe* U, lua_State* L2, uint_t L2_cache_i, lua_State* L, uint_t i, LookupMode mode_, char const* upName_) 453bool_t copydeep( Universe* U, lua_State* L2, uint_t L2_cache_i, lua_State* L, uint_t i, LookupMode mode_, char const* upName_)
454{ 454{
455 char const* errmsg; 455 char const* errmsg;
456 luaG_IdFunction idfunc = get_idfunc( L, i, mode_); 456 luaG_IdFunction idfunc = get_idfunc( L, i, mode_);
457 int nuv = 0; 457 int nuv = 0;
458 458
459 if( idfunc == NULL) 459 if( idfunc == NULL)
460 { 460 {
461 return FALSE; // not a deep userdata 461 return FALSE; // not a deep userdata
462 } 462 }
463 463
464 STACK_CHECK( L, 0); 464 STACK_CHECK( L, 0);
465 STACK_CHECK( L2, 0); 465 STACK_CHECK( L2, 0);
466 466
467 // extract all uservalues of the source 467 // extract all uservalues of the source
468 while( lua_getiuservalue( L, i, nuv + 1) != LUA_TNONE) // ... u [uv]* nil 468 while( lua_getiuservalue( L, i, nuv + 1) != LUA_TNONE) // ... u [uv]* nil
469 { 469 {
470 ++ nuv; 470 ++ nuv;
471 } 471 }
472 // last call returned TNONE and pushed nil, that we don't need 472 // last call returned TNONE and pushed nil, that we don't need
473 lua_pop( L, 1); // ... u [uv]* 473 lua_pop( L, 1); // ... u [uv]*
474 STACK_MID( L, nuv); 474 STACK_MID( L, nuv);
475 475
476 errmsg = push_deep_proxy( U, L2, *(DeepPrelude**) lua_touserdata( L, i), nuv, mode_); // u 476 errmsg = push_deep_proxy( U, L2, *(DeepPrelude**) lua_touserdata( L, i), nuv, mode_); // u
477 477
478 // transfer all uservalues of the source in the destination 478 // transfer all uservalues of the source in the destination
479 { 479 {
480 int const clone_i = lua_gettop( L2); 480 int const clone_i = lua_gettop( L2);
481 while( nuv) 481 while( nuv)
482 { 482 {
483 inter_copy_one( U, L2, L2_cache_i, L, lua_absindex( L, -1), VT_NORMAL, mode_, upName_); // u uv 483 inter_copy_one( U, L2, L2_cache_i, L, lua_absindex( L, -1), VT_NORMAL, mode_, upName_); // u uv
484 lua_pop( L, 1); // ... u [uv]* 484 lua_pop( L, 1); // ... u [uv]*
485 // this pops the value from the stack 485 // this pops the value from the stack
486 lua_setiuservalue( L2, clone_i, nuv); // u 486 lua_setiuservalue( L2, clone_i, nuv); // u
487 -- nuv; 487 -- nuv;
488 } 488 }
489 } 489 }
490 490
491 STACK_END( L2, 1); 491 STACK_END( L2, 1);
492 STACK_END( L, 0); 492 STACK_END( L, 0);
493 493
494 if( errmsg != NULL) 494 if( errmsg != NULL)
495 { 495 {
496 // raise the error in the proper state (not the keeper) 496 // raise the error in the proper state (not the keeper)
497 lua_State* errL = (mode_ == eLM_FromKeeper) ? L2 : L; 497 lua_State* errL = (mode_ == eLM_FromKeeper) ? L2 : L;
498 luaL_error( errL, errmsg); 498 luaL_error( errL, errmsg);
499 } 499 }
500 return TRUE; 500 return TRUE;
501} \ No newline at end of file 501} \ No newline at end of file
diff --git a/src/deep.h b/src/deep.h
index 35c8bd4..9b00b70 100644
--- a/src/deep.h
+++ b/src/deep.h
@@ -24,18 +24,18 @@ typedef struct s_Universe Universe;
24 24
25enum eLookupMode 25enum eLookupMode
26{ 26{
27 eLM_LaneBody, // send the lane body directly from the source to the destination lane 27 eLM_LaneBody, // send the lane body directly from the source to the destination lane
28 eLM_ToKeeper, // send a function from a lane to a keeper state 28 eLM_ToKeeper, // send a function from a lane to a keeper state
29 eLM_FromKeeper // send a function from a keeper state to a lane 29 eLM_FromKeeper // send a function from a keeper state to a lane
30}; 30};
31typedef enum eLookupMode LookupMode; 31typedef enum eLookupMode LookupMode;
32 32
33enum eDeepOp 33enum eDeepOp
34{ 34{
35 eDO_new, 35 eDO_new,
36 eDO_delete, 36 eDO_delete,
37 eDO_metatable, 37 eDO_metatable,
38 eDO_module, 38 eDO_module,
39}; 39};
40typedef enum eDeepOp DeepOp; 40typedef enum eDeepOp DeepOp;
41 41
@@ -49,11 +49,11 @@ static DECLARE_CONST_UNIQUE_KEY( DEEP_VERSION, 0xB4B0119C10642B29);
49// should be used as header for full userdata 49// should be used as header for full userdata
50struct s_DeepPrelude 50struct s_DeepPrelude
51{ 51{
52 DECLARE_UNIQUE_KEY( magic); // must be filled by the Deep userdata idfunc that allocates it on eDO_new operation 52 DECLARE_UNIQUE_KEY( magic); // must be filled by the Deep userdata idfunc that allocates it on eDO_new operation
53 // when stored in a keeper state, the full userdata doesn't have a metatable, so we need direct access to the idfunc 53 // when stored in a keeper state, the full userdata doesn't have a metatable, so we need direct access to the idfunc
54 luaG_IdFunction idfunc; 54 luaG_IdFunction idfunc;
55 // data is destroyed when refcount is 0 55 // data is destroyed when refcount is 0
56 volatile int refcount; 56 volatile int refcount;
57}; 57};
58typedef struct s_DeepPrelude DeepPrelude; 58typedef struct s_DeepPrelude DeepPrelude;
59 59
diff --git a/src/keeper.c b/src/keeper.c
index c777866..eea017f 100644
--- a/src/keeper.c
+++ b/src/keeper.c
@@ -61,9 +61,9 @@
61 61
62typedef struct 62typedef struct
63{ 63{
64 lua_Integer first; 64 lua_Integer first;
65 lua_Integer count; 65 lua_Integer count;
66 lua_Integer limit; 66 lua_Integer limit;
67} keeper_fifo; 67} keeper_fifo;
68 68
69static int const CONTENTS_TABLE = 1; 69static int const CONTENTS_TABLE = 1;
@@ -71,47 +71,47 @@ static int const CONTENTS_TABLE = 1;
71// replaces the fifo ud by its uservalue on the stack 71// replaces the fifo ud by its uservalue on the stack
72static keeper_fifo* prepare_fifo_access( lua_State* L, int idx_) 72static keeper_fifo* prepare_fifo_access( lua_State* L, int idx_)
73{ 73{
74 keeper_fifo* fifo = (keeper_fifo*) lua_touserdata( L, idx_); 74 keeper_fifo* fifo = (keeper_fifo*) lua_touserdata( L, idx_);
75 if( fifo != NULL) 75 if( fifo != NULL)
76 { 76 {
77 idx_ = lua_absindex( L, idx_); 77 idx_ = lua_absindex( L, idx_);
78 STACK_GROW( L, 1); 78 STACK_GROW( L, 1);
79 // we can replace the fifo userdata in the stack without fear of it being GCed, there are other references around 79 // we can replace the fifo userdata in the stack without fear of it being GCed, there are other references around
80 lua_getiuservalue( L, idx_, CONTENTS_TABLE); 80 lua_getiuservalue( L, idx_, CONTENTS_TABLE);
81 lua_replace( L, idx_); 81 lua_replace( L, idx_);
82 } 82 }
83 return fifo; 83 return fifo;
84} 84}
85 85
86// in: nothing 86// in: nothing
87// out: { first = 1, count = 0, limit = -1} 87// out: { first = 1, count = 0, limit = -1}
88static void fifo_new( lua_State* L) 88static void fifo_new( lua_State* L)
89{ 89{
90 keeper_fifo* fifo; 90 keeper_fifo* fifo;
91 STACK_GROW( L, 2); 91 STACK_GROW( L, 2);
92 // a fifo full userdata has one uservalue, the table that holds the actual fifo contents 92 // a fifo full userdata has one uservalue, the table that holds the actual fifo contents
93 fifo = (keeper_fifo*)lua_newuserdatauv( L, sizeof( keeper_fifo), 1); 93 fifo = (keeper_fifo*)lua_newuserdatauv( L, sizeof( keeper_fifo), 1);
94 fifo->first = 1; 94 fifo->first = 1;
95 fifo->count = 0; 95 fifo->count = 0;
96 fifo->limit = -1; 96 fifo->limit = -1;
97 lua_newtable( L); 97 lua_newtable( L);
98 lua_setiuservalue( L, -2, CONTENTS_TABLE); 98 lua_setiuservalue( L, -2, CONTENTS_TABLE);
99} 99}
100 100
101// in: expect fifo ... on top of the stack 101// in: expect fifo ... on top of the stack
102// out: nothing, removes all pushed values from the stack 102// out: nothing, removes all pushed values from the stack
103static void fifo_push( lua_State* L, keeper_fifo* fifo_, lua_Integer count_) 103static void fifo_push( lua_State* L, keeper_fifo* fifo_, lua_Integer count_)
104{ 104{
105 int const idx = lua_gettop( L) - (int) count_; 105 int const idx = lua_gettop( L) - (int) count_;
106 lua_Integer start = fifo_->first + fifo_->count - 1; 106 lua_Integer start = fifo_->first + fifo_->count - 1;
107 lua_Integer i; 107 lua_Integer i;
108 // pop all additional arguments, storing them in the fifo 108 // pop all additional arguments, storing them in the fifo
109 for( i = count_; i >= 1; -- i) 109 for( i = count_; i >= 1; -- i)
110 { 110 {
111 // store in the fifo the value at the top of the stack at the specified index, popping it from the stack 111 // store in the fifo the value at the top of the stack at the specified index, popping it from the stack
112 lua_rawseti( L, idx, (int)(start + i)); 112 lua_rawseti( L, idx, (int)(start + i));
113 } 113 }
114 fifo_->count += count_; 114 fifo_->count += count_;
115} 115}
116 116
117// in: fifo 117// in: fifo
@@ -121,46 +121,46 @@ static void fifo_push( lua_State* L, keeper_fifo* fifo_, lua_Integer count_)
121// function assumes that there is enough data in the fifo to satisfy the request 121// function assumes that there is enough data in the fifo to satisfy the request
122static void fifo_peek( lua_State* L, keeper_fifo* fifo_, lua_Integer count_) 122static void fifo_peek( lua_State* L, keeper_fifo* fifo_, lua_Integer count_)
123{ 123{
124 lua_Integer i; 124 lua_Integer i;
125 STACK_GROW( L, count_); 125 STACK_GROW( L, count_);
126 for( i = 0; i < count_; ++ i) 126 for( i = 0; i < count_; ++ i)
127 { 127 {
128 lua_rawgeti( L, 1, (int)( fifo_->first + i)); 128 lua_rawgeti( L, 1, (int)( fifo_->first + i));
129 } 129 }
130} 130}
131 131
132// in: fifo 132// in: fifo
133// out: remove the fifo from the stack, push as many items as required on the stack (function assumes they exist in sufficient number) 133// out: remove the fifo from the stack, push as many items as required on the stack (function assumes they exist in sufficient number)
134static void fifo_pop( lua_State* L, keeper_fifo* fifo_, lua_Integer count_) 134static void fifo_pop( lua_State* L, keeper_fifo* fifo_, lua_Integer count_)
135{ 135{
136 int const fifo_idx = lua_gettop( L); // ... fifo 136 int const fifo_idx = lua_gettop( L); // ... fifo
137 int i; 137 int i;
138 // each iteration pushes a value on the stack! 138 // each iteration pushes a value on the stack!
139 STACK_GROW( L, count_ + 2); 139 STACK_GROW( L, count_ + 2);
140 // skip first item, we will push it last 140 // skip first item, we will push it last
141 for( i = 1; i < count_; ++ i) 141 for( i = 1; i < count_; ++ i)
142 { 142 {
143 int const at = (int)( fifo_->first + i); 143 int const at = (int)( fifo_->first + i);
144 // push item on the stack 144 // push item on the stack
145 lua_rawgeti( L, fifo_idx, at); // ... fifo val 145 lua_rawgeti( L, fifo_idx, at); // ... fifo val
146 // remove item from the fifo 146 // remove item from the fifo
147 lua_pushnil( L); // ... fifo val nil 147 lua_pushnil( L); // ... fifo val nil
148 lua_rawseti( L, fifo_idx, at); // ... fifo val 148 lua_rawseti( L, fifo_idx, at); // ... fifo val
149 } 149 }
150 // now process first item 150 // now process first item
151 { 151 {
152 int const at = (int)( fifo_->first); 152 int const at = (int)( fifo_->first);
153 lua_rawgeti( L, fifo_idx, at); // ... fifo vals val 153 lua_rawgeti( L, fifo_idx, at); // ... fifo vals val
154 lua_pushnil( L); // ... fifo vals val nil 154 lua_pushnil( L); // ... fifo vals val nil
155 lua_rawseti( L, fifo_idx, at); // ... fifo vals val 155 lua_rawseti( L, fifo_idx, at); // ... fifo vals val
156 lua_replace( L, fifo_idx); // ... vals 156 lua_replace( L, fifo_idx); // ... vals
157 } 157 }
158 { 158 {
159 // avoid ever-growing indexes by resetting each time we detect the fifo is empty 159 // avoid ever-growing indexes by resetting each time we detect the fifo is empty
160 lua_Integer const new_count = fifo_->count - count_; 160 lua_Integer const new_count = fifo_->count - count_;
161 fifo_->first = (new_count == 0) ? 1 : (fifo_->first + count_); 161 fifo_->first = (new_count == 0) ? 1 : (fifo_->first + count_);
162 fifo_->count = new_count; 162 fifo_->count = new_count;
163 } 163 }
164} 164}
165 165
166// in: linda_ud expected at *absolute* stack slot idx 166// in: linda_ud expected at *absolute* stack slot idx
@@ -169,87 +169,87 @@ static void fifo_pop( lua_State* L, keeper_fifo* fifo_, lua_Integer count_)
169static DECLARE_CONST_UNIQUE_KEY( FIFOS_KEY, 0xdce50bbc351cd465); 169static DECLARE_CONST_UNIQUE_KEY( FIFOS_KEY, 0xdce50bbc351cd465);
170static void push_table( lua_State* L, int idx_) 170static void push_table( lua_State* L, int idx_)
171{ 171{
172 STACK_GROW( L, 4); 172 STACK_GROW( L, 4);
173 STACK_CHECK( L, 0); 173 STACK_CHECK( L, 0);
174 idx_ = lua_absindex( L, idx_); 174 idx_ = lua_absindex( L, idx_);
175 REGISTRY_GET( L, FIFOS_KEY); // ud fifos 175 REGISTRY_GET( L, FIFOS_KEY); // ud fifos
176 lua_pushvalue( L, idx_); // ud fifos ud 176 lua_pushvalue( L, idx_); // ud fifos ud
177 lua_rawget( L, -2); // ud fifos fifos[ud] 177 lua_rawget( L, -2); // ud fifos fifos[ud]
178 STACK_MID( L, 2); 178 STACK_MID( L, 2);
179 if( lua_isnil( L, -1)) 179 if( lua_isnil( L, -1))
180 { 180 {
181 lua_pop( L, 1); // ud fifos 181 lua_pop( L, 1); // ud fifos
182 // add a new fifos table for this linda 182 // add a new fifos table for this linda
183 lua_newtable( L); // ud fifos fifos[ud] 183 lua_newtable( L); // ud fifos fifos[ud]
184 lua_pushvalue( L, idx_); // ud fifos fifos[ud] ud 184 lua_pushvalue( L, idx_); // ud fifos fifos[ud] ud
185 lua_pushvalue( L, -2); // ud fifos fifos[ud] ud fifos[ud] 185 lua_pushvalue( L, -2); // ud fifos fifos[ud] ud fifos[ud]
186 lua_rawset( L, -4); // ud fifos fifos[ud] 186 lua_rawset( L, -4); // ud fifos fifos[ud]
187 } 187 }
188 lua_remove( L, -2); // ud fifos[ud] 188 lua_remove( L, -2); // ud fifos[ud]
189 STACK_END( L, 1); 189 STACK_END( L, 1);
190} 190}
191 191
192int keeper_push_linda_storage( Universe* U, lua_State* L, void* ptr_, ptrdiff_t magic_) 192int keeper_push_linda_storage( Universe* U, lua_State* L, void* ptr_, ptrdiff_t magic_)
193{ 193{
194 Keeper* const K = which_keeper( U->keepers, magic_); 194 Keeper* const K = which_keeper( U->keepers, magic_);
195 lua_State* const KL = K ? K->L : NULL; 195 lua_State* const KL = K ? K->L : NULL;
196 if( KL == NULL) return 0; 196 if( KL == NULL) return 0;
197 STACK_GROW( KL, 4); 197 STACK_GROW( KL, 4);
198 STACK_CHECK( KL, 0); 198 STACK_CHECK( KL, 0);
199 REGISTRY_GET( KL, FIFOS_KEY); // fifos 199 REGISTRY_GET( KL, FIFOS_KEY); // fifos
200 lua_pushlightuserdata( KL, ptr_); // fifos ud 200 lua_pushlightuserdata( KL, ptr_); // fifos ud
201 lua_rawget( KL, -2); // fifos storage 201 lua_rawget( KL, -2); // fifos storage
202 lua_remove( KL, -2); // storage 202 lua_remove( KL, -2); // storage
203 if( !lua_istable( KL, -1)) 203 if( !lua_istable( KL, -1))
204 { 204 {
205 lua_pop( KL, 1); // 205 lua_pop( KL, 1); //
206 STACK_MID( KL, 0); 206 STACK_MID( KL, 0);
207 return 0; 207 return 0;
208 } 208 }
209 // move data from keeper to destination state KEEPER MAIN 209 // move data from keeper to destination state KEEPER MAIN
210 lua_pushnil( KL); // storage nil 210 lua_pushnil( KL); // storage nil
211 STACK_GROW( L, 5); 211 STACK_GROW( L, 5);
212 STACK_CHECK( L, 0); 212 STACK_CHECK( L, 0);
213 lua_newtable( L); // out 213 lua_newtable( L); // out
214 while( lua_next( KL, -2)) // storage key fifo 214 while( lua_next( KL, -2)) // storage key fifo
215 { 215 {
216 keeper_fifo* fifo = prepare_fifo_access( KL, -1); // storage key fifo 216 keeper_fifo* fifo = prepare_fifo_access( KL, -1); // storage key fifo
217 lua_pushvalue( KL, -2); // storage key fifo key 217 lua_pushvalue( KL, -2); // storage key fifo key
218 luaG_inter_move( U, KL, L, 1, eLM_FromKeeper); // storage key fifo // out key 218 luaG_inter_move( U, KL, L, 1, eLM_FromKeeper); // storage key fifo // out key
219 STACK_MID( L, 2); 219 STACK_MID( L, 2);
220 lua_newtable( L); // out key keyout 220 lua_newtable( L); // out key keyout
221 luaG_inter_move( U, KL, L, 1, eLM_FromKeeper); // storage key // out key keyout fifo 221 luaG_inter_move( U, KL, L, 1, eLM_FromKeeper); // storage key // out key keyout fifo
222 lua_pushinteger( L, fifo->first); // out key keyout fifo first 222 lua_pushinteger( L, fifo->first); // out key keyout fifo first
223 STACK_MID( L, 5); 223 STACK_MID( L, 5);
224 lua_setfield( L, -3, "first"); // out key keyout fifo 224 lua_setfield( L, -3, "first"); // out key keyout fifo
225 lua_pushinteger( L, fifo->count); // out key keyout fifo count 225 lua_pushinteger( L, fifo->count); // out key keyout fifo count
226 STACK_MID( L, 5); 226 STACK_MID( L, 5);
227 lua_setfield( L, -3, "count"); // out key keyout fifo 227 lua_setfield( L, -3, "count"); // out key keyout fifo
228 lua_pushinteger( L, fifo->limit); // out key keyout fifo limit 228 lua_pushinteger( L, fifo->limit); // out key keyout fifo limit
229 STACK_MID( L, 5); 229 STACK_MID( L, 5);
230 lua_setfield( L, -3, "limit"); // out key keyout fifo 230 lua_setfield( L, -3, "limit"); // out key keyout fifo
231 lua_setfield( L, -2, "fifo"); // out key keyout 231 lua_setfield( L, -2, "fifo"); // out key keyout
232 lua_rawset( L, -3); // out 232 lua_rawset( L, -3); // out
233 STACK_MID( L, 1); 233 STACK_MID( L, 1);
234 } 234 }
235 STACK_END( L, 1); 235 STACK_END( L, 1);
236 lua_pop( KL, 1); // 236 lua_pop( KL, 1); //
237 STACK_END( KL, 0); 237 STACK_END( KL, 0);
238 return 1; 238 return 1;
239} 239}
240 240
241// in: linda_ud 241// in: linda_ud
242int keepercall_clear( lua_State* L) 242int keepercall_clear( lua_State* L)
243{ 243{
244 STACK_GROW( L, 3); 244 STACK_GROW( L, 3);
245 STACK_CHECK( L, 0); 245 STACK_CHECK( L, 0);
246 REGISTRY_GET( L, FIFOS_KEY); // ud fifos 246 REGISTRY_GET( L, FIFOS_KEY); // ud fifos
247 lua_pushvalue( L, 1); // ud fifos ud 247 lua_pushvalue( L, 1); // ud fifos ud
248 lua_pushnil( L); // ud fifos ud nil 248 lua_pushnil( L); // ud fifos ud nil
249 lua_rawset( L, -3); // ud fifos 249 lua_rawset( L, -3); // ud fifos
250 lua_pop( L, 1); // ud 250 lua_pop( L, 1); // ud
251 STACK_END( L, 0); 251 STACK_END( L, 0);
252 return 0; 252 return 0;
253} 253}
254 254
255 255
@@ -257,311 +257,311 @@ int keepercall_clear( lua_State* L)
257// out: true|false 257// out: true|false
258int keepercall_send( lua_State* L) 258int keepercall_send( lua_State* L)
259{ 259{
260 keeper_fifo* fifo; 260 keeper_fifo* fifo;
261 int n = lua_gettop( L) - 2; 261 int n = lua_gettop( L) - 2;
262 push_table( L, 1); // ud key ... fifos 262 push_table( L, 1); // ud key ... fifos
263 // get the fifo associated to this key in this linda, create it if it doesn't exist 263 // get the fifo associated to this key in this linda, create it if it doesn't exist
264 lua_pushvalue( L, 2); // ud key ... fifos key 264 lua_pushvalue( L, 2); // ud key ... fifos key
265 lua_rawget( L, -2); // ud key ... fifos fifo 265 lua_rawget( L, -2); // ud key ... fifos fifo
266 if( lua_isnil( L, -1)) 266 if( lua_isnil( L, -1))
267 { 267 {
268 lua_pop( L, 1); // ud key ... fifos 268 lua_pop( L, 1); // ud key ... fifos
269 fifo_new( L); // ud key ... fifos fifo 269 fifo_new( L); // ud key ... fifos fifo
270 lua_pushvalue( L, 2); // ud key ... fifos fifo key 270 lua_pushvalue( L, 2); // ud key ... fifos fifo key
271 lua_pushvalue( L, -2); // ud key ... fifos fifo key fifo 271 lua_pushvalue( L, -2); // ud key ... fifos fifo key fifo
272 lua_rawset( L, -4); // ud key ... fifos fifo 272 lua_rawset( L, -4); // ud key ... fifos fifo
273 } 273 }
274 lua_remove( L, -2); // ud key ... fifo 274 lua_remove( L, -2); // ud key ... fifo
275 fifo = (keeper_fifo*) lua_touserdata( L, -1); 275 fifo = (keeper_fifo*) lua_touserdata( L, -1);
276 if( fifo->limit >= 0 && fifo->count + n > fifo->limit) 276 if( fifo->limit >= 0 && fifo->count + n > fifo->limit)
277 { 277 {
278 lua_settop( L, 0); // 278 lua_settop( L, 0); //
279 lua_pushboolean( L, 0); // false 279 lua_pushboolean( L, 0); // false
280 } 280 }
281 else 281 else
282 { 282 {
283 fifo = prepare_fifo_access( L, -1); 283 fifo = prepare_fifo_access( L, -1);
284 lua_replace( L, 2); // ud fifo ... 284 lua_replace( L, 2); // ud fifo ...
285 fifo_push( L, fifo, n); // ud fifo 285 fifo_push( L, fifo, n); // ud fifo
286 lua_settop( L, 0); // 286 lua_settop( L, 0); //
287 lua_pushboolean( L, 1); // true 287 lua_pushboolean( L, 1); // true
288 } 288 }
289 return 1; 289 return 1;
290} 290}
291 291
292// in: linda_ud, key [, key]? 292// in: linda_ud, key [, key]?
293// out: (key, val) or nothing 293// out: (key, val) or nothing
294int keepercall_receive( lua_State* L) 294int keepercall_receive( lua_State* L)
295{ 295{
296 int top = lua_gettop( L); 296 int top = lua_gettop( L);
297 int i; 297 int i;
298 push_table( L, 1); // ud keys fifos 298 push_table( L, 1); // ud keys fifos
299 lua_replace( L, 1); // fifos keys 299 lua_replace( L, 1); // fifos keys
300 for( i = 2; i <= top; ++ i) 300 for( i = 2; i <= top; ++ i)
301 { 301 {
302 keeper_fifo* fifo; 302 keeper_fifo* fifo;
303 lua_pushvalue( L, i); // fifos keys key[i] 303 lua_pushvalue( L, i); // fifos keys key[i]
304 lua_rawget( L, 1); // fifos keys fifo 304 lua_rawget( L, 1); // fifos keys fifo
305 fifo = prepare_fifo_access( L, -1); // fifos keys fifo 305 fifo = prepare_fifo_access( L, -1); // fifos keys fifo
306 if( fifo != NULL && fifo->count > 0) 306 if( fifo != NULL && fifo->count > 0)
307 { 307 {
308 fifo_pop( L, fifo, 1); // fifos keys val 308 fifo_pop( L, fifo, 1); // fifos keys val
309 if( !lua_isnil( L, -1)) 309 if( !lua_isnil( L, -1))
310 { 310 {
311 lua_replace( L, 1); // val keys 311 lua_replace( L, 1); // val keys
312 lua_settop( L, i); // val keys key[i] 312 lua_settop( L, i); // val keys key[i]
313 if( i != 2) 313 if( i != 2)
314 { 314 {
315 lua_replace( L, 2); // val key keys 315 lua_replace( L, 2); // val key keys
316 lua_settop( L, 2); // val key 316 lua_settop( L, 2); // val key
317 } 317 }
318 lua_insert( L, 1); // key, val 318 lua_insert( L, 1); // key, val
319 return 2; 319 return 2;
320 } 320 }
321 } 321 }
322 lua_settop( L, top); // data keys 322 lua_settop( L, top); // data keys
323 } 323 }
324 // nothing to receive 324 // nothing to receive
325 return 0; 325 return 0;
326} 326}
327 327
328//in: linda_ud key mincount [maxcount] 328//in: linda_ud key mincount [maxcount]
329int keepercall_receive_batched( lua_State* L) 329int keepercall_receive_batched( lua_State* L)
330{ 330{
331 lua_Integer const min_count = lua_tointeger( L, 3); 331 lua_Integer const min_count = lua_tointeger( L, 3);
332 if( min_count > 0) 332 if( min_count > 0)
333 { 333 {
334 keeper_fifo* fifo; 334 keeper_fifo* fifo;
335 lua_Integer const max_count = luaL_optinteger( L, 4, min_count); 335 lua_Integer const max_count = luaL_optinteger( L, 4, min_count);
336 lua_settop( L, 2); // ud key 336 lua_settop( L, 2); // ud key
337 lua_insert( L, 1); // key ud 337 lua_insert( L, 1); // key ud
338 push_table( L, 2); // key ud fifos 338 push_table( L, 2); // key ud fifos
339 lua_remove( L, 2); // key fifos 339 lua_remove( L, 2); // key fifos
340 lua_pushvalue( L, 1); // key fifos key 340 lua_pushvalue( L, 1); // key fifos key
341 lua_rawget( L, 2); // key fifos fifo 341 lua_rawget( L, 2); // key fifos fifo
342 lua_remove( L, 2); // key fifo 342 lua_remove( L, 2); // key fifo
343 fifo = prepare_fifo_access( L, 2); // key fifo 343 fifo = prepare_fifo_access( L, 2); // key fifo
344 if( fifo != NULL && fifo->count >= min_count) 344 if( fifo != NULL && fifo->count >= min_count)
345 { 345 {
346 fifo_pop( L, fifo, __min( max_count, fifo->count)); // key ... 346 fifo_pop( L, fifo, __min( max_count, fifo->count)); // key ...
347 } 347 }
348 else 348 else
349 { 349 {
350 lua_settop( L, 0); 350 lua_settop( L, 0);
351 } 351 }
352 return lua_gettop( L); 352 return lua_gettop( L);
353 } 353 }
354 else 354 else
355 { 355 {
356 return 0; 356 return 0;
357 } 357 }
358} 358}
359 359
360// in: linda_ud key n 360// in: linda_ud key n
361// out: true or nil 361// out: true or nil
362int keepercall_limit( lua_State* L) 362int keepercall_limit( lua_State* L)
363{ 363{
364 keeper_fifo* fifo; 364 keeper_fifo* fifo;
365 lua_Integer limit = lua_tointeger( L, 3); 365 lua_Integer limit = lua_tointeger( L, 3);
366 push_table( L, 1); // ud key n fifos 366 push_table( L, 1); // ud key n fifos
367 lua_replace( L, 1); // fifos key n 367 lua_replace( L, 1); // fifos key n
368 lua_pop( L, 1); // fifos key 368 lua_pop( L, 1); // fifos key
369 lua_pushvalue( L, -1); // fifos key key 369 lua_pushvalue( L, -1); // fifos key key
370 lua_rawget( L, -3); // fifos key fifo|nil 370 lua_rawget( L, -3); // fifos key fifo|nil
371 fifo = (keeper_fifo*) lua_touserdata( L, -1); 371 fifo = (keeper_fifo*) lua_touserdata( L, -1);
372 if( fifo == NULL) 372 if( fifo == NULL)
373 { // fifos key nil 373 { // fifos key nil
374 lua_pop( L, 1); // fifos key 374 lua_pop( L, 1); // fifos key
375 fifo_new( L); // fifos key fifo 375 fifo_new( L); // fifos key fifo
376 fifo = (keeper_fifo*) lua_touserdata( L, -1); 376 fifo = (keeper_fifo*) lua_touserdata( L, -1);
377 lua_rawset( L, -3); // fifos 377 lua_rawset( L, -3); // fifos
378 } 378 }
379 // remove any clutter on the stack 379 // remove any clutter on the stack
380 lua_settop( L, 0); 380 lua_settop( L, 0);
381 // return true if we decide that blocked threads waiting to write on that key should be awakened 381 // return true if we decide that blocked threads waiting to write on that key should be awakened
382 // this is the case if we detect the key was full but it is no longer the case 382 // this is the case if we detect the key was full but it is no longer the case
383 if( 383 if(
384 ((fifo->limit >= 0) && (fifo->count >= fifo->limit)) // the key was full if limited and count exceeded the previous limit 384 ((fifo->limit >= 0) && (fifo->count >= fifo->limit)) // the key was full if limited and count exceeded the previous limit
385 && ((limit < 0) || (fifo->count < limit)) // the key is not full if unlimited or count is lower than the new limit 385 && ((limit < 0) || (fifo->count < limit)) // the key is not full if unlimited or count is lower than the new limit
386 ) 386 )
387 { 387 {
388 lua_pushboolean( L, 1); 388 lua_pushboolean( L, 1);
389 } 389 }
390 // set the new limit 390 // set the new limit
391 fifo->limit = limit; 391 fifo->limit = limit;
392 // return 0 or 1 value 392 // return 0 or 1 value
393 return lua_gettop( L); 393 return lua_gettop( L);
394} 394}
395 395
396//in: linda_ud key [[val] ...] 396//in: linda_ud key [[val] ...]
397//out: true or nil 397//out: true or nil
398int keepercall_set( lua_State* L) 398int keepercall_set( lua_State* L)
399{ 399{
400 bool_t should_wake_writers = FALSE; 400 bool_t should_wake_writers = FALSE;
401 STACK_GROW( L, 6); 401 STACK_GROW( L, 6);
402 402
403 // retrieve fifos associated with the linda 403 // retrieve fifos associated with the linda
404 push_table( L, 1); // ud key [val [, ...]] fifos 404 push_table( L, 1); // ud key [val [, ...]] fifos
405 lua_replace( L, 1); // fifos key [val [, ...]] 405 lua_replace( L, 1); // fifos key [val [, ...]]
406 406
407 // make sure we have a value on the stack 407 // make sure we have a value on the stack
408 if( lua_gettop( L) == 2) // fifos key 408 if( lua_gettop( L) == 2) // fifos key
409 { 409 {
410 keeper_fifo* fifo; 410 keeper_fifo* fifo;
411 lua_pushvalue( L, -1); // fifos key key 411 lua_pushvalue( L, -1); // fifos key key
412 lua_rawget( L, 1); // fifos key fifo|nil 412 lua_rawget( L, 1); // fifos key fifo|nil
413 // empty the fifo for the specified key: replace uservalue with a virgin table, reset counters, but leave limit unchanged! 413 // empty the fifo for the specified key: replace uservalue with a virgin table, reset counters, but leave limit unchanged!
414 fifo = (keeper_fifo*) lua_touserdata( L, -1); 414 fifo = (keeper_fifo*) lua_touserdata( L, -1);
415 if( fifo != NULL) // might be NULL if we set a nonexistent key to nil 415 if( fifo != NULL) // might be NULL if we set a nonexistent key to nil
416 { // fifos key fifo 416 { // fifos key fifo
417 if( fifo->limit < 0) // fifo limit value is the default (unlimited): we can totally remove it 417 if( fifo->limit < 0) // fifo limit value is the default (unlimited): we can totally remove it
418 { 418 {
419 lua_pop( L, 1); // fifos key 419 lua_pop( L, 1); // fifos key
420 lua_pushnil( L); // fifos key nil 420 lua_pushnil( L); // fifos key nil
421 lua_rawset( L, -3); // fifos 421 lua_rawset( L, -3); // fifos
422 } 422 }
423 else 423 else
424 { 424 {
425 // we create room if the fifo was full but it is no longer the case 425 // we create room if the fifo was full but it is no longer the case
426 should_wake_writers = (fifo->limit > 0) && (fifo->count >= fifo->limit); 426 should_wake_writers = (fifo->limit > 0) && (fifo->count >= fifo->limit);
427 lua_remove( L, -2); // fifos fifo 427 lua_remove( L, -2); // fifos fifo
428 lua_newtable( L); // fifos fifo {} 428 lua_newtable( L); // fifos fifo {}
429 lua_setiuservalue( L, -2, CONTENTS_TABLE); // fifos fifo 429 lua_setiuservalue( L, -2, CONTENTS_TABLE); // fifos fifo
430 fifo->first = 1; 430 fifo->first = 1;
431 fifo->count = 0; 431 fifo->count = 0;
432 } 432 }
433 } 433 }
434 } 434 }
435 else // set/replace contents stored at the specified key? 435 else // set/replace contents stored at the specified key?
436 { 436 {
437 lua_Integer count = lua_gettop( L) - 2; // number of items we want to store 437 lua_Integer count = lua_gettop( L) - 2; // number of items we want to store
438 keeper_fifo* fifo; // fifos key [val [, ...]] 438 keeper_fifo* fifo; // fifos key [val [, ...]]
439 lua_pushvalue( L, 2); // fifos key [val [, ...]] key 439 lua_pushvalue( L, 2); // fifos key [val [, ...]] key
440 lua_rawget( L, 1); // fifos key [val [, ...]] fifo|nil 440 lua_rawget( L, 1); // fifos key [val [, ...]] fifo|nil
441 fifo = (keeper_fifo*) lua_touserdata( L, -1); 441 fifo = (keeper_fifo*) lua_touserdata( L, -1);
442 if( fifo == NULL) // can be NULL if we store a value at a new key 442 if( fifo == NULL) // can be NULL if we store a value at a new key
443 { // fifos key [val [, ...]] nil 443 { // fifos key [val [, ...]] nil
444 // no need to wake writers in that case, because a writer can't wait on an inexistent key 444 // no need to wake writers in that case, because a writer can't wait on an inexistent key
445 lua_pop( L, 1); // fifos key [val [, ...]] 445 lua_pop( L, 1); // fifos key [val [, ...]]
446 fifo_new( L); // fifos key [val [, ...]] fifo 446 fifo_new( L); // fifos key [val [, ...]] fifo
447 lua_pushvalue( L, 2); // fifos key [val [, ...]] fifo key 447 lua_pushvalue( L, 2); // fifos key [val [, ...]] fifo key
448 lua_pushvalue( L, -2); // fifos key [val [, ...]] fifo key fifo 448 lua_pushvalue( L, -2); // fifos key [val [, ...]] fifo key fifo
449 lua_rawset( L, 1); // fifos key [val [, ...]] fifo 449 lua_rawset( L, 1); // fifos key [val [, ...]] fifo
450 } 450 }
451 else // the fifo exists, we just want to update its contents 451 else // the fifo exists, we just want to update its contents
452 { // fifos key [val [, ...]] fifo 452 { // fifos key [val [, ...]] fifo
453 // we create room if the fifo was full but it is no longer the case 453 // we create room if the fifo was full but it is no longer the case
454 should_wake_writers = (fifo->limit > 0) && (fifo->count >= fifo->limit) && (count < fifo->limit); 454 should_wake_writers = (fifo->limit > 0) && (fifo->count >= fifo->limit) && (count < fifo->limit);
455 // empty the fifo for the specified key: replace uservalue with a virgin table, reset counters, but leave limit unchanged! 455 // empty the fifo for the specified key: replace uservalue with a virgin table, reset counters, but leave limit unchanged!
456 lua_newtable( L); // fifos key [val [, ...]] fifo {} 456 lua_newtable( L); // fifos key [val [, ...]] fifo {}
457 lua_setiuservalue( L, -2, CONTENTS_TABLE); // fifos key [val [, ...]] fifo 457 lua_setiuservalue( L, -2, CONTENTS_TABLE); // fifos key [val [, ...]] fifo
458 fifo->first = 1; 458 fifo->first = 1;
459 fifo->count = 0; 459 fifo->count = 0;
460 } 460 }
461 fifo = prepare_fifo_access( L, -1); 461 fifo = prepare_fifo_access( L, -1);
462 // move the fifo below the values we want to store 462 // move the fifo below the values we want to store
463 lua_insert( L, 3); // fifos key fifo [val [, ...]] 463 lua_insert( L, 3); // fifos key fifo [val [, ...]]
464 fifo_push( L, fifo, count); // fifos key fifo 464 fifo_push( L, fifo, count); // fifos key fifo
465 } 465 }
466 return should_wake_writers ? (lua_pushboolean( L, 1), 1) : 0; 466 return should_wake_writers ? (lua_pushboolean( L, 1), 1) : 0;
467} 467}
468 468
469// in: linda_ud key [count] 469// in: linda_ud key [count]
470// out: at most <count> values 470// out: at most <count> values
471int keepercall_get( lua_State* L) 471int keepercall_get( lua_State* L)
472{ 472{
473 keeper_fifo* fifo; 473 keeper_fifo* fifo;
474 lua_Integer count = 1; 474 lua_Integer count = 1;
475 if( lua_gettop( L) == 3) // ud key count 475 if( lua_gettop( L) == 3) // ud key count
476 { 476 {
477 count = lua_tointeger( L, 3); 477 count = lua_tointeger( L, 3);
478 lua_pop( L, 1); // ud key 478 lua_pop( L, 1); // ud key
479 } 479 }
480 push_table( L, 1); // ud key fifos 480 push_table( L, 1); // ud key fifos
481 lua_replace( L, 1); // fifos key 481 lua_replace( L, 1); // fifos key
482 lua_rawget( L, 1); // fifos fifo 482 lua_rawget( L, 1); // fifos fifo
483 fifo = prepare_fifo_access( L, -1); // fifos fifo 483 fifo = prepare_fifo_access( L, -1); // fifos fifo
484 if( fifo != NULL && fifo->count > 0) 484 if( fifo != NULL && fifo->count > 0)
485 { 485 {
486 lua_remove( L, 1); // fifo 486 lua_remove( L, 1); // fifo
487 count = __min( count, fifo->count); 487 count = __min( count, fifo->count);
488 // read <count> value off the fifo 488 // read <count> value off the fifo
489 fifo_peek( L, fifo, count); // fifo ... 489 fifo_peek( L, fifo, count); // fifo ...
490 return (int) count; 490 return (int) count;
491 } 491 }
492 // no fifo was ever registered for this key, or it is empty 492 // no fifo was ever registered for this key, or it is empty
493 return 0; 493 return 0;
494} 494}
495 495
496// in: linda_ud [, key [, ...]] 496// in: linda_ud [, key [, ...]]
497int keepercall_count( lua_State* L) 497int keepercall_count( lua_State* L)
498{ 498{
499 push_table( L, 1); // ud keys fifos 499 push_table( L, 1); // ud keys fifos
500 switch( lua_gettop( L)) 500 switch( lua_gettop( L))
501 { 501 {
502 // no key is specified: return a table giving the count of all known keys 502 // no key is specified: return a table giving the count of all known keys
503 case 2: // ud fifos 503 case 2: // ud fifos
504 lua_newtable( L); // ud fifos out 504 lua_newtable( L); // ud fifos out
505 lua_replace( L, 1); // out fifos 505 lua_replace( L, 1); // out fifos
506 lua_pushnil( L); // out fifos nil 506 lua_pushnil( L); // out fifos nil
507 while( lua_next( L, 2)) // out fifos key fifo 507 while( lua_next( L, 2)) // out fifos key fifo
508 { 508 {
509 keeper_fifo* fifo = prepare_fifo_access( L, -1); // out fifos key fifo 509 keeper_fifo* fifo = prepare_fifo_access( L, -1); // out fifos key fifo
510 lua_pop( L, 1); // out fifos key 510 lua_pop( L, 1); // out fifos key
511 lua_pushvalue( L, -1); // out fifos key key 511 lua_pushvalue( L, -1); // out fifos key key
512 lua_pushinteger( L, fifo->count); // out fifos key key count 512 lua_pushinteger( L, fifo->count); // out fifos key key count
513 lua_rawset( L, -5); // out fifos key 513 lua_rawset( L, -5); // out fifos key
514 } 514 }
515 lua_pop( L, 1); // out 515 lua_pop( L, 1); // out
516 break; 516 break;
517 517
518 // 1 key is specified: return its count 518 // 1 key is specified: return its count
519 case 3: // ud key fifos 519 case 3: // ud key fifos
520 { 520 {
521 keeper_fifo* fifo; 521 keeper_fifo* fifo;
522 lua_replace( L, 1); // fifos key 522 lua_replace( L, 1); // fifos key
523 lua_rawget( L, -2); // fifos fifo|nil 523 lua_rawget( L, -2); // fifos fifo|nil
524 if( lua_isnil( L, -1)) // the key is unknown 524 if( lua_isnil( L, -1)) // the key is unknown
525 { // fifos nil 525 { // fifos nil
526 lua_remove( L, -2); // nil 526 lua_remove( L, -2); // nil
527 } 527 }
528 else // the key is known 528 else // the key is known
529 { // fifos fifo 529 { // fifos fifo
530 fifo = prepare_fifo_access( L, -1); // fifos fifo 530 fifo = prepare_fifo_access( L, -1); // fifos fifo
531 lua_pushinteger( L, fifo->count); // fifos fifo count 531 lua_pushinteger( L, fifo->count); // fifos fifo count
532 lua_replace( L, -3); // count fifo 532 lua_replace( L, -3); // count fifo
533 lua_pop( L, 1); // count 533 lua_pop( L, 1); // count
534 } 534 }
535 } 535 }
536 break; 536 break;
537 537
538 // a variable number of keys is specified: return a table of their counts 538 // a variable number of keys is specified: return a table of their counts
539 default: // ud keys fifos 539 default: // ud keys fifos
540 lua_newtable( L); // ud keys fifos out 540 lua_newtable( L); // ud keys fifos out
541 lua_replace( L, 1); // out keys fifos 541 lua_replace( L, 1); // out keys fifos
542 // shifts all keys up in the stack. potentially slow if there are a lot of them, but then it should be bearable 542 // shifts all keys up in the stack. potentially slow if there are a lot of them, but then it should be bearable
543 lua_insert( L, 2); // out fifos keys 543 lua_insert( L, 2); // out fifos keys
544 while( lua_gettop( L) > 2) 544 while( lua_gettop( L) > 2)
545 { 545 {
546 keeper_fifo* fifo; 546 keeper_fifo* fifo;
547 lua_pushvalue( L, -1); // out fifos keys key 547 lua_pushvalue( L, -1); // out fifos keys key
548 lua_rawget( L, 2); // out fifos keys fifo|nil 548 lua_rawget( L, 2); // out fifos keys fifo|nil
549 fifo = prepare_fifo_access( L, -1); // out fifos keys fifo|nil 549 fifo = prepare_fifo_access( L, -1); // out fifos keys fifo|nil
550 lua_pop( L, 1); // out fifos keys 550 lua_pop( L, 1); // out fifos keys
551 if( fifo != NULL) // the key is known 551 if( fifo != NULL) // the key is known
552 { 552 {
553 lua_pushinteger( L, fifo->count); // out fifos keys count 553 lua_pushinteger( L, fifo->count); // out fifos keys count
554 lua_rawset( L, 1); // out fifos keys 554 lua_rawset( L, 1); // out fifos keys
555 } 555 }
556 else // the key is unknown 556 else // the key is unknown
557 { 557 {
558 lua_pop( L, 1); // out fifos keys 558 lua_pop( L, 1); // out fifos keys
559 } 559 }
560 } 560 }
561 lua_pop( L, 1); // out 561 lua_pop( L, 1); // out
562 } 562 }
563 ASSERT_L( lua_gettop( L) == 1); 563 ASSERT_L( lua_gettop( L) == 1);
564 return 1; 564 return 1;
565} 565}
566 566
567//################################################################################### 567//###################################################################################
@@ -582,41 +582,41 @@ int keepercall_count( lua_State* L)
582// called as __gc for the keepers array userdata 582// called as __gc for the keepers array userdata
583void close_keepers( Universe* U, lua_State* L) 583void close_keepers( Universe* U, lua_State* L)
584{ 584{
585 if( U->keepers != NULL) 585 if( U->keepers != NULL)
586 { 586 {
587 int i; 587 int i;
588 int nbKeepers = U->keepers->nb_keepers; 588 int nbKeepers = U->keepers->nb_keepers;
589 // NOTE: imagine some keeper state N+1 currently holds a linda that uses another keeper N, and a _gc that will make use of it 589 // NOTE: imagine some keeper state N+1 currently holds a linda that uses another keeper N, and a _gc that will make use of it
590 // when keeper N+1 is closed, object is GCed, linda operation is called, which attempts to acquire keeper N, whose Lua state no longer exists 590 // when keeper N+1 is closed, object is GCed, linda operation is called, which attempts to acquire keeper N, whose Lua state no longer exists
591 // in that case, the linda operation should do nothing. which means that these operations must check for keeper acquisition success 591 // in that case, the linda operation should do nothing. which means that these operations must check for keeper acquisition success
592 // which is early-outed with a U->keepers->nbKeepers null-check 592 // which is early-outed with a U->keepers->nbKeepers null-check
593 U->keepers->nb_keepers = 0; 593 U->keepers->nb_keepers = 0;
594 for( i = 0; i < nbKeepers; ++ i) 594 for( i = 0; i < nbKeepers; ++ i)
595 { 595 {
596 lua_State* K = U->keepers->keeper_array[i].L; 596 lua_State* K = U->keepers->keeper_array[i].L;
597 U->keepers->keeper_array[i].L = NULL; 597 U->keepers->keeper_array[i].L = NULL;
598 if( K != NULL) 598 if( K != NULL)
599 { 599 {
600 lua_close( K); 600 lua_close( K);
601 } 601 }
602 else 602 else
603 { 603 {
604 // detected partial init: destroy only the mutexes that got initialized properly 604 // detected partial init: destroy only the mutexes that got initialized properly
605 nbKeepers = i; 605 nbKeepers = i;
606 } 606 }
607 } 607 }
608 for( i = 0; i < nbKeepers; ++ i) 608 for( i = 0; i < nbKeepers; ++ i)
609 { 609 {
610 MUTEX_FREE( &U->keepers->keeper_array[i].keeper_cs); 610 MUTEX_FREE( &U->keepers->keeper_array[i].keeper_cs);
611 } 611 }
612 // free the keeper bookkeeping structure 612 // free the keeper bookkeeping structure
613 { 613 {
614 void* allocUD; 614 void* allocUD;
615 lua_Alloc allocF = lua_getallocf( L, &allocUD); 615 lua_Alloc allocF = lua_getallocf( L, &allocUD);
616 allocF( allocUD, U->keepers, sizeof( Keepers) + (nbKeepers - 1) * sizeof( Keeper), 0); 616 allocF( allocUD, U->keepers, sizeof( Keepers) + (nbKeepers - 1) * sizeof( Keeper), 0);
617 U->keepers = NULL; 617 U->keepers = NULL;
618 } 618 }
619 } 619 }
620} 620}
621 621
622/* 622/*
@@ -632,156 +632,156 @@ void close_keepers( Universe* U, lua_State* L)
632 */ 632 */
633void init_keepers( Universe* U, lua_State* L) 633void init_keepers( Universe* U, lua_State* L)
634{ 634{
635 int i; 635 int i;
636 int nb_keepers; 636 int nb_keepers;
637 void* allocUD; 637 void* allocUD;
638 lua_Alloc allocF = lua_getallocf( L, &allocUD); 638 lua_Alloc allocF = lua_getallocf( L, &allocUD);
639 639
640 STACK_CHECK( L, 0); // L K 640 STACK_CHECK( L, 0); // L K
641 lua_getfield( L, 1, "nb_keepers"); // nb_keepers 641 lua_getfield( L, 1, "nb_keepers"); // nb_keepers
642 nb_keepers = (int) lua_tointeger( L, -1); 642 nb_keepers = (int) lua_tointeger( L, -1);
643 lua_pop( L, 1); // 643 lua_pop( L, 1); //
644 if( nb_keepers < 1) 644 if( nb_keepers < 1)
645 { 645 {
646 (void) luaL_error( L, "Bad number of keepers (%d)", nb_keepers); 646 (void) luaL_error( L, "Bad number of keepers (%d)", nb_keepers);
647 } 647 }
648 648
649 // Keepers contains an array of 1 s_Keeper, adjust for the actual number of keeper states 649 // Keepers contains an array of 1 s_Keeper, adjust for the actual number of keeper states
650 { 650 {
651 size_t const bytes = sizeof( Keepers) + (nb_keepers - 1) * sizeof( Keeper); 651 size_t const bytes = sizeof( Keepers) + (nb_keepers - 1) * sizeof( Keeper);
652 U->keepers = (Keepers*) allocF( allocUD, NULL, 0, bytes); 652 U->keepers = (Keepers*) allocF( allocUD, NULL, 0, bytes);
653 if( U->keepers == NULL) 653 if( U->keepers == NULL)
654 { 654 {
655 (void) luaL_error( L, "init_keepers() failed while creating keeper array; out of memory"); 655 (void) luaL_error( L, "init_keepers() failed while creating keeper array; out of memory");
656 return; 656 return;
657 } 657 }
658 memset( U->keepers, 0, bytes); 658 memset( U->keepers, 0, bytes);
659 U->keepers->nb_keepers = nb_keepers; 659 U->keepers->nb_keepers = nb_keepers;
660 } 660 }
661 for( i = 0; i < nb_keepers; ++ i) // keepersUD 661 for( i = 0; i < nb_keepers; ++ i) // keepersUD
662 { 662 {
663 // note that we will leak K if we raise an error later 663 // note that we will leak K if we raise an error later
664 lua_State* K = create_state( U, L); 664 lua_State* K = create_state( U, L);
665 if( K == NULL) 665 if( K == NULL)
666 { 666 {
667 (void) luaL_error( L, "init_keepers() failed while creating keeper states; out of memory"); 667 (void) luaL_error( L, "init_keepers() failed while creating keeper states; out of memory");
668 return; 668 return;
669 } 669 }
670 670
671 U->keepers->keeper_array[i].L = K; 671 U->keepers->keeper_array[i].L = K;
672 // we can trigger a GC from inside keeper_call(), where a keeper is acquired 672 // we can trigger a GC from inside keeper_call(), where a keeper is acquired
673 // from there, GC can collect a linda, which would acquire the keeper again, and deadlock the thread. 673 // from there, GC can collect a linda, which would acquire the keeper again, and deadlock the thread.
674 // therefore, we need a recursive mutex. 674 // therefore, we need a recursive mutex.
675 MUTEX_RECURSIVE_INIT( &U->keepers->keeper_array[i].keeper_cs); 675 MUTEX_RECURSIVE_INIT( &U->keepers->keeper_array[i].keeper_cs);
676 676
677 STACK_CHECK( K, 0); 677 STACK_CHECK( K, 0);
678 678
679 // copy the universe pointer in the keeper itself 679 // copy the universe pointer in the keeper itself
680 universe_store( K, U); 680 universe_store( K, U);
681 STACK_MID( K, 0); 681 STACK_MID( K, 0);
682 682
683 // make sure 'package' is initialized in keeper states, so that we have require() 683 // make sure 'package' is initialized in keeper states, so that we have require()
684 // this because this is needed when transferring deep userdata object 684 // this because this is needed when transferring deep userdata object
685 luaL_requiref( K, "package", luaopen_package, 1); // package 685 luaL_requiref( K, "package", luaopen_package, 1); // package
686 lua_pop( K, 1); // 686 lua_pop( K, 1); //
687 STACK_MID( K, 0); 687 STACK_MID( K, 0);
688 serialize_require( DEBUGSPEW_PARAM_COMMA( U) K); 688 serialize_require( DEBUGSPEW_PARAM_COMMA( U) K);
689 STACK_MID( K, 0); 689 STACK_MID( K, 0);
690 690
691 // copy package.path and package.cpath from the source state 691 // copy package.path and package.cpath from the source state
692 lua_getglobal( L, "package"); // "..." keepersUD package 692 lua_getglobal( L, "package"); // "..." keepersUD package
693 if( !lua_isnil( L, -1)) 693 if( !lua_isnil( L, -1))
694 { 694 {
695 // when copying with mode eLM_ToKeeper, error message is pushed at the top of the stack, not raised immediately 695 // when copying with mode eLM_ToKeeper, error message is pushed at the top of the stack, not raised immediately
696 if( luaG_inter_copy_package( U, L, K, -1, eLM_ToKeeper)) 696 if( luaG_inter_copy_package( U, L, K, -1, eLM_ToKeeper))
697 { 697 {
698 // if something went wrong, the error message is at the top of the stack 698 // if something went wrong, the error message is at the top of the stack
699 lua_remove( L, -2); // error_msg 699 lua_remove( L, -2); // error_msg
700 (void) lua_error( L); 700 (void) lua_error( L);
701 return; 701 return;
702 } 702 }
703 } 703 }
704 lua_pop( L, 1); // 704 lua_pop( L, 1); //
705 STACK_MID( L, 0); 705 STACK_MID( L, 0);
706 706
707 // attempt to call on_state_create(), if we have one and it is a C function 707 // attempt to call on_state_create(), if we have one and it is a C function
708 // (only support a C function because we can't transfer executable Lua code in keepers) 708 // (only support a C function because we can't transfer executable Lua code in keepers)
709 // will raise an error in L in case of problem 709 // will raise an error in L in case of problem
710 call_on_state_create( U, K, L, eLM_ToKeeper); 710 call_on_state_create( U, K, L, eLM_ToKeeper);
711 711
712 // to see VM name in Decoda debugger 712 // to see VM name in Decoda debugger
713 lua_pushfstring( K, "Keeper #%d", i + 1); // "Keeper #n" 713 lua_pushfstring( K, "Keeper #%d", i + 1); // "Keeper #n"
714 lua_setglobal( K, "decoda_name"); // 714 lua_setglobal( K, "decoda_name"); //
715 715
716 // create the fifos table in the keeper state 716 // create the fifos table in the keeper state
717 REGISTRY_SET( K, FIFOS_KEY, lua_newtable( K)); 717 REGISTRY_SET( K, FIFOS_KEY, lua_newtable( K));
718 STACK_END( K, 0); 718 STACK_END( K, 0);
719 } 719 }
720 STACK_END( L, 0); 720 STACK_END( L, 0);
721} 721}
722 722
723// should be called only when inside a keeper_acquire/keeper_release pair (see linda_protected_call) 723// should be called only when inside a keeper_acquire/keeper_release pair (see linda_protected_call)
724Keeper* which_keeper(Keepers* keepers_, ptrdiff_t magic_) 724Keeper* which_keeper(Keepers* keepers_, ptrdiff_t magic_)
725{ 725{
726 int const nbKeepers = keepers_->nb_keepers; 726 int const nbKeepers = keepers_->nb_keepers;
727 unsigned int i = (unsigned int)((magic_ >> KEEPER_MAGIC_SHIFT) % nbKeepers); 727 unsigned int i = (unsigned int)((magic_ >> KEEPER_MAGIC_SHIFT) % nbKeepers);
728 return &keepers_->keeper_array[i]; 728 return &keepers_->keeper_array[i];
729} 729}
730 730
731Keeper* keeper_acquire( Keepers* keepers_, ptrdiff_t magic_) 731Keeper* keeper_acquire( Keepers* keepers_, ptrdiff_t magic_)
732{ 732{
733 int const nbKeepers = keepers_->nb_keepers; 733 int const nbKeepers = keepers_->nb_keepers;
734 // can be 0 if this happens during main state shutdown (lanes is being GC'ed -> no keepers) 734 // can be 0 if this happens during main state shutdown (lanes is being GC'ed -> no keepers)
735 if( nbKeepers == 0) 735 if( nbKeepers == 0)
736 { 736 {
737 return NULL; 737 return NULL;
738 } 738 }
739 else 739 else
740 { 740 {
741 /* 741 /*
742 * Any hashing will do that maps pointers to 0..GNbKeepers-1 742 * Any hashing will do that maps pointers to 0..GNbKeepers-1
743 * consistently. 743 * consistently.
744 * 744 *
745 * Pointers are often aligned by 8 or so - ignore the low order bits 745 * Pointers are often aligned by 8 or so - ignore the low order bits
746 * have to cast to unsigned long to avoid compilation warnings about loss of data when converting pointer-to-integer 746 * have to cast to unsigned long to avoid compilation warnings about loss of data when converting pointer-to-integer
747 */ 747 */
748 unsigned int i = (unsigned int)((magic_ >> KEEPER_MAGIC_SHIFT) % nbKeepers); 748 unsigned int i = (unsigned int)((magic_ >> KEEPER_MAGIC_SHIFT) % nbKeepers);
749 Keeper* K = &keepers_->keeper_array[i]; 749 Keeper* K = &keepers_->keeper_array[i];
750 750
751 MUTEX_LOCK( &K->keeper_cs); 751 MUTEX_LOCK( &K->keeper_cs);
752 //++ K->count; 752 //++ K->count;
753 return K; 753 return K;
754 } 754 }
755} 755}
756 756
757void keeper_release( Keeper* K) 757void keeper_release( Keeper* K)
758{ 758{
759 //-- K->count; 759 //-- K->count;
760 if( K) MUTEX_UNLOCK( &K->keeper_cs); 760 if( K) MUTEX_UNLOCK( &K->keeper_cs);
761} 761}
762 762
763void keeper_toggle_nil_sentinels( lua_State* L, int val_i_, LookupMode const mode_) 763void keeper_toggle_nil_sentinels( lua_State* L, int val_i_, LookupMode const mode_)
764{ 764{
765 int i, n = lua_gettop( L); 765 int i, n = lua_gettop( L);
766 for( i = val_i_; i <= n; ++ i) 766 for( i = val_i_; i <= n; ++ i)
767 { 767 {
768 if( mode_ == eLM_ToKeeper) 768 if( mode_ == eLM_ToKeeper)
769 { 769 {
770 if( lua_isnil( L, i)) 770 if( lua_isnil( L, i))
771 { 771 {
772 push_unique_key( L, NIL_SENTINEL); 772 push_unique_key( L, NIL_SENTINEL);
773 lua_replace( L, i); 773 lua_replace( L, i);
774 } 774 }
775 } 775 }
776 else 776 else
777 { 777 {
778 if( equal_unique_key( L, i, NIL_SENTINEL)) 778 if( equal_unique_key( L, i, NIL_SENTINEL))
779 { 779 {
780 lua_pushnil( L); 780 lua_pushnil( L);
781 lua_replace( L, i); 781 lua_replace( L, i);
782 } 782 }
783 } 783 }
784 } 784 }
785} 785}
786 786
787/* 787/*
@@ -795,31 +795,31 @@ void keeper_toggle_nil_sentinels( lua_State* L, int val_i_, LookupMode const mod
795*/ 795*/
796int keeper_call( Universe* U, lua_State* K, keeper_api_t func_, lua_State* L, void* linda, uint_t starting_index) 796int keeper_call( Universe* U, lua_State* K, keeper_api_t func_, lua_State* L, void* linda, uint_t starting_index)
797{ 797{
798 int const args = starting_index ? (lua_gettop( L) - starting_index + 1) : 0; 798 int const args = starting_index ? (lua_gettop( L) - starting_index + 1) : 0;
799 int const Ktos = lua_gettop( K); 799 int const Ktos = lua_gettop( K);
800 int retvals = -1; 800 int retvals = -1;
801 801
802 STACK_GROW( K, 2); 802 STACK_GROW( K, 2);
803 803
804 PUSH_KEEPER_FUNC( K, func_); 804 PUSH_KEEPER_FUNC( K, func_);
805 805
806 lua_pushlightuserdata( K, linda); 806 lua_pushlightuserdata( K, linda);
807 807
808 if( (args == 0) || luaG_inter_copy( U, L, K, args, eLM_ToKeeper) == 0) // L->K 808 if( (args == 0) || luaG_inter_copy( U, L, K, args, eLM_ToKeeper) == 0) // L->K
809 { 809 {
810 lua_call( K, 1 + args, LUA_MULTRET); 810 lua_call( K, 1 + args, LUA_MULTRET);
811 811
812 retvals = lua_gettop( K) - Ktos; 812 retvals = lua_gettop( K) - Ktos;
813 // note that this can raise a luaL_error while the keeper state (and its mutex) is acquired 813 // note that this can raise a luaL_error while the keeper state (and its mutex) is acquired
814 // this may interrupt a lane, causing the destruction of the underlying OS thread 814 // this may interrupt a lane, causing the destruction of the underlying OS thread
815 // after this, another lane making use of this keeper can get an error code from the mutex-locking function 815 // after this, another lane making use of this keeper can get an error code from the mutex-locking function
816 // when attempting to grab the mutex again (WINVER <= 0x400 does this, but locks just fine, I don't know about pthread) 816 // when attempting to grab the mutex again (WINVER <= 0x400 does this, but locks just fine, I don't know about pthread)
817 if( (retvals > 0) && luaG_inter_move( U, K, L, retvals, eLM_FromKeeper) != 0) // K->L 817 if( (retvals > 0) && luaG_inter_move( U, K, L, retvals, eLM_FromKeeper) != 0) // K->L
818 { 818 {
819 retvals = -1; 819 retvals = -1;
820 } 820 }
821 } 821 }
822 // whatever happens, restore the stack to where it was at the origin 822 // whatever happens, restore the stack to where it was at the origin
823 lua_settop( K, Ktos); 823 lua_settop( K, Ktos);
824 return retvals; 824 return retvals;
825} 825}
diff --git a/src/keeper.h b/src/keeper.h
index 60410da..8c09322 100644
--- a/src/keeper.h
+++ b/src/keeper.h
@@ -13,16 +13,16 @@ typedef enum eLookupMode LookupMode;
13 13
14struct s_Keeper 14struct s_Keeper
15{ 15{
16 MUTEX_T keeper_cs; 16 MUTEX_T keeper_cs;
17 lua_State* L; 17 lua_State* L;
18 //int count; 18 //int count;
19}; 19};
20typedef struct s_Keeper Keeper; 20typedef struct s_Keeper Keeper;
21 21
22struct s_Keepers 22struct s_Keepers
23{ 23{
24 int nb_keepers; 24 int nb_keepers;
25 Keeper keeper_array[1]; 25 Keeper keeper_array[1];
26}; 26};
27typedef struct s_Keepers Keepers; 27typedef struct s_Keepers Keepers;
28 28
diff --git a/src/lanes.c b/src/lanes.c
index e697bf5..c5b6c4f 100644
--- a/src/lanes.c
+++ b/src/lanes.c
@@ -113,16 +113,16 @@ THE SOFTWARE.
113// intern the debug name in the specified lua state so that the pointer remains valid when the lane's state is closed 113// intern the debug name in the specified lua state so that the pointer remains valid when the lane's state is closed
114static void securize_debug_threadname( lua_State* L, Lane* s) 114static void securize_debug_threadname( lua_State* L, Lane* s)
115{ 115{
116 STACK_CHECK( L, 0); 116 STACK_CHECK( L, 0);
117 STACK_GROW( L, 3); 117 STACK_GROW( L, 3);
118 lua_getiuservalue( L, 1, 1); 118 lua_getiuservalue( L, 1, 1);
119 lua_newtable( L); 119 lua_newtable( L);
120 // Lua 5.1 can't do 's->debug_name = lua_pushstring( L, s->debug_name);' 120 // Lua 5.1 can't do 's->debug_name = lua_pushstring( L, s->debug_name);'
121 lua_pushstring( L, s->debug_name); 121 lua_pushstring( L, s->debug_name);
122 s->debug_name = lua_tostring( L, -1); 122 s->debug_name = lua_tostring( L, -1);
123 lua_rawset( L, -3); 123 lua_rawset( L, -3);
124 lua_pop( L, 1); 124 lua_pop( L, 1);
125 STACK_END( L, 0); 125 STACK_END( L, 0);
126} 126}
127 127
128#if ERROR_FULL_STACK 128#if ERROR_FULL_STACK
@@ -154,24 +154,24 @@ struct s_Linda;
154*/ 154*/
155static bool_t push_registry_table( lua_State* L, UniqueKey key, bool_t create) 155static bool_t push_registry_table( lua_State* L, UniqueKey key, bool_t create)
156{ 156{
157 STACK_GROW( L, 3); 157 STACK_GROW( L, 3);
158 STACK_CHECK( L, 0); 158 STACK_CHECK( L, 0);
159 159
160 REGISTRY_GET( L, key); // ? 160 REGISTRY_GET( L, key); // ?
161 if( lua_isnil( L, -1)) // nil? 161 if( lua_isnil( L, -1)) // nil?
162 { 162 {
163 lua_pop( L, 1); // 163 lua_pop( L, 1); //
164 164
165 if( !create) 165 if( !create)
166 { 166 {
167 return FALSE; 167 return FALSE;
168 } 168 }
169 169
170 lua_newtable( L); // t 170 lua_newtable( L); // t
171 REGISTRY_SET( L, key, lua_pushvalue( L, -2)); 171 REGISTRY_SET( L, key, lua_pushvalue( L, -2));
172 } 172 }
173 STACK_END( L, 1); 173 STACK_END( L, 1);
174 return TRUE; // table pushed 174 return TRUE; // table pushed
175} 175}
176 176
177#if HAVE_LANE_TRACKING 177#if HAVE_LANE_TRACKING
@@ -187,14 +187,14 @@ static bool_t push_registry_table( lua_State* L, UniqueKey key, bool_t create)
187static void tracking_add( Lane* s) 187static void tracking_add( Lane* s)
188{ 188{
189 189
190 MUTEX_LOCK( &s->U->tracking_cs); 190 MUTEX_LOCK( &s->U->tracking_cs);
191 { 191 {
192 assert( s->tracking_next == NULL); 192 assert( s->tracking_next == NULL);
193 193
194 s->tracking_next = s->U->tracking_first; 194 s->tracking_next = s->U->tracking_first;
195 s->U->tracking_first = s; 195 s->U->tracking_first = s;
196 } 196 }
197 MUTEX_UNLOCK( &s->U->tracking_cs); 197 MUTEX_UNLOCK( &s->U->tracking_cs);
198} 198}
199 199
200/* 200/*
@@ -202,33 +202,33 @@ static void tracking_add( Lane* s)
202 */ 202 */
203static bool_t tracking_remove( Lane* s) 203static bool_t tracking_remove( Lane* s)
204{ 204{
205 bool_t found = FALSE; 205 bool_t found = FALSE;
206 MUTEX_LOCK( &s->U->tracking_cs); 206 MUTEX_LOCK( &s->U->tracking_cs);
207 { 207 {
208 // Make sure (within the MUTEX) that we actually are in the chain 208 // Make sure (within the MUTEX) that we actually are in the chain
209 // still (at process exit they will remove us from chain and then 209 // still (at process exit they will remove us from chain and then
210 // cancel/kill). 210 // cancel/kill).
211 // 211 //
212 if( s->tracking_next != NULL) 212 if( s->tracking_next != NULL)
213 { 213 {
214 Lane** ref = (Lane**) &s->U->tracking_first; 214 Lane** ref = (Lane**) &s->U->tracking_first;
215 215
216 while( *ref != TRACKING_END) 216 while( *ref != TRACKING_END)
217 { 217 {
218 if( *ref == s) 218 if( *ref == s)
219 { 219 {
220 *ref = s->tracking_next; 220 *ref = s->tracking_next;
221 s->tracking_next = NULL; 221 s->tracking_next = NULL;
222 found = TRUE; 222 found = TRUE;
223 break; 223 break;
224 } 224 }
225 ref = (Lane**) &((*ref)->tracking_next); 225 ref = (Lane**) &((*ref)->tracking_next);
226 } 226 }
227 assert( found); 227 assert( found);
228 } 228 }
229 } 229 }
230 MUTEX_UNLOCK( &s->U->tracking_cs); 230 MUTEX_UNLOCK( &s->U->tracking_cs);
231 return found; 231 return found;
232} 232}
233 233
234#endif // HAVE_LANE_TRACKING 234#endif // HAVE_LANE_TRACKING
@@ -238,22 +238,22 @@ static bool_t tracking_remove( Lane* s)
238 238
239static void lane_cleanup( Lane* s) 239static void lane_cleanup( Lane* s)
240{ 240{
241 // Clean up after a (finished) thread 241 // Clean up after a (finished) thread
242 // 242 //
243#if THREADWAIT_METHOD == THREADWAIT_CONDVAR 243#if THREADWAIT_METHOD == THREADWAIT_CONDVAR
244 SIGNAL_FREE( &s->done_signal); 244 SIGNAL_FREE( &s->done_signal);
245 MUTEX_FREE( &s->done_lock); 245 MUTEX_FREE( &s->done_lock);
246#endif // THREADWAIT_METHOD == THREADWAIT_CONDVAR 246#endif // THREADWAIT_METHOD == THREADWAIT_CONDVAR
247 247
248#if HAVE_LANE_TRACKING 248#if HAVE_LANE_TRACKING
249 if( s->U->tracking_first != NULL) 249 if( s->U->tracking_first != NULL)
250 { 250 {
251 // Lane was cleaned up, no need to handle at process termination 251 // Lane was cleaned up, no need to handle at process termination
252 tracking_remove( s); 252 tracking_remove( s);
253 } 253 }
254#endif // HAVE_LANE_TRACKING 254#endif // HAVE_LANE_TRACKING
255 255
256 free( s); 256 free( s);
257} 257}
258 258
259/* 259/*
@@ -272,16 +272,16 @@ static void lane_cleanup( Lane* s)
272// 272//
273LUAG_FUNC( set_finalizer) 273LUAG_FUNC( set_finalizer)
274{ 274{
275 luaL_argcheck( L, lua_isfunction( L, 1), 1, "finalizer should be a function"); 275 luaL_argcheck( L, lua_isfunction( L, 1), 1, "finalizer should be a function");
276 luaL_argcheck( L, lua_gettop( L) == 1, 1, "too many arguments"); 276 luaL_argcheck( L, lua_gettop( L) == 1, 1, "too many arguments");
277 // Get the current finalizer table (if any) 277 // Get the current finalizer table (if any)
278 push_registry_table( L, FINALIZER_REGKEY, TRUE /*do create if none*/); // finalizer {finalisers} 278 push_registry_table( L, FINALIZER_REGKEY, TRUE /*do create if none*/); // finalizer {finalisers}
279 STACK_GROW( L, 2); 279 STACK_GROW( L, 2);
280 lua_pushinteger( L, lua_rawlen( L, -1) + 1); // finalizer {finalisers} idx 280 lua_pushinteger( L, lua_rawlen( L, -1) + 1); // finalizer {finalisers} idx
281 lua_pushvalue( L, 1); // finalizer {finalisers} idx finalizer 281 lua_pushvalue( L, 1); // finalizer {finalisers} idx finalizer
282 lua_rawset( L, -3); // finalizer {finalisers} 282 lua_rawset( L, -3); // finalizer {finalisers}
283 lua_pop( L, 2); // 283 lua_pop( L, 2); //
284 return 0; 284 return 0;
285} 285}
286 286
287 287
@@ -302,74 +302,74 @@ static void push_stack_trace( lua_State* L, int rc_, int stk_base_);
302 302
303static int run_finalizers( lua_State* L, int lua_rc) 303static int run_finalizers( lua_State* L, int lua_rc)
304{ 304{
305 int finalizers_index; 305 int finalizers_index;
306 int n; 306 int n;
307 int err_handler_index = 0; 307 int err_handler_index = 0;
308 int rc = LUA_OK; // ... 308 int rc = LUA_OK; // ...
309 if( !push_registry_table( L, FINALIZER_REGKEY, FALSE)) // ... finalizers? 309 if( !push_registry_table( L, FINALIZER_REGKEY, FALSE)) // ... finalizers?
310 { 310 {
311 return 0; // no finalizers 311 return 0; // no finalizers
312 } 312 }
313 313
314 STACK_GROW( L, 5); 314 STACK_GROW( L, 5);
315 315
316 finalizers_index = lua_gettop( L); 316 finalizers_index = lua_gettop( L);
317 317
318#if ERROR_FULL_STACK 318#if ERROR_FULL_STACK
319 lua_pushcfunction( L, lane_error); // ... finalizers lane_error 319 lua_pushcfunction( L, lane_error); // ... finalizers lane_error
320 err_handler_index = lua_gettop( L); 320 err_handler_index = lua_gettop( L);
321#endif // ERROR_FULL_STACK 321#endif // ERROR_FULL_STACK
322 322
323 for( n = (int) lua_rawlen( L, finalizers_index); n > 0; -- n) 323 for( n = (int) lua_rawlen( L, finalizers_index); n > 0; -- n)
324 { 324 {
325 int args = 0; 325 int args = 0;
326 lua_pushinteger( L, n); // ... finalizers lane_error n 326 lua_pushinteger( L, n); // ... finalizers lane_error n
327 lua_rawget( L, finalizers_index); // ... finalizers lane_error finalizer 327 lua_rawget( L, finalizers_index); // ... finalizers lane_error finalizer
328 ASSERT_L( lua_isfunction( L, -1)); 328 ASSERT_L( lua_isfunction( L, -1));
329 if( lua_rc != LUA_OK) // we have an error message and an optional stack trace at the bottom of the stack 329 if( lua_rc != LUA_OK) // we have an error message and an optional stack trace at the bottom of the stack
330 { 330 {
331 ASSERT_L( finalizers_index == 2 || finalizers_index == 3); 331 ASSERT_L( finalizers_index == 2 || finalizers_index == 3);
332 //char const* err_msg = lua_tostring( L, 1); 332 //char const* err_msg = lua_tostring( L, 1);
333 lua_pushvalue( L, 1); // ... finalizers lane_error finalizer err_msg 333 lua_pushvalue( L, 1); // ... finalizers lane_error finalizer err_msg
334 // note we don't always have a stack trace for example when CANCEL_ERROR, or when we got an error that doesn't call our handler, such as LUA_ERRMEM 334 // note we don't always have a stack trace for example when CANCEL_ERROR, or when we got an error that doesn't call our handler, such as LUA_ERRMEM
335 if( finalizers_index == 3) 335 if( finalizers_index == 3)
336 { 336 {
337 lua_pushvalue( L, 2); // ... finalizers lane_error finalizer err_msg stack_trace 337 lua_pushvalue( L, 2); // ... finalizers lane_error finalizer err_msg stack_trace
338 } 338 }
339 args = finalizers_index - 1; 339 args = finalizers_index - 1;
340 } 340 }
341 341
342 // if no error from the main body, finalizer doesn't receive any argument, else it gets the error message and optional stack trace 342 // if no error from the main body, finalizer doesn't receive any argument, else it gets the error message and optional stack trace
343 rc = lua_pcall( L, args, 0, err_handler_index); // ... finalizers lane_error err_msg2? 343 rc = lua_pcall( L, args, 0, err_handler_index); // ... finalizers lane_error err_msg2?
344 if( rc != LUA_OK) 344 if( rc != LUA_OK)
345 { 345 {
346 push_stack_trace( L, rc, lua_gettop( L)); 346 push_stack_trace( L, rc, lua_gettop( L));
347 // If one finalizer fails, don't run the others. Return this 347 // If one finalizer fails, don't run the others. Return this
348 // as the 'real' error, replacing what we could have had (or not) 348 // as the 'real' error, replacing what we could have had (or not)
349 // from the actual code. 349 // from the actual code.
350 break; 350 break;
351 } 351 }
352 // no error, proceed to next finalizer // ... finalizers lane_error 352 // no error, proceed to next finalizer // ... finalizers lane_error
353 } 353 }
354 354
355 if( rc != LUA_OK) 355 if( rc != LUA_OK)
356 { 356 {
357 // ERROR_FULL_STACK accounts for the presence of lane_error on the stack 357 // ERROR_FULL_STACK accounts for the presence of lane_error on the stack
358 int nb_err_slots = lua_gettop( L) - finalizers_index - ERROR_FULL_STACK; 358 int nb_err_slots = lua_gettop( L) - finalizers_index - ERROR_FULL_STACK;
359 // a finalizer generated an error, this is what we leave of the stack 359 // a finalizer generated an error, this is what we leave of the stack
360 for( n = nb_err_slots; n > 0; -- n) 360 for( n = nb_err_slots; n > 0; -- n)
361 { 361 {
362 lua_replace( L, n); 362 lua_replace( L, n);
363 } 363 }
364 // leave on the stack only the error and optional stack trace produced by the error in the finalizer 364 // leave on the stack only the error and optional stack trace produced by the error in the finalizer
365 lua_settop( L, nb_err_slots); 365 lua_settop( L, nb_err_slots);
366 } 366 }
367 else // no error from the finalizers, make sure only the original return values from the lane body remain on the stack 367 else // no error from the finalizers, make sure only the original return values from the lane body remain on the stack
368 { 368 {
369 lua_settop( L, finalizers_index - 1); 369 lua_settop( L, finalizers_index - 1);
370 } 370 }
371 371
372 return rc; 372 return rc;
373} 373}
374 374
375/* 375/*
@@ -392,12 +392,12 @@ static int run_finalizers( lua_State* L, int lua_rc)
392 */ 392 */
393static void selfdestruct_add( Lane* s) 393static void selfdestruct_add( Lane* s)
394{ 394{
395 MUTEX_LOCK( &s->U->selfdestruct_cs); 395 MUTEX_LOCK( &s->U->selfdestruct_cs);
396 assert( s->selfdestruct_next == NULL); 396 assert( s->selfdestruct_next == NULL);
397 397
398 s->selfdestruct_next = s->U->selfdestruct_first; 398 s->selfdestruct_next = s->U->selfdestruct_first;
399 s->U->selfdestruct_first= s; 399 s->U->selfdestruct_first= s;
400 MUTEX_UNLOCK( &s->U->selfdestruct_cs); 400 MUTEX_UNLOCK( &s->U->selfdestruct_cs);
401} 401}
402 402
403/* 403/*
@@ -405,35 +405,35 @@ static void selfdestruct_add( Lane* s)
405 */ 405 */
406static bool_t selfdestruct_remove( Lane* s) 406static bool_t selfdestruct_remove( Lane* s)
407{ 407{
408 bool_t found = FALSE; 408 bool_t found = FALSE;
409 MUTEX_LOCK( &s->U->selfdestruct_cs); 409 MUTEX_LOCK( &s->U->selfdestruct_cs);
410 { 410 {
411 // Make sure (within the MUTEX) that we actually are in the chain 411 // Make sure (within the MUTEX) that we actually are in the chain
412 // still (at process exit they will remove us from chain and then 412 // still (at process exit they will remove us from chain and then
413 // cancel/kill). 413 // cancel/kill).
414 // 414 //
415 if( s->selfdestruct_next != NULL) 415 if( s->selfdestruct_next != NULL)
416 { 416 {
417 Lane** ref = (Lane**) &s->U->selfdestruct_first; 417 Lane** ref = (Lane**) &s->U->selfdestruct_first;
418 418
419 while( *ref != SELFDESTRUCT_END ) 419 while( *ref != SELFDESTRUCT_END )
420 { 420 {
421 if( *ref == s) 421 if( *ref == s)
422 { 422 {
423 *ref = s->selfdestruct_next; 423 *ref = s->selfdestruct_next;
424 s->selfdestruct_next = NULL; 424 s->selfdestruct_next = NULL;
425 // the terminal shutdown should wait until the lane is done with its lua_close() 425 // the terminal shutdown should wait until the lane is done with its lua_close()
426 ++ s->U->selfdestructing_count; 426 ++ s->U->selfdestructing_count;
427 found = TRUE; 427 found = TRUE;
428 break; 428 break;
429 } 429 }
430 ref = (Lane**) &((*ref)->selfdestruct_next); 430 ref = (Lane**) &((*ref)->selfdestruct_next);
431 } 431 }
432 assert( found); 432 assert( found);
433 } 433 }
434 } 434 }
435 MUTEX_UNLOCK( &s->U->selfdestruct_cs); 435 MUTEX_UNLOCK( &s->U->selfdestruct_cs);
436 return found; 436 return found;
437} 437}
438 438
439/* 439/*
@@ -441,160 +441,160 @@ static bool_t selfdestruct_remove( Lane* s)
441*/ 441*/
442static int selfdestruct_gc( lua_State* L) 442static int selfdestruct_gc( lua_State* L)
443{ 443{
444 Universe* U = (Universe*) lua_touserdata( L, 1); 444 Universe* U = (Universe*) lua_touserdata( L, 1);
445 445
446 while( U->selfdestruct_first != SELFDESTRUCT_END) // true at most once! 446 while( U->selfdestruct_first != SELFDESTRUCT_END) // true at most once!
447 { 447 {
448 // Signal _all_ still running threads to exit (including the timer thread) 448 // Signal _all_ still running threads to exit (including the timer thread)
449 // 449 //
450 MUTEX_LOCK( &U->selfdestruct_cs); 450 MUTEX_LOCK( &U->selfdestruct_cs);
451 { 451 {
452 Lane* s = U->selfdestruct_first; 452 Lane* s = U->selfdestruct_first;
453 while( s != SELFDESTRUCT_END) 453 while( s != SELFDESTRUCT_END)
454 { 454 {
455 // attempt a regular unforced hard cancel with a small timeout 455 // attempt a regular unforced hard cancel with a small timeout
456 bool_t cancelled = THREAD_ISNULL( s->thread) || thread_cancel( L, s, CO_Hard, 0.0001, FALSE, 0.0); 456 bool_t cancelled = THREAD_ISNULL( s->thread) || thread_cancel( L, s, CO_Hard, 0.0001, FALSE, 0.0);
457 // if we failed, and we know the thread is waiting on a linda 457 // if we failed, and we know the thread is waiting on a linda
458 if( cancelled == FALSE && s->status == WAITING && s->waiting_on != NULL) 458 if( cancelled == FALSE && s->status == WAITING && s->waiting_on != NULL)
459 { 459 {
460 // signal the linda the wake up the thread so that it can react to the cancel query 460 // signal the linda the wake up the thread so that it can react to the cancel query
461 // let us hope we never land here with a pointer on a linda that has been destroyed... 461 // let us hope we never land here with a pointer on a linda that has been destroyed...
462 SIGNAL_T *waiting_on = s->waiting_on; 462 SIGNAL_T *waiting_on = s->waiting_on;
463 //s->waiting_on = NULL; // useful, or not? 463 //s->waiting_on = NULL; // useful, or not?
464 SIGNAL_ALL( waiting_on); 464 SIGNAL_ALL( waiting_on);
465 } 465 }
466 s = s->selfdestruct_next; 466 s = s->selfdestruct_next;
467 } 467 }
468 } 468 }
469 MUTEX_UNLOCK( &U->selfdestruct_cs); 469 MUTEX_UNLOCK( &U->selfdestruct_cs);
470 470
471 // When noticing their cancel, the lanes will remove themselves from 471 // When noticing their cancel, the lanes will remove themselves from
472 // the selfdestruct chain. 472 // the selfdestruct chain.
473 473
474 // TBD: Not sure if Windows (multi core) will require the timed approach, 474 // TBD: Not sure if Windows (multi core) will require the timed approach,
475 // or single Yield. I don't have machine to test that (so leaving 475 // or single Yield. I don't have machine to test that (so leaving
476 // for timed approach). -- AKa 25-Oct-2008 476 // for timed approach). -- AKa 25-Oct-2008
477 477
478 // OS X 10.5 (Intel) needs more to avoid segfaults. 478 // OS X 10.5 (Intel) needs more to avoid segfaults.
479 // 479 //
480 // "make test" is okay. 100's of "make require" are okay. 480 // "make test" is okay. 100's of "make require" are okay.
481 // 481 //
482 // Tested on MacBook Core Duo 2GHz and 10.5.5: 482 // Tested on MacBook Core Duo 2GHz and 10.5.5:
483 // -- AKa 25-Oct-2008 483 // -- AKa 25-Oct-2008
484 // 484 //
485 { 485 {
486 lua_Number const shutdown_timeout = lua_tonumber( L, lua_upvalueindex( 1)); 486 lua_Number const shutdown_timeout = lua_tonumber( L, lua_upvalueindex( 1));
487 double const t_until = now_secs() + shutdown_timeout; 487 double const t_until = now_secs() + shutdown_timeout;
488 488
489 while( U->selfdestruct_first != SELFDESTRUCT_END) 489 while( U->selfdestruct_first != SELFDESTRUCT_END)
490 { 490 {
491 YIELD(); // give threads time to act on their cancel 491 YIELD(); // give threads time to act on their cancel
492 { 492 {
493 // count the number of cancelled thread that didn't have the time to act yet 493 // count the number of cancelled thread that didn't have the time to act yet
494 int n = 0; 494 int n = 0;
495 double t_now = 0.0; 495 double t_now = 0.0;
496 MUTEX_LOCK( &U->selfdestruct_cs); 496 MUTEX_LOCK( &U->selfdestruct_cs);
497 { 497 {
498 Lane* s = U->selfdestruct_first; 498 Lane* s = U->selfdestruct_first;
499 while( s != SELFDESTRUCT_END) 499 while( s != SELFDESTRUCT_END)
500 { 500 {
501 if( s->cancel_request == CANCEL_HARD) 501 if( s->cancel_request == CANCEL_HARD)
502 ++ n; 502 ++ n;
503 s = s->selfdestruct_next; 503 s = s->selfdestruct_next;
504 } 504 }
505 } 505 }
506 MUTEX_UNLOCK( &U->selfdestruct_cs); 506 MUTEX_UNLOCK( &U->selfdestruct_cs);
507 // if timeout elapsed, or we know all threads have acted, stop waiting 507 // if timeout elapsed, or we know all threads have acted, stop waiting
508 t_now = now_secs(); 508 t_now = now_secs();
509 if( n == 0 || (t_now >= t_until)) 509 if( n == 0 || (t_now >= t_until))
510 { 510 {
511 DEBUGSPEW_CODE( fprintf( stderr, "%d uncancelled lane(s) remain after waiting %fs at process end.\n", n, shutdown_timeout - (t_until - t_now))); 511 DEBUGSPEW_CODE( fprintf( stderr, "%d uncancelled lane(s) remain after waiting %fs at process end.\n", n, shutdown_timeout - (t_until - t_now)));
512 break; 512 break;
513 } 513 }
514 } 514 }
515 } 515 }
516 } 516 }
517 517
518 // If some lanes are currently cleaning after themselves, wait until they are done. 518 // If some lanes are currently cleaning after themselves, wait until they are done.
519 // They are no longer listed in the selfdestruct chain, but they still have to lua_close(). 519 // They are no longer listed in the selfdestruct chain, but they still have to lua_close().
520 while( U->selfdestructing_count > 0) 520 while( U->selfdestructing_count > 0)
521 { 521 {
522 YIELD(); 522 YIELD();
523 } 523 }
524 524
525 //--- 525 //---
526 // Kill the still free running threads 526 // Kill the still free running threads
527 // 527 //
528 if( U->selfdestruct_first != SELFDESTRUCT_END) 528 if( U->selfdestruct_first != SELFDESTRUCT_END)
529 { 529 {
530 unsigned int n = 0; 530 unsigned int n = 0;
531 // first thing we did was to raise the linda signals the threads were waiting on (if any) 531 // first thing we did was to raise the linda signals the threads were waiting on (if any)
532 // therefore, any well-behaved thread should be in CANCELLED state 532 // therefore, any well-behaved thread should be in CANCELLED state
533 // these are not running, and the state can be closed 533 // these are not running, and the state can be closed
534 MUTEX_LOCK( &U->selfdestruct_cs); 534 MUTEX_LOCK( &U->selfdestruct_cs);
535 { 535 {
536 Lane* s = U->selfdestruct_first; 536 Lane* s = U->selfdestruct_first;
537 while( s != SELFDESTRUCT_END) 537 while( s != SELFDESTRUCT_END)
538 { 538 {
539 Lane* next_s = s->selfdestruct_next; 539 Lane* next_s = s->selfdestruct_next;
540 s->selfdestruct_next = NULL; // detach from selfdestruct chain 540 s->selfdestruct_next = NULL; // detach from selfdestruct chain
541 if( !THREAD_ISNULL( s->thread)) // can be NULL if previous 'soft' termination succeeded 541 if( !THREAD_ISNULL( s->thread)) // can be NULL if previous 'soft' termination succeeded
542 { 542 {
543 THREAD_KILL( &s->thread); 543 THREAD_KILL( &s->thread);
544#if THREADAPI == THREADAPI_PTHREAD 544#if THREADAPI == THREADAPI_PTHREAD
545 // pthread: make sure the thread is really stopped! 545 // pthread: make sure the thread is really stopped!
546 THREAD_WAIT( &s->thread, -1, &s->done_signal, &s->done_lock, &s->status); 546 THREAD_WAIT( &s->thread, -1, &s->done_signal, &s->done_lock, &s->status);
547#endif // THREADAPI == THREADAPI_PTHREAD 547#endif // THREADAPI == THREADAPI_PTHREAD
548 } 548 }
549 // NO lua_close() in this case because we don't know where execution of the state was interrupted 549 // NO lua_close() in this case because we don't know where execution of the state was interrupted
550 lane_cleanup( s); 550 lane_cleanup( s);
551 s = next_s; 551 s = next_s;
552 ++ n; 552 ++ n;
553 } 553 }
554 U->selfdestruct_first = SELFDESTRUCT_END; 554 U->selfdestruct_first = SELFDESTRUCT_END;
555 } 555 }
556 MUTEX_UNLOCK( &U->selfdestruct_cs); 556 MUTEX_UNLOCK( &U->selfdestruct_cs);
557 557
558 DEBUGSPEW_CODE( fprintf( stderr, "Killed %d lane(s) at process end.\n", n)); 558 DEBUGSPEW_CODE( fprintf( stderr, "Killed %d lane(s) at process end.\n", n));
559 } 559 }
560 } 560 }
561 561
562 // If some lanes are currently cleaning after themselves, wait until they are done. 562 // If some lanes are currently cleaning after themselves, wait until they are done.
563 // They are no longer listed in the selfdestruct chain, but they still have to lua_close(). 563 // They are no longer listed in the selfdestruct chain, but they still have to lua_close().
564 while( U->selfdestructing_count > 0) 564 while( U->selfdestructing_count > 0)
565 { 565 {
566 YIELD(); 566 YIELD();
567 } 567 }
568 568
569 // necessary so that calling free_deep_prelude doesn't crash because linda_id expects a linda lightuserdata at absolute slot 1 569 // necessary so that calling free_deep_prelude doesn't crash because linda_id expects a linda lightuserdata at absolute slot 1
570 lua_settop( L, 0); 570 lua_settop( L, 0);
571 // no need to mutex-protect this as all threads in the universe are gone at that point 571 // no need to mutex-protect this as all threads in the universe are gone at that point
572 if( U->timer_deep != NULL) // test ins case some early internal error prevented Lanes from creating the deep timer 572 if( U->timer_deep != NULL) // test ins case some early internal error prevented Lanes from creating the deep timer
573 { 573 {
574 -- U->timer_deep->refcount; // should be 0 now 574 -- U->timer_deep->refcount; // should be 0 now
575 } 575 free_deep_prelude( L, (DeepPrelude*) U->timer_deep);
576 free_deep_prelude( L, (DeepPrelude*) U->timer_deep); 576 U->timer_deep = NULL;
577 U->timer_deep = NULL; 577 }
578 578
579 close_keepers( U, L); 579 close_keepers( U, L);
580 580
581 // remove the protected allocator, if any 581 // remove the protected allocator, if any
582 cleanup_allocator_function( U, L); 582 cleanup_allocator_function( U, L);
583 583
584#if HAVE_LANE_TRACKING 584#if HAVE_LANE_TRACKING
585 MUTEX_FREE( &U->tracking_cs); 585 MUTEX_FREE( &U->tracking_cs);
586#endif // HAVE_LANE_TRACKING 586#endif // HAVE_LANE_TRACKING
587 // Linked chains handling 587 // Linked chains handling
588 MUTEX_FREE( &U->selfdestruct_cs); 588 MUTEX_FREE( &U->selfdestruct_cs);
589 MUTEX_FREE( &U->require_cs); 589 MUTEX_FREE( &U->require_cs);
590 // Locks for 'tools.c' inc/dec counters 590 // Locks for 'tools.c' inc/dec counters
591 MUTEX_FREE( &U->deep_lock); 591 MUTEX_FREE( &U->deep_lock);
592 MUTEX_FREE( &U->mtid_lock); 592 MUTEX_FREE( &U->mtid_lock);
593 // universe is no longer available (nor necessary) 593 // universe is no longer available (nor necessary)
594 // we need to do this in case some deep userdata objects were created before Lanes was initialized, 594 // we need to do this in case some deep userdata objects were created before Lanes was initialized,
595 // as potentially they will be garbage collected after Lanes at application shutdown 595 // as potentially they will be garbage collected after Lanes at application shutdown
596 universe_store( L, NULL); 596 universe_store( L, NULL);
597 return 0; 597 return 0;
598} 598}
599 599
600 600
@@ -606,23 +606,23 @@ static int selfdestruct_gc( lua_State* L)
606// 606//
607LUAG_FUNC( set_singlethreaded) 607LUAG_FUNC( set_singlethreaded)
608{ 608{
609 uint_t cores = luaG_optunsigned( L, 1, 1); 609 uint_t cores = luaG_optunsigned( L, 1, 1);
610 (void) cores; // prevent "unused" warning 610 (void) cores; // prevent "unused" warning
611 611
612#ifdef PLATFORM_OSX 612#ifdef PLATFORM_OSX
613#ifdef _UTILBINDTHREADTOCPU 613#ifdef _UTILBINDTHREADTOCPU
614 if( cores > 1) 614 if( cores > 1)
615 { 615 {
616 return luaL_error( L, "Limiting to N>1 cores not possible"); 616 return luaL_error( L, "Limiting to N>1 cores not possible");
617 } 617 }
618 // requires 'chudInitialize()' 618 // requires 'chudInitialize()'
619 utilBindThreadToCPU(0); // # of CPU to run on (we cannot limit to 2..N CPUs?) 619 utilBindThreadToCPU(0); // # of CPU to run on (we cannot limit to 2..N CPUs?)
620 return 0; 620 return 0;
621#else 621#else
622 return luaL_error( L, "Not available: compile with _UTILBINDTHREADTOCPU"); 622 return luaL_error( L, "Not available: compile with _UTILBINDTHREADTOCPU");
623#endif 623#endif
624#else 624#else
625 return luaL_error( L, "not implemented"); 625 return luaL_error( L, "not implemented");
626#endif 626#endif
627} 627}
628 628
@@ -650,190 +650,190 @@ static DECLARE_CONST_UNIQUE_KEY( EXTENDED_STACKTRACE_REGKEY, 0x2357c69a7c92c936)
650 650
651LUAG_FUNC( set_error_reporting) 651LUAG_FUNC( set_error_reporting)
652{ 652{
653 bool_t equal; 653 bool_t equal;
654 luaL_checktype( L, 1, LUA_TSTRING); 654 luaL_checktype( L, 1, LUA_TSTRING);
655 lua_pushliteral( L, "extended"); 655 lua_pushliteral( L, "extended");
656 equal = lua_rawequal( L, -1, 1); 656 equal = lua_rawequal( L, -1, 1);
657 lua_pop( L, 1); 657 lua_pop( L, 1);
658 if( equal) 658 if( equal)
659 { 659 {
660 goto done; 660 goto done;
661 } 661 }
662 lua_pushliteral( L, "basic"); 662 lua_pushliteral( L, "basic");
663 equal = !lua_rawequal( L, -1, 1); 663 equal = !lua_rawequal( L, -1, 1);
664 lua_pop( L, 1); 664 lua_pop( L, 1);
665 if( equal) 665 if( equal)
666 { 666 {
667 return luaL_error( L, "unsupported error reporting model"); 667 return luaL_error( L, "unsupported error reporting model");
668 } 668 }
669done: 669done:
670 REGISTRY_SET( L, EXTENDED_STACKTRACE_REGKEY, lua_pushboolean( L, equal)); 670 REGISTRY_SET( L, EXTENDED_STACKTRACE_REGKEY, lua_pushboolean( L, equal));
671 return 0; 671 return 0;
672} 672}
673 673
674static int lane_error( lua_State* L) 674static int lane_error( lua_State* L)
675{ 675{
676 lua_Debug ar; 676 lua_Debug ar;
677 int n; 677 int n;
678 bool_t extended; 678 bool_t extended;
679 679
680 // error message (any type) 680 // error message (any type)
681 STACK_CHECK_ABS( L, 1); // some_error 681 STACK_CHECK_ABS( L, 1); // some_error
682 682
683 // Don't do stack survey for cancelled lanes. 683 // Don't do stack survey for cancelled lanes.
684 // 684 //
685 if( equal_unique_key( L, 1, CANCEL_ERROR)) 685 if( equal_unique_key( L, 1, CANCEL_ERROR))
686 { 686 {
687 return 1; // just pass on 687 return 1; // just pass on
688 } 688 }
689 689
690 STACK_GROW( L, 3); 690 STACK_GROW( L, 3);
691 REGISTRY_GET( L, EXTENDED_STACKTRACE_REGKEY); // some_error basic|extended 691 REGISTRY_GET( L, EXTENDED_STACKTRACE_REGKEY); // some_error basic|extended
692 extended = lua_toboolean( L, -1); 692 extended = lua_toboolean( L, -1);
693 lua_pop( L, 1); // some_error 693 lua_pop( L, 1); // some_error
694 694
695 // Place stack trace at 'registry[lane_error]' for the 'lua_pcall()' 695 // Place stack trace at 'registry[lane_error]' for the 'lua_pcall()'
696 // caller to fetch. This bypasses the Lua 5.1 limitation of only one 696 // caller to fetch. This bypasses the Lua 5.1 limitation of only one
697 // return value from error handler to 'lua_pcall()' caller. 697 // return value from error handler to 'lua_pcall()' caller.
698 698
699 // It's adequate to push stack trace as a table. This gives the receiver 699 // It's adequate to push stack trace as a table. This gives the receiver
700 // of the stack best means to format it to their liking. Also, it allows 700 // of the stack best means to format it to their liking. Also, it allows
701 // us to add more stack info later, if needed. 701 // us to add more stack info later, if needed.
702 // 702 //
703 // table of { "sourcefile.lua:<line>", ... } 703 // table of { "sourcefile.lua:<line>", ... }
704 // 704 //
705 lua_newtable( L); // some_error {} 705 lua_newtable( L); // some_error {}
706 706
707 // Best to start from level 1, but in some cases it might be a C function 707 // Best to start from level 1, but in some cases it might be a C function
708 // and we don't get '.currentline' for that. It's okay - just keep level 708 // and we don't get '.currentline' for that. It's okay - just keep level
709 // and table index growing separate. --AKa 22-Jan-2009 709 // and table index growing separate. --AKa 22-Jan-2009
710 // 710 //
711 for( n = 1; lua_getstack( L, n, &ar); ++ n) 711 for( n = 1; lua_getstack( L, n, &ar); ++ n)
712 { 712 {
713 lua_getinfo( L, extended ? "Sln" : "Sl", &ar); 713 lua_getinfo( L, extended ? "Sln" : "Sl", &ar);
714 if( extended) 714 if( extended)
715 { 715 {
716 lua_newtable( L); // some_error {} {} 716 lua_newtable( L); // some_error {} {}
717 717
718 lua_pushstring( L, ar.source); // some_error {} {} source 718 lua_pushstring( L, ar.source); // some_error {} {} source
719 lua_setfield( L, -2, "source"); // some_error {} {} 719 lua_setfield( L, -2, "source"); // some_error {} {}
720 720
721 lua_pushinteger( L, ar.currentline); // some_error {} {} currentline 721 lua_pushinteger( L, ar.currentline); // some_error {} {} currentline
722 lua_setfield( L, -2, "currentline"); // some_error {} {} 722 lua_setfield( L, -2, "currentline"); // some_error {} {}
723 723
724 lua_pushstring( L, ar.name); // some_error {} {} name 724 lua_pushstring( L, ar.name); // some_error {} {} name
725 lua_setfield( L, -2, "name"); // some_error {} {} 725 lua_setfield( L, -2, "name"); // some_error {} {}
726 726
727 lua_pushstring( L, ar.namewhat); // some_error {} {} namewhat 727 lua_pushstring( L, ar.namewhat); // some_error {} {} namewhat
728 lua_setfield( L, -2, "namewhat"); // some_error {} {} 728 lua_setfield( L, -2, "namewhat"); // some_error {} {}
729 729
730 lua_pushstring( L, ar.what); // some_error {} {} what 730 lua_pushstring( L, ar.what); // some_error {} {} what
731 lua_setfield( L, -2, "what"); // some_error {} {} 731 lua_setfield( L, -2, "what"); // some_error {} {}
732 } 732 }
733 else if( ar.currentline > 0) 733 else if( ar.currentline > 0)
734 { 734 {
735 lua_pushfstring( L, "%s:%d", ar.short_src, ar.currentline); // some_error {} "blah:blah" 735 lua_pushfstring( L, "%s:%d", ar.short_src, ar.currentline); // some_error {} "blah:blah"
736 } 736 }
737 else 737 else
738 { 738 {
739 lua_pushfstring( L, "%s:?", ar.short_src); // some_error {} "blah" 739 lua_pushfstring( L, "%s:?", ar.short_src); // some_error {} "blah"
740 } 740 }
741 lua_rawseti( L, -2, (lua_Integer) n); // some_error {} 741 lua_rawseti( L, -2, (lua_Integer) n); // some_error {}
742 } 742 }
743 743
744 REGISTRY_SET( L, STACKTRACE_REGKEY, lua_insert( L, -2)); // some_error 744 REGISTRY_SET( L, STACKTRACE_REGKEY, lua_insert( L, -2)); // some_error
745 745
746 STACK_END( L, 1); 746 STACK_END( L, 1);
747 return 1; // the untouched error value 747 return 1; // the untouched error value
748} 748}
749#endif // ERROR_FULL_STACK 749#endif // ERROR_FULL_STACK
750 750
751static void push_stack_trace( lua_State* L, int rc_, int stk_base_) 751static void push_stack_trace( lua_State* L, int rc_, int stk_base_)
752{ 752{
753 // Lua 5.1 error handler is limited to one return value; it stored the stack trace in the registry 753 // Lua 5.1 error handler is limited to one return value; it stored the stack trace in the registry
754 switch( rc_) 754 switch( rc_)
755 { 755 {
756 case LUA_OK: // no error, body return values are on the stack 756 case LUA_OK: // no error, body return values are on the stack
757 break; 757 break;
758 758
759 case LUA_ERRRUN: // cancellation or a runtime error 759 case LUA_ERRRUN: // cancellation or a runtime error
760#if ERROR_FULL_STACK // when ERROR_FULL_STACK, we installed a handler 760#if ERROR_FULL_STACK // when ERROR_FULL_STACK, we installed a handler
761 { 761 {
762 STACK_CHECK( L, 0); 762 STACK_CHECK( L, 0);
763 // fetch the call stack table from the registry where the handler stored it 763 // fetch the call stack table from the registry where the handler stored it
764 STACK_GROW( L, 1); 764 STACK_GROW( L, 1);
765 // yields nil if no stack was generated (in case of cancellation for example) 765 // yields nil if no stack was generated (in case of cancellation for example)
766 REGISTRY_GET( L, STACKTRACE_REGKEY); // err trace|nil 766 REGISTRY_GET( L, STACKTRACE_REGKEY); // err trace|nil
767 STACK_END( L, 1); 767 STACK_END( L, 1);
768 768
769 // For cancellation the error message is CANCEL_ERROR, and a stack trace isn't placed 769 // For cancellation the error message is CANCEL_ERROR, and a stack trace isn't placed
770 // For other errors, the message can be whatever was thrown, and we should have a stack trace table 770 // For other errors, the message can be whatever was thrown, and we should have a stack trace table
771 ASSERT_L( lua_type( L, 1 + stk_base_) == (equal_unique_key( L, stk_base_, CANCEL_ERROR) ? LUA_TNIL : LUA_TTABLE)); 771 ASSERT_L( lua_type( L, 1 + stk_base_) == (equal_unique_key( L, stk_base_, CANCEL_ERROR) ? LUA_TNIL : LUA_TTABLE));
772 // Just leaving the stack trace table on the stack is enough to get it through to the master. 772 // Just leaving the stack trace table on the stack is enough to get it through to the master.
773 break; 773 break;
774 } 774 }
775#endif // fall through if not ERROR_FULL_STACK 775#endif // fall through if not ERROR_FULL_STACK
776 776
777 case LUA_ERRMEM: // memory allocation error (handler not called) 777 case LUA_ERRMEM: // memory allocation error (handler not called)
778 case LUA_ERRERR: // error while running the error handler (if any, for example an out-of-memory condition) 778 case LUA_ERRERR: // error while running the error handler (if any, for example an out-of-memory condition)
779 default: 779 default:
780 // we should have a single value which is either a string (the error message) or CANCEL_ERROR 780 // we should have a single value which is either a string (the error message) or CANCEL_ERROR
781 ASSERT_L( (lua_gettop( L) == stk_base_) && ((lua_type( L, stk_base_) == LUA_TSTRING) || equal_unique_key( L, stk_base_, CANCEL_ERROR))); 781 ASSERT_L( (lua_gettop( L) == stk_base_) && ((lua_type( L, stk_base_) == LUA_TSTRING) || equal_unique_key( L, stk_base_, CANCEL_ERROR)));
782 break; 782 break;
783 } 783 }
784} 784}
785 785
786LUAG_FUNC( set_debug_threadname) 786LUAG_FUNC( set_debug_threadname)
787{ 787{
788 DECLARE_CONST_UNIQUE_KEY( hidden_regkey, LG_set_debug_threadname); 788 DECLARE_CONST_UNIQUE_KEY( hidden_regkey, LG_set_debug_threadname);
789 // C s_lane structure is a light userdata upvalue 789 // C s_lane structure is a light userdata upvalue
790 Lane* s = lua_touserdata( L, lua_upvalueindex( 1)); 790 Lane* s = lua_touserdata( L, lua_upvalueindex( 1));
791 luaL_checktype( L, -1, LUA_TSTRING); // "name" 791 luaL_checktype( L, -1, LUA_TSTRING); // "name"
792 lua_settop( L, 1); 792 lua_settop( L, 1);
793 STACK_CHECK_ABS( L, 1); 793 STACK_CHECK_ABS( L, 1);
794 // store a hidden reference in the registry to make sure the string is kept around even if a lane decides to manually change the "decoda_name" global... 794 // store a hidden reference in the registry to make sure the string is kept around even if a lane decides to manually change the "decoda_name" global...
795 REGISTRY_SET( L, hidden_regkey, lua_pushvalue( L, -2)); 795 REGISTRY_SET( L, hidden_regkey, lua_pushvalue( L, -2));
796 STACK_MID( L, 1); 796 STACK_MID( L, 1);
797 s->debug_name = lua_tostring( L, -1); 797 s->debug_name = lua_tostring( L, -1);
798 // keep a direct pointer on the string 798 // keep a direct pointer on the string
799 THREAD_SETNAME( s->debug_name); 799 THREAD_SETNAME( s->debug_name);
800 // to see VM name in Decoda debugger Virtual Machine window 800 // to see VM name in Decoda debugger Virtual Machine window
801 lua_setglobal( L, "decoda_name"); // 801 lua_setglobal( L, "decoda_name"); //
802 STACK_END( L, 0); 802 STACK_END( L, 0);
803 return 0; 803 return 0;
804} 804}
805 805
806LUAG_FUNC( get_debug_threadname) 806LUAG_FUNC( get_debug_threadname)
807{ 807{
808 Lane* const s = lua_toLane( L, 1); 808 Lane* const s = lua_toLane( L, 1);
809 luaL_argcheck( L, lua_gettop( L) == 1, 2, "too many arguments"); 809 luaL_argcheck( L, lua_gettop( L) == 1, 2, "too many arguments");
810 lua_pushstring( L, s->debug_name); 810 lua_pushstring( L, s->debug_name);
811 return 1; 811 return 1;
812} 812}
813 813
814LUAG_FUNC( set_thread_priority) 814LUAG_FUNC( set_thread_priority)
815{ 815{
816 int const prio = (int) luaL_checkinteger( L, 1); 816 int const prio = (int) luaL_checkinteger( L, 1);
817 // public Lanes API accepts a generic range -3/+3 817 // public Lanes API accepts a generic range -3/+3
818 // that will be remapped into the platform-specific scheduler priority scheme 818 // that will be remapped into the platform-specific scheduler priority scheme
819 // On some platforms, -3 is equivalent to -2 and +3 to +2 819 // On some platforms, -3 is equivalent to -2 and +3 to +2
820 if( prio < THREAD_PRIO_MIN || prio > THREAD_PRIO_MAX) 820 if( prio < THREAD_PRIO_MIN || prio > THREAD_PRIO_MAX)
821 { 821 {
822 return luaL_error( L, "priority out of range: %d..+%d (%d)", THREAD_PRIO_MIN, THREAD_PRIO_MAX, prio); 822 return luaL_error( L, "priority out of range: %d..+%d (%d)", THREAD_PRIO_MIN, THREAD_PRIO_MAX, prio);
823 } 823 }
824 THREAD_SET_PRIORITY( prio); 824 THREAD_SET_PRIORITY( prio);
825 return 0; 825 return 0;
826} 826}
827 827
828LUAG_FUNC( set_thread_affinity) 828LUAG_FUNC( set_thread_affinity)
829{ 829{
830 lua_Integer affinity = luaL_checkinteger( L, 1); 830 lua_Integer affinity = luaL_checkinteger( L, 1);
831 if( affinity <= 0) 831 if( affinity <= 0)
832 { 832 {
833 return luaL_error( L, "invalid affinity (%d)", affinity); 833 return luaL_error( L, "invalid affinity (%d)", affinity);
834 } 834 }
835 THREAD_SET_AFFINITY( (unsigned int) affinity); 835 THREAD_SET_AFFINITY( (unsigned int) affinity);
836 return 0; 836 return 0;
837} 837}
838 838
839#if USE_DEBUG_SPEW 839#if USE_DEBUG_SPEW
@@ -841,141 +841,141 @@ LUAG_FUNC( set_thread_affinity)
841// LUA_ERRERR doesn't have the same value 841// LUA_ERRERR doesn't have the same value
842struct errcode_name 842struct errcode_name
843{ 843{
844 int code; 844 int code;
845 char const* name; 845 char const* name;
846}; 846};
847 847
848static struct errcode_name s_errcodes[] = 848static struct errcode_name s_errcodes[] =
849{ 849{
850 { LUA_OK, "LUA_OK"}, 850 { LUA_OK, "LUA_OK"},
851 { LUA_YIELD, "LUA_YIELD"}, 851 { LUA_YIELD, "LUA_YIELD"},
852 { LUA_ERRRUN, "LUA_ERRRUN"}, 852 { LUA_ERRRUN, "LUA_ERRRUN"},
853 { LUA_ERRSYNTAX, "LUA_ERRSYNTAX"}, 853 { LUA_ERRSYNTAX, "LUA_ERRSYNTAX"},
854 { LUA_ERRMEM, "LUA_ERRMEM"}, 854 { LUA_ERRMEM, "LUA_ERRMEM"},
855 { LUA_ERRGCMM, "LUA_ERRGCMM"}, 855 { LUA_ERRGCMM, "LUA_ERRGCMM"},
856 { LUA_ERRERR, "LUA_ERRERR"}, 856 { LUA_ERRERR, "LUA_ERRERR"},
857}; 857};
858static char const* get_errcode_name( int _code) 858static char const* get_errcode_name( int _code)
859{ 859{
860 int i; 860 int i;
861 for( i = 0; i < 7; ++ i) 861 for( i = 0; i < 7; ++ i)
862 { 862 {
863 if( s_errcodes[i].code == _code) 863 if( s_errcodes[i].code == _code)
864 { 864 {
865 return s_errcodes[i].name; 865 return s_errcodes[i].name;
866 } 866 }
867 } 867 }
868 return "<NULL>"; 868 return "<NULL>";
869} 869}
870#endif // USE_DEBUG_SPEW 870#endif // USE_DEBUG_SPEW
871 871
872#if THREADWAIT_METHOD == THREADWAIT_CONDVAR // implies THREADAPI == THREADAPI_PTHREAD 872#if THREADWAIT_METHOD == THREADWAIT_CONDVAR // implies THREADAPI == THREADAPI_PTHREAD
873static void thread_cleanup_handler( void* opaque) 873static void thread_cleanup_handler( void* opaque)
874{ 874{
875 Lane* s= (Lane*) opaque; 875 Lane* s= (Lane*) opaque;
876 MUTEX_LOCK( &s->done_lock); 876 MUTEX_LOCK( &s->done_lock);
877 s->status = CANCELLED; 877 s->status = CANCELLED;
878 SIGNAL_ONE( &s->done_signal); // wake up master (while 's->done_lock' is on) 878 SIGNAL_ONE( &s->done_signal); // wake up master (while 's->done_lock' is on)
879 MUTEX_UNLOCK( &s->done_lock); 879 MUTEX_UNLOCK( &s->done_lock);
880} 880}
881#endif // THREADWAIT_METHOD == THREADWAIT_CONDVAR 881#endif // THREADWAIT_METHOD == THREADWAIT_CONDVAR
882 882
883static THREAD_RETURN_T THREAD_CALLCONV lane_main( void* vs) 883static THREAD_RETURN_T THREAD_CALLCONV lane_main( void* vs)
884{ 884{
885 Lane* s = (Lane*) vs; 885 Lane* s = (Lane*) vs;
886 int rc, rc2; 886 int rc, rc2;
887 lua_State* L = s->L; 887 lua_State* L = s->L;
888 // Called with the lane function and arguments on the stack 888 // Called with the lane function and arguments on the stack
889 int const nargs = lua_gettop( L) - 1; 889 int const nargs = lua_gettop( L) - 1;
890 DEBUGSPEW_CODE( Universe* U = universe_get( L)); 890 DEBUGSPEW_CODE( Universe* U = universe_get( L));
891 THREAD_MAKE_ASYNCH_CANCELLABLE(); 891 THREAD_MAKE_ASYNCH_CANCELLABLE();
892 THREAD_CLEANUP_PUSH( thread_cleanup_handler, s); 892 THREAD_CLEANUP_PUSH( thread_cleanup_handler, s);
893 s->status = RUNNING; // PENDING -> RUNNING 893 s->status = RUNNING; // PENDING -> RUNNING
894 894
895 // Tie "set_finalizer()" to the state 895 // Tie "set_finalizer()" to the state
896 lua_pushcfunction( L, LG_set_finalizer); 896 lua_pushcfunction( L, LG_set_finalizer);
897 populate_func_lookup_table( L, -1, "set_finalizer"); 897 populate_func_lookup_table( L, -1, "set_finalizer");
898 lua_setglobal( L, "set_finalizer"); 898 lua_setglobal( L, "set_finalizer");
899 899
900 // Tie "set_debug_threadname()" to the state 900 // Tie "set_debug_threadname()" to the state
901 // But don't register it in the lookup database because of the s_lane pointer upvalue 901 // But don't register it in the lookup database because of the s_lane pointer upvalue
902 lua_pushlightuserdata( L, s); 902 lua_pushlightuserdata( L, s);
903 lua_pushcclosure( L, LG_set_debug_threadname, 1); 903 lua_pushcclosure( L, LG_set_debug_threadname, 1);
904 lua_setglobal( L, "set_debug_threadname"); 904 lua_setglobal( L, "set_debug_threadname");
905 905
906 // Tie "cancel_test()" to the state 906 // Tie "cancel_test()" to the state
907 lua_pushcfunction( L, LG_cancel_test); 907 lua_pushcfunction( L, LG_cancel_test);
908 populate_func_lookup_table( L, -1, "cancel_test"); 908 populate_func_lookup_table( L, -1, "cancel_test");
909 lua_setglobal( L, "cancel_test"); 909 lua_setglobal( L, "cancel_test");
910 910
911 // this could be done in lane_new before the lane body function is pushed on the stack to avoid unnecessary stack slot shifting around 911 // this could be done in lane_new before the lane body function is pushed on the stack to avoid unnecessary stack slot shifting around
912#if ERROR_FULL_STACK 912#if ERROR_FULL_STACK
913 // Tie "set_error_reporting()" to the state 913 // Tie "set_error_reporting()" to the state
914 lua_pushcfunction( L, LG_set_error_reporting); 914 lua_pushcfunction( L, LG_set_error_reporting);
915 populate_func_lookup_table( L, -1, "set_error_reporting"); 915 populate_func_lookup_table( L, -1, "set_error_reporting");
916 lua_setglobal( L, "set_error_reporting"); 916 lua_setglobal( L, "set_error_reporting");
917 917
918 STACK_GROW( L, 1); 918 STACK_GROW( L, 1);
919 lua_pushcfunction( L, lane_error); // func args handler 919 lua_pushcfunction( L, lane_error); // func args handler
920 lua_insert( L, 1); // handler func args 920 lua_insert( L, 1); // handler func args
921#endif // ERROR_FULL_STACK 921#endif // ERROR_FULL_STACK
922 922
923 rc = lua_pcall( L, nargs, LUA_MULTRET, ERROR_FULL_STACK); // retvals|err 923 rc = lua_pcall( L, nargs, LUA_MULTRET, ERROR_FULL_STACK); // retvals|err
924 924
925#if ERROR_FULL_STACK 925#if ERROR_FULL_STACK
926 lua_remove( L, 1); // retvals|error 926 lua_remove( L, 1); // retvals|error
927# endif // ERROR_FULL_STACK 927# endif // ERROR_FULL_STACK
928 928
929 // in case of error and if it exists, fetch stack trace from registry and push it 929 // in case of error and if it exists, fetch stack trace from registry and push it
930 push_stack_trace( L, rc, 1); // retvals|error [trace] 930 push_stack_trace( L, rc, 1); // retvals|error [trace]
931 931
932 DEBUGSPEW_CODE( fprintf( stderr, INDENT_BEGIN "Lane %p body: %s (%s)\n" INDENT_END, L, get_errcode_name( rc), equal_unique_key( L, 1, CANCEL_ERROR) ? "cancelled" : lua_typename( L, lua_type( L, 1)))); 932 DEBUGSPEW_CODE( fprintf( stderr, INDENT_BEGIN "Lane %p body: %s (%s)\n" INDENT_END, L, get_errcode_name( rc), equal_unique_key( L, 1, CANCEL_ERROR) ? "cancelled" : lua_typename( L, lua_type( L, 1))));
933 //STACK_DUMP(L); 933 //STACK_DUMP(L);
934 // Call finalizers, if the script has set them up. 934 // Call finalizers, if the script has set them up.
935 // 935 //
936 rc2 = run_finalizers( L, rc); 936 rc2 = run_finalizers( L, rc);
937 DEBUGSPEW_CODE( fprintf( stderr, INDENT_BEGIN "Lane %p finalizer: %s\n" INDENT_END, L, get_errcode_name( rc2))); 937 DEBUGSPEW_CODE( fprintf( stderr, INDENT_BEGIN "Lane %p finalizer: %s\n" INDENT_END, L, get_errcode_name( rc2)));
938 if( rc2 != LUA_OK) // Error within a finalizer! 938 if( rc2 != LUA_OK) // Error within a finalizer!
939 { 939 {
940 // the finalizer generated an error, and left its own error message [and stack trace] on the stack 940 // the finalizer generated an error, and left its own error message [and stack trace] on the stack
941 rc = rc2; // we're overruling the earlier script error or normal return 941 rc = rc2; // we're overruling the earlier script error or normal return
942 } 942 }
943 s->waiting_on = NULL; // just in case 943 s->waiting_on = NULL; // just in case
944 if( selfdestruct_remove( s)) // check and remove (under lock!) 944 if( selfdestruct_remove( s)) // check and remove (under lock!)
945 { 945 {
946 // We're a free-running thread and no-one's there to clean us up. 946 // We're a free-running thread and no-one's there to clean us up.
947 // 947 //
948 lua_close( s->L); 948 lua_close( s->L);
949 949
950 MUTEX_LOCK( &s->U->selfdestruct_cs); 950 MUTEX_LOCK( &s->U->selfdestruct_cs);
951 // done with lua_close(), terminal shutdown sequence may proceed 951 // done with lua_close(), terminal shutdown sequence may proceed
952 -- s->U->selfdestructing_count; 952 -- s->U->selfdestructing_count;
953 MUTEX_UNLOCK( &s->U->selfdestruct_cs); 953 MUTEX_UNLOCK( &s->U->selfdestruct_cs);
954 954
955 lane_cleanup( s); // s is freed at this point 955 lane_cleanup( s); // s is freed at this point
956 } 956 }
957 else 957 else
958 { 958 {
959 // leave results (1..top) or error message + stack trace (1..2) on the stack - master will copy them 959 // leave results (1..top) or error message + stack trace (1..2) on the stack - master will copy them
960 960
961 enum e_status st = (rc == 0) ? DONE : equal_unique_key( L, 1, CANCEL_ERROR) ? CANCELLED : ERROR_ST; 961 enum e_status st = (rc == 0) ? DONE : equal_unique_key( L, 1, CANCEL_ERROR) ? CANCELLED : ERROR_ST;
962 962
963 // Posix no PTHREAD_TIMEDJOIN: 963 // Posix no PTHREAD_TIMEDJOIN:
964 // 'done_lock' protects the -> DONE|ERROR_ST|CANCELLED state change 964 // 'done_lock' protects the -> DONE|ERROR_ST|CANCELLED state change
965 // 965 //
966#if THREADWAIT_METHOD == THREADWAIT_CONDVAR 966#if THREADWAIT_METHOD == THREADWAIT_CONDVAR
967 MUTEX_LOCK( &s->done_lock); 967 MUTEX_LOCK( &s->done_lock);
968 { 968 {
969#endif // THREADWAIT_METHOD == THREADWAIT_CONDVAR 969#endif // THREADWAIT_METHOD == THREADWAIT_CONDVAR
970 s->status = st; 970 s->status = st;
971#if THREADWAIT_METHOD == THREADWAIT_CONDVAR 971#if THREADWAIT_METHOD == THREADWAIT_CONDVAR
972 SIGNAL_ONE( &s->done_signal); // wake up master (while 's->done_lock' is on) 972 SIGNAL_ONE( &s->done_signal); // wake up master (while 's->done_lock' is on)
973 } 973 }
974 MUTEX_UNLOCK( &s->done_lock); 974 MUTEX_UNLOCK( &s->done_lock);
975#endif // THREADWAIT_METHOD == THREADWAIT_CONDVAR 975#endif // THREADWAIT_METHOD == THREADWAIT_CONDVAR
976 } 976 }
977 THREAD_CLEANUP_POP( FALSE); 977 THREAD_CLEANUP_POP( FALSE);
978 return 0; // ignored 978 return 0; // ignored
979} 979}
980 980
981// --- If a client wants to transfer stuff of a given module from the current state to another Lane, the module must be required 981// --- If a client wants to transfer stuff of a given module from the current state to another Lane, the module must be required
@@ -984,20 +984,20 @@ static THREAD_RETURN_T THREAD_CALLCONV lane_main( void* vs)
984// upvalue[1]: _G.require 984// upvalue[1]: _G.require
985LUAG_FUNC( require) 985LUAG_FUNC( require)
986{ 986{
987 char const* name = lua_tostring( L, 1); 987 char const* name = lua_tostring( L, 1);
988 int const nargs = lua_gettop( L); 988 int const nargs = lua_gettop( L);
989 DEBUGSPEW_CODE( Universe* U = universe_get( L)); 989 DEBUGSPEW_CODE( Universe* U = universe_get( L));
990 STACK_CHECK( L, 0); 990 STACK_CHECK( L, 0);
991 DEBUGSPEW_CODE( fprintf( stderr, INDENT_BEGIN "lanes.require %s BEGIN\n" INDENT_END, name)); 991 DEBUGSPEW_CODE( fprintf( stderr, INDENT_BEGIN "lanes.require %s BEGIN\n" INDENT_END, name));
992 DEBUGSPEW_CODE( ++ U->debugspew_indent_depth); 992 DEBUGSPEW_CODE( ++ U->debugspew_indent_depth);
993 lua_pushvalue( L, lua_upvalueindex(1)); // "name" require 993 lua_pushvalue( L, lua_upvalueindex(1)); // "name" require
994 lua_insert( L, 1); // require "name" 994 lua_insert( L, 1); // require "name"
995 lua_call( L, nargs, 1); // module 995 lua_call( L, nargs, 1); // module
996 populate_func_lookup_table( L, -1, name); 996 populate_func_lookup_table( L, -1, name);
997 DEBUGSPEW_CODE( fprintf( stderr, INDENT_BEGIN "lanes.require %s END\n" INDENT_END, name)); 997 DEBUGSPEW_CODE( fprintf( stderr, INDENT_BEGIN "lanes.require %s END\n" INDENT_END, name));
998 DEBUGSPEW_CODE( -- U->debugspew_indent_depth); 998 DEBUGSPEW_CODE( -- U->debugspew_indent_depth);
999 STACK_END( L, 0); 999 STACK_END( L, 0);
1000 return 1; 1000 return 1;
1001} 1001}
1002 1002
1003 1003
@@ -1006,20 +1006,20 @@ LUAG_FUNC( require)
1006// lanes.register( "modname", module) 1006// lanes.register( "modname", module)
1007LUAG_FUNC( register) 1007LUAG_FUNC( register)
1008{ 1008{
1009 char const* name = luaL_checkstring( L, 1); 1009 char const* name = luaL_checkstring( L, 1);
1010 int const mod_type = lua_type( L, 2); 1010 int const mod_type = lua_type( L, 2);
1011 // ignore extra parameters, just in case 1011 // ignore extra parameters, just in case
1012 lua_settop( L, 2); 1012 lua_settop( L, 2);
1013 luaL_argcheck( L, (mod_type == LUA_TTABLE) || (mod_type == LUA_TFUNCTION), 2, "unexpected module type"); 1013 luaL_argcheck( L, (mod_type == LUA_TTABLE) || (mod_type == LUA_TFUNCTION), 2, "unexpected module type");
1014 DEBUGSPEW_CODE( Universe* U = universe_get( L)); 1014 DEBUGSPEW_CODE( Universe* U = universe_get( L));
1015 STACK_CHECK( L, 0); // "name" mod_table 1015 STACK_CHECK( L, 0); // "name" mod_table
1016 DEBUGSPEW_CODE( fprintf( stderr, INDENT_BEGIN "lanes.register %s BEGIN\n" INDENT_END, name)); 1016 DEBUGSPEW_CODE( fprintf( stderr, INDENT_BEGIN "lanes.register %s BEGIN\n" INDENT_END, name));
1017 DEBUGSPEW_CODE( ++ U->debugspew_indent_depth); 1017 DEBUGSPEW_CODE( ++ U->debugspew_indent_depth);
1018 populate_func_lookup_table( L, -1, name); 1018 populate_func_lookup_table( L, -1, name);
1019 DEBUGSPEW_CODE( fprintf( stderr, INDENT_BEGIN "lanes.register %s END\n" INDENT_END, name)); 1019 DEBUGSPEW_CODE( fprintf( stderr, INDENT_BEGIN "lanes.register %s END\n" INDENT_END, name));
1020 DEBUGSPEW_CODE( -- U->debugspew_indent_depth); 1020 DEBUGSPEW_CODE( -- U->debugspew_indent_depth);
1021 STACK_END( L, 0); 1021 STACK_END( L, 0);
1022 return 0; 1022 return 0;
1023} 1023}
1024 1024
1025// crc64/we of string "GCCB_KEY" generated at http://www.nitrxgen.net/hashgen/ 1025// crc64/we of string "GCCB_KEY" generated at http://www.nitrxgen.net/hashgen/
@@ -1039,247 +1039,247 @@ static DECLARE_CONST_UNIQUE_KEY( GCCB_KEY, 0xcfb1f046ef074e88);
1039// 1039//
1040LUAG_FUNC( lane_new) 1040LUAG_FUNC( lane_new)
1041{ 1041{
1042 lua_State* L2; 1042 lua_State* L2;
1043 Lane* s; 1043 Lane* s;
1044 Lane** ud; 1044 Lane** ud;
1045 1045
1046 char const* libs_str = lua_tostring( L, 2); 1046 char const* libs_str = lua_tostring( L, 2);
1047 int const priority = (int) luaL_optinteger( L, 3, 0); 1047 int const priority = (int) luaL_optinteger( L, 3, 0);
1048 uint_t globals_idx = lua_isnoneornil( L, 4) ? 0 : 4; 1048 uint_t globals_idx = lua_isnoneornil( L, 4) ? 0 : 4;
1049 uint_t package_idx = lua_isnoneornil( L, 5) ? 0 : 5; 1049 uint_t package_idx = lua_isnoneornil( L, 5) ? 0 : 5;
1050 uint_t required_idx = lua_isnoneornil( L, 6) ? 0 : 6; 1050 uint_t required_idx = lua_isnoneornil( L, 6) ? 0 : 6;
1051 uint_t gc_cb_idx = lua_isnoneornil( L, 7) ? 0 : 7; 1051 uint_t gc_cb_idx = lua_isnoneornil( L, 7) ? 0 : 7;
1052 1052
1053#define FIXED_ARGS 7 1053#define FIXED_ARGS 7
1054 int const nargs = lua_gettop(L) - FIXED_ARGS; 1054 int const nargs = lua_gettop(L) - FIXED_ARGS;
1055 Universe* U = universe_get( L); 1055 Universe* U = universe_get( L);
1056 ASSERT_L( nargs >= 0); 1056 ASSERT_L( nargs >= 0);
1057 1057
1058 // public Lanes API accepts a generic range -3/+3 1058 // public Lanes API accepts a generic range -3/+3
1059 // that will be remapped into the platform-specific scheduler priority scheme 1059 // that will be remapped into the platform-specific scheduler priority scheme
1060 // On some platforms, -3 is equivalent to -2 and +3 to +2 1060 // On some platforms, -3 is equivalent to -2 and +3 to +2
1061 if( priority < THREAD_PRIO_MIN || priority > THREAD_PRIO_MAX) 1061 if( priority < THREAD_PRIO_MIN || priority > THREAD_PRIO_MAX)
1062 { 1062 {
1063 return luaL_error( L, "Priority out of range: %d..+%d (%d)", THREAD_PRIO_MIN, THREAD_PRIO_MAX, priority); 1063 return luaL_error( L, "Priority out of range: %d..+%d (%d)", THREAD_PRIO_MIN, THREAD_PRIO_MAX, priority);
1064 } 1064 }
1065 1065
1066 /* --- Create and prepare the sub state --- */ 1066 /* --- Create and prepare the sub state --- */
1067 DEBUGSPEW_CODE( fprintf( stderr, INDENT_BEGIN "lane_new: setup\n" INDENT_END)); 1067 DEBUGSPEW_CODE( fprintf( stderr, INDENT_BEGIN "lane_new: setup\n" INDENT_END));
1068 DEBUGSPEW_CODE( ++ U->debugspew_indent_depth); 1068 DEBUGSPEW_CODE( ++ U->debugspew_indent_depth);
1069 1069
1070 // populate with selected libraries at the same time 1070 // populate with selected libraries at the same time
1071 L2 = luaG_newstate( U, L, libs_str); // L // L2 1071 L2 = luaG_newstate( U, L, libs_str); // L // L2
1072 1072
1073 STACK_GROW( L2, nargs + 3); // 1073 STACK_GROW( L2, nargs + 3); //
1074 STACK_CHECK( L2, 0); 1074 STACK_CHECK( L2, 0);
1075 1075
1076 STACK_GROW( L, 3); // func libs priority globals package required gc_cb [... args ...] 1076 STACK_GROW( L, 3); // func libs priority globals package required gc_cb [... args ...]
1077 STACK_CHECK( L, 0); 1077 STACK_CHECK( L, 0);
1078 1078
1079 // give a default "Lua" name to the thread to see VM name in Decoda debugger 1079 // give a default "Lua" name to the thread to see VM name in Decoda debugger
1080 lua_pushfstring( L2, "Lane #%p", L2); // "..." 1080 lua_pushfstring( L2, "Lane #%p", L2); // "..."
1081 lua_setglobal( L2, "decoda_name"); // 1081 lua_setglobal( L2, "decoda_name"); //
1082 ASSERT_L( lua_gettop( L2) == 0); 1082 ASSERT_L( lua_gettop( L2) == 0);
1083 1083
1084 DEBUGSPEW_CODE( fprintf( stderr, INDENT_BEGIN "lane_new: update 'package'\n" INDENT_END)); 1084 DEBUGSPEW_CODE( fprintf( stderr, INDENT_BEGIN "lane_new: update 'package'\n" INDENT_END));
1085 // package 1085 // package
1086 if( package_idx != 0) 1086 if( package_idx != 0)
1087 { 1087 {
1088 // when copying with mode eLM_LaneBody, should raise an error in case of problem, not leave it one the stack 1088 // when copying with mode eLM_LaneBody, should raise an error in case of problem, not leave it one the stack
1089 (void) luaG_inter_copy_package( U, L, L2, package_idx, eLM_LaneBody); 1089 (void) luaG_inter_copy_package( U, L, L2, package_idx, eLM_LaneBody);
1090 } 1090 }
1091 1091
1092 // modules to require in the target lane *before* the function is transfered! 1092 // modules to require in the target lane *before* the function is transfered!
1093 1093
1094 if( required_idx != 0) 1094 if( required_idx != 0)
1095 { 1095 {
1096 int nbRequired = 1; 1096 int nbRequired = 1;
1097 DEBUGSPEW_CODE( fprintf( stderr, INDENT_BEGIN "lane_new: require 'required' list\n" INDENT_END)); 1097 DEBUGSPEW_CODE( fprintf( stderr, INDENT_BEGIN "lane_new: require 'required' list\n" INDENT_END));
1098 DEBUGSPEW_CODE( ++ U->debugspew_indent_depth); 1098 DEBUGSPEW_CODE( ++ U->debugspew_indent_depth);
1099 // should not happen, was checked in lanes.lua before calling lane_new() 1099 // should not happen, was checked in lanes.lua before calling lane_new()
1100 if( lua_type( L, required_idx) != LUA_TTABLE) 1100 if( lua_type( L, required_idx) != LUA_TTABLE)
1101 { 1101 {
1102 return luaL_error( L, "expected required module list as a table, got %s", luaL_typename( L, required_idx)); 1102 return luaL_error( L, "expected required module list as a table, got %s", luaL_typename( L, required_idx));
1103 } 1103 }
1104 1104
1105 lua_pushnil( L); // func libs priority globals package required gc_cb [... args ...] nil 1105 lua_pushnil( L); // func libs priority globals package required gc_cb [... args ...] nil
1106 while( lua_next( L, required_idx) != 0) // func libs priority globals package required gc_cb [... args ...] n "modname" 1106 while( lua_next( L, required_idx) != 0) // func libs priority globals package required gc_cb [... args ...] n "modname"
1107 { 1107 {
1108 if( lua_type( L, -1) != LUA_TSTRING || lua_type( L, -2) != LUA_TNUMBER || lua_tonumber( L, -2) != nbRequired) 1108 if( lua_type( L, -1) != LUA_TSTRING || lua_type( L, -2) != LUA_TNUMBER || lua_tonumber( L, -2) != nbRequired)
1109 { 1109 {
1110 return luaL_error( L, "required module list should be a list of strings"); 1110 return luaL_error( L, "required module list should be a list of strings");
1111 } 1111 }
1112 else 1112 else
1113 { 1113 {
1114 // require the module in the target state, and populate the lookup table there too 1114 // require the module in the target state, and populate the lookup table there too
1115 size_t len; 1115 size_t len;
1116 char const* name = lua_tolstring( L, -1, &len); 1116 char const* name = lua_tolstring( L, -1, &len);
1117 DEBUGSPEW_CODE( fprintf( stderr, INDENT_BEGIN "lane_new: require '%s'\n" INDENT_END, name)); 1117 DEBUGSPEW_CODE( fprintf( stderr, INDENT_BEGIN "lane_new: require '%s'\n" INDENT_END, name));
1118 1118
1119 // require the module in the target lane 1119 // require the module in the target lane
1120 lua_getglobal( L2, "require"); // require()? 1120 lua_getglobal( L2, "require"); // require()?
1121 if( lua_isnil( L2, -1)) 1121 if( lua_isnil( L2, -1))
1122 { 1122 {
1123 lua_pop( L2, 1); // 1123 lua_pop( L2, 1); //
1124 luaL_error( L, "cannot pre-require modules without loading 'package' library first"); 1124 luaL_error( L, "cannot pre-require modules without loading 'package' library first");
1125 } 1125 }
1126 else 1126 else
1127 { 1127 {
1128 lua_pushlstring( L2, name, len); // require() name 1128 lua_pushlstring( L2, name, len); // require() name
1129 if( lua_pcall( L2, 1, 1, 0) != LUA_OK) // ret/errcode 1129 if( lua_pcall( L2, 1, 1, 0) != LUA_OK) // ret/errcode
1130 { 1130 {
1131 // propagate error to main state if any 1131 // propagate error to main state if any
1132 luaG_inter_move( U, L2, L, 1, eLM_LaneBody); // func libs priority globals package required gc_cb [... args ...] n "modname" error 1132 luaG_inter_move( U, L2, L, 1, eLM_LaneBody); // func libs priority globals package required gc_cb [... args ...] n "modname" error
1133 return lua_error( L); 1133 return lua_error( L);
1134 } 1134 }
1135 // after requiring the module, register the functions it exported in our name<->function database 1135 // after requiring the module, register the functions it exported in our name<->function database
1136 populate_func_lookup_table( L2, -1, name); 1136 populate_func_lookup_table( L2, -1, name);
1137 lua_pop( L2, 1); // 1137 lua_pop( L2, 1); //
1138 } 1138 }
1139 } 1139 }
1140 lua_pop( L, 1); // func libs priority globals package required gc_cb [... args ...] n 1140 lua_pop( L, 1); // func libs priority globals package required gc_cb [... args ...] n
1141 ++ nbRequired; 1141 ++ nbRequired;
1142 } // func libs priority globals package required gc_cb [... args ...] 1142 } // func libs priority globals package required gc_cb [... args ...]
1143 DEBUGSPEW_CODE( -- U->debugspew_indent_depth); 1143 DEBUGSPEW_CODE( -- U->debugspew_indent_depth);
1144 } 1144 }
1145 STACK_MID( L, 0); 1145 STACK_MID( L, 0);
1146 STACK_MID( L2, 0); // 1146 STACK_MID( L2, 0); //
1147 1147
1148 // Appending the specified globals to the global environment 1148 // Appending the specified globals to the global environment
1149 // *after* stdlibs have been loaded and modules required, in case we transfer references to native functions they exposed... 1149 // *after* stdlibs have been loaded and modules required, in case we transfer references to native functions they exposed...
1150 // 1150 //
1151 if( globals_idx != 0) 1151 if( globals_idx != 0)
1152 { 1152 {
1153 DEBUGSPEW_CODE( fprintf( stderr, INDENT_BEGIN "lane_new: transfer globals\n" INDENT_END)); 1153 DEBUGSPEW_CODE( fprintf( stderr, INDENT_BEGIN "lane_new: transfer globals\n" INDENT_END));
1154 if( !lua_istable( L, globals_idx)) 1154 if( !lua_istable( L, globals_idx))
1155 { 1155 {
1156 return luaL_error( L, "Expected table, got %s", luaL_typename( L, globals_idx)); 1156 return luaL_error( L, "Expected table, got %s", luaL_typename( L, globals_idx));
1157 } 1157 }
1158 1158
1159 DEBUGSPEW_CODE( ++ U->debugspew_indent_depth); 1159 DEBUGSPEW_CODE( ++ U->debugspew_indent_depth);
1160 lua_pushnil( L); // func libs priority globals package required gc_cb [... args ...] nil 1160 lua_pushnil( L); // func libs priority globals package required gc_cb [... args ...] nil
1161 // Lua 5.2 wants us to push the globals table on the stack 1161 // Lua 5.2 wants us to push the globals table on the stack
1162 lua_pushglobaltable( L2); // _G 1162 lua_pushglobaltable( L2); // _G
1163 while( lua_next( L, globals_idx)) // func libs priority globals package required gc_cb [... args ...] k v 1163 while( lua_next( L, globals_idx)) // func libs priority globals package required gc_cb [... args ...] k v
1164 { 1164 {
1165 luaG_inter_copy( U, L, L2, 2, eLM_LaneBody); // _G k v 1165 luaG_inter_copy( U, L, L2, 2, eLM_LaneBody); // _G k v
1166 // assign it in L2's globals table 1166 // assign it in L2's globals table
1167 lua_rawset( L2, -3); // _G 1167 lua_rawset( L2, -3); // _G
1168 lua_pop( L, 1); // func libs priority globals package required gc_cb [... args ...] k 1168 lua_pop( L, 1); // func libs priority globals package required gc_cb [... args ...] k
1169 } // func libs priority globals package required gc_cb [... args ...] 1169 } // func libs priority globals package required gc_cb [... args ...]
1170 lua_pop( L2, 1); // 1170 lua_pop( L2, 1); //
1171 1171
1172 DEBUGSPEW_CODE( -- U->debugspew_indent_depth); 1172 DEBUGSPEW_CODE( -- U->debugspew_indent_depth);
1173 } 1173 }
1174 STACK_MID( L, 0); 1174 STACK_MID( L, 0);
1175 STACK_MID( L2, 0); 1175 STACK_MID( L2, 0);
1176 1176
1177 // Lane main function 1177 // Lane main function
1178 if( lua_type( L, 1) == LUA_TFUNCTION) 1178 if( lua_type( L, 1) == LUA_TFUNCTION)
1179 { 1179 {
1180 int res; 1180 int res;
1181 DEBUGSPEW_CODE( fprintf( stderr, INDENT_BEGIN "lane_new: transfer lane body\n" INDENT_END)); 1181 DEBUGSPEW_CODE( fprintf( stderr, INDENT_BEGIN "lane_new: transfer lane body\n" INDENT_END));
1182 DEBUGSPEW_CODE( ++ U->debugspew_indent_depth); 1182 DEBUGSPEW_CODE( ++ U->debugspew_indent_depth);
1183 lua_pushvalue( L, 1); // func libs priority globals package required gc_cb [... args ...] func 1183 lua_pushvalue( L, 1); // func libs priority globals package required gc_cb [... args ...] func
1184 res = luaG_inter_move( U, L, L2, 1, eLM_LaneBody); // func libs priority globals package required gc_cb [... args ...] // func 1184 res = luaG_inter_move( U, L, L2, 1, eLM_LaneBody); // func libs priority globals package required gc_cb [... args ...] // func
1185 DEBUGSPEW_CODE( -- U->debugspew_indent_depth); 1185 DEBUGSPEW_CODE( -- U->debugspew_indent_depth);
1186 if( res != 0) 1186 if( res != 0)
1187 { 1187 {
1188 return luaL_error( L, "tried to copy unsupported types"); 1188 return luaL_error( L, "tried to copy unsupported types");
1189 } 1189 }
1190 } 1190 }
1191 else if( lua_type( L, 1) == LUA_TSTRING) 1191 else if( lua_type( L, 1) == LUA_TSTRING)
1192 { 1192 {
1193 // compile the string 1193 // compile the string
1194 if( luaL_loadstring( L2, lua_tostring( L, 1)) != 0) // func 1194 if( luaL_loadstring( L2, lua_tostring( L, 1)) != 0) // func
1195 { 1195 {
1196 return luaL_error( L, "error when parsing lane function code"); 1196 return luaL_error( L, "error when parsing lane function code");
1197 } 1197 }
1198 } 1198 }
1199 STACK_MID( L, 0); 1199 STACK_MID( L, 0);
1200 STACK_MID( L2, 1); 1200 STACK_MID( L2, 1);
1201 ASSERT_L( lua_isfunction( L2, 1)); 1201 ASSERT_L( lua_isfunction( L2, 1));
1202 1202
1203 // revive arguments 1203 // revive arguments
1204 if( nargs > 0) 1204 if( nargs > 0)
1205 { 1205 {
1206 int res; 1206 int res;
1207 DEBUGSPEW_CODE( fprintf( stderr, INDENT_BEGIN "lane_new: transfer lane arguments\n" INDENT_END)); 1207 DEBUGSPEW_CODE( fprintf( stderr, INDENT_BEGIN "lane_new: transfer lane arguments\n" INDENT_END));
1208 DEBUGSPEW_CODE( ++ U->debugspew_indent_depth); 1208 DEBUGSPEW_CODE( ++ U->debugspew_indent_depth);
1209 res = luaG_inter_move( U, L, L2, nargs, eLM_LaneBody); // func libs priority globals package required gc_cb // func [... args ...] 1209 res = luaG_inter_move( U, L, L2, nargs, eLM_LaneBody); // func libs priority globals package required gc_cb // func [... args ...]
1210 DEBUGSPEW_CODE( -- U->debugspew_indent_depth); 1210 DEBUGSPEW_CODE( -- U->debugspew_indent_depth);
1211 if( res != 0) 1211 if( res != 0)
1212 { 1212 {
1213 return luaL_error( L, "tried to copy unsupported types"); 1213 return luaL_error( L, "tried to copy unsupported types");
1214 } 1214 }
1215 } 1215 }
1216 STACK_END( L, -nargs); 1216 STACK_END( L, -nargs);
1217 ASSERT_L( lua_gettop( L) == FIXED_ARGS); 1217 ASSERT_L( lua_gettop( L) == FIXED_ARGS);
1218 STACK_CHECK( L, 0); 1218 STACK_CHECK( L, 0);
1219 STACK_MID( L2, 1 + nargs); 1219 STACK_MID( L2, 1 + nargs);
1220 1220
1221 // 's' is allocated from heap, not Lua, since its life span may surpass the handle's (if free running thread) 1221 // 's' is allocated from heap, not Lua, since its life span may surpass the handle's (if free running thread)
1222 // 1222 //
1223 // a Lane full userdata needs a single uservalue 1223 // a Lane full userdata needs a single uservalue
1224 ud = lua_newuserdatauv( L, sizeof( Lane*), 1); // func libs priority globals package required gc_cb lane 1224 ud = lua_newuserdatauv( L, sizeof( Lane*), 1); // func libs priority globals package required gc_cb lane
1225 s = *ud = (Lane*) malloc( sizeof( Lane)); 1225 s = *ud = (Lane*) malloc( sizeof( Lane));
1226 if( s == NULL) 1226 if( s == NULL)
1227 { 1227 {
1228 return luaL_error( L, "could not create lane: out of memory"); 1228 return luaL_error( L, "could not create lane: out of memory");
1229 } 1229 }
1230 1230
1231 s->L = L2; 1231 s->L = L2;
1232 s->U = U; 1232 s->U = U;
1233 s->status = PENDING; 1233 s->status = PENDING;
1234 s->waiting_on = NULL; 1234 s->waiting_on = NULL;
1235 s->debug_name = "<unnamed>"; 1235 s->debug_name = "<unnamed>";
1236 s->cancel_request = CANCEL_NONE; 1236 s->cancel_request = CANCEL_NONE;
1237 1237
1238#if THREADWAIT_METHOD == THREADWAIT_CONDVAR 1238#if THREADWAIT_METHOD == THREADWAIT_CONDVAR
1239 MUTEX_INIT( &s->done_lock); 1239 MUTEX_INIT( &s->done_lock);
1240 SIGNAL_INIT( &s->done_signal); 1240 SIGNAL_INIT( &s->done_signal);
1241#endif // THREADWAIT_METHOD == THREADWAIT_CONDVAR 1241#endif // THREADWAIT_METHOD == THREADWAIT_CONDVAR
1242 s->mstatus = NORMAL; 1242 s->mstatus = NORMAL;
1243 s->selfdestruct_next = NULL; 1243 s->selfdestruct_next = NULL;
1244#if HAVE_LANE_TRACKING 1244#if HAVE_LANE_TRACKING
1245 s->tracking_next = NULL; 1245 s->tracking_next = NULL;
1246 if( s->U->tracking_first) 1246 if( s->U->tracking_first)
1247 { 1247 {
1248 tracking_add( s); 1248 tracking_add( s);
1249 } 1249 }
1250#endif // HAVE_LANE_TRACKING 1250#endif // HAVE_LANE_TRACKING
1251 1251
1252 // Set metatable for the userdata 1252 // Set metatable for the userdata
1253 // 1253 //
1254 lua_pushvalue( L, lua_upvalueindex( 1)); // func libs priority globals package required gc_cb lane mt 1254 lua_pushvalue( L, lua_upvalueindex( 1)); // func libs priority globals package required gc_cb lane mt
1255 lua_setmetatable( L, -2); // func libs priority globals package required gc_cb lane 1255 lua_setmetatable( L, -2); // func libs priority globals package required gc_cb lane
1256 STACK_MID( L, 1); 1256 STACK_MID( L, 1);
1257 1257
1258 // Create uservalue for the userdata 1258 // Create uservalue for the userdata
1259 // (this is where lane body return values will be stored when the handle is indexed by a numeric key) 1259 // (this is where lane body return values will be stored when the handle is indexed by a numeric key)
1260 lua_newtable( L); // func libs cancelstep priority globals package required gc_cb lane uv 1260 lua_newtable( L); // func libs cancelstep priority globals package required gc_cb lane uv
1261 1261
1262 // Store the gc_cb callback in the uservalue 1262 // Store the gc_cb callback in the uservalue
1263 if( gc_cb_idx > 0) 1263 if( gc_cb_idx > 0)
1264 { 1264 {
1265 push_unique_key( L, GCCB_KEY); // func libs priority globals package required gc_cb lane uv k 1265 push_unique_key( L, GCCB_KEY); // func libs priority globals package required gc_cb lane uv k
1266 lua_pushvalue( L, gc_cb_idx); // func libs priority globals package required gc_cb lane uv k gc_cb 1266 lua_pushvalue( L, gc_cb_idx); // func libs priority globals package required gc_cb lane uv k gc_cb
1267 lua_rawset( L, -3); // func libs priority globals package required gc_cb lane uv 1267 lua_rawset( L, -3); // func libs priority globals package required gc_cb lane uv
1268 } 1268 }
1269 1269
1270 lua_setiuservalue( L, -2, 1); // func libs priority globals package required gc_cb lane 1270 lua_setiuservalue( L, -2, 1); // func libs priority globals package required gc_cb lane
1271 1271
1272 // Store 's' in the lane's registry, for 'cancel_test()' (we do cancel tests at pending send/receive). 1272 // Store 's' in the lane's registry, for 'cancel_test()' (we do cancel tests at pending send/receive).
1273 REGISTRY_SET( L2, CANCEL_TEST_KEY, lua_pushlightuserdata( L2, s)); // func [... args ...] 1273 REGISTRY_SET( L2, CANCEL_TEST_KEY, lua_pushlightuserdata( L2, s)); // func [... args ...]
1274 1274
1275 STACK_END( L, 1); 1275 STACK_END( L, 1);
1276 STACK_END( L2, 1 + nargs); 1276 STACK_END( L2, 1 + nargs);
1277 1277
1278 DEBUGSPEW_CODE( fprintf( stderr, INDENT_BEGIN "lane_new: launching thread\n" INDENT_END)); 1278 DEBUGSPEW_CODE( fprintf( stderr, INDENT_BEGIN "lane_new: launching thread\n" INDENT_END));
1279 THREAD_CREATE( &s->thread, lane_main, s, priority); 1279 THREAD_CREATE( &s->thread, lane_main, s, priority);
1280 1280
1281 DEBUGSPEW_CODE( -- U->debugspew_indent_depth); 1281 DEBUGSPEW_CODE( -- U->debugspew_indent_depth);
1282 return 1; 1282 return 1;
1283} 1283}
1284 1284
1285 1285
@@ -1297,79 +1297,79 @@ LUAG_FUNC( lane_new)
1297// 1297//
1298LUAG_FUNC( thread_gc) 1298LUAG_FUNC( thread_gc)
1299{ 1299{
1300 bool_t have_gc_cb = FALSE; 1300 bool_t have_gc_cb = FALSE;
1301 Lane* s = lua_toLane( L, 1); // ud 1301 Lane* s = lua_toLane( L, 1); // ud
1302 1302
1303 // if there a gc callback? 1303 // if there a gc callback?
1304 lua_getiuservalue( L, 1, 1); // ud uservalue 1304 lua_getiuservalue( L, 1, 1); // ud uservalue
1305 push_unique_key( L, GCCB_KEY); // ud uservalue __gc 1305 push_unique_key( L, GCCB_KEY); // ud uservalue __gc
1306 lua_rawget( L, -2); // ud uservalue gc_cb|nil 1306 lua_rawget( L, -2); // ud uservalue gc_cb|nil
1307 if( !lua_isnil( L, -1)) 1307 if( !lua_isnil( L, -1))
1308 { 1308 {
1309 lua_remove( L, -2); // ud gc_cb|nil 1309 lua_remove( L, -2); // ud gc_cb|nil
1310 lua_pushstring( L, s->debug_name); // ud gc_cb name 1310 lua_pushstring( L, s->debug_name); // ud gc_cb name
1311 have_gc_cb = TRUE; 1311 have_gc_cb = TRUE;
1312 } 1312 }
1313 else 1313 else
1314 { 1314 {
1315 lua_pop( L, 2); // ud 1315 lua_pop( L, 2); // ud
1316 } 1316 }
1317 1317
1318 // We can read 's->status' without locks, but not wait for it 1318 // We can read 's->status' without locks, but not wait for it
1319 // test KILLED state first, as it doesn't need to enter the selfdestruct chain 1319 // test KILLED state first, as it doesn't need to enter the selfdestruct chain
1320 if( s->mstatus == KILLED) 1320 if( s->mstatus == KILLED)
1321 { 1321 {
1322 // Make sure a kill has proceeded, before cleaning up the data structure. 1322 // Make sure a kill has proceeded, before cleaning up the data structure.
1323 // 1323 //
1324 // NO lua_close() in this case because we don't know where execution of the state was interrupted 1324 // NO lua_close() in this case because we don't know where execution of the state was interrupted
1325 DEBUGSPEW_CODE( fprintf( stderr, "** Joining with a killed thread (needs testing) **")); 1325 DEBUGSPEW_CODE( fprintf( stderr, "** Joining with a killed thread (needs testing) **"));
1326 // make sure the thread is no longer running, just like thread_join() 1326 // make sure the thread is no longer running, just like thread_join()
1327 if(! THREAD_ISNULL( s->thread)) 1327 if(! THREAD_ISNULL( s->thread))
1328 { 1328 {
1329 THREAD_WAIT( &s->thread, -1, &s->done_signal, &s->done_lock, &s->status); 1329 THREAD_WAIT( &s->thread, -1, &s->done_signal, &s->done_lock, &s->status);
1330 } 1330 }
1331 if( s->status >= DONE && s->L) 1331 if( s->status >= DONE && s->L)
1332 { 1332 {
1333 // we know the thread was killed while the Lua VM was not doing anything: we should be able to close it without crashing 1333 // we know the thread was killed while the Lua VM was not doing anything: we should be able to close it without crashing
1334 // now, thread_cancel() will not forcefully kill a lane with s->status >= DONE, so I am not sure it can ever happen 1334 // now, thread_cancel() will not forcefully kill a lane with s->status >= DONE, so I am not sure it can ever happen
1335 lua_close( s->L); 1335 lua_close( s->L);
1336 s->L = 0; 1336 s->L = 0;
1337 // just in case, but s will be freed soon so... 1337 // just in case, but s will be freed soon so...
1338 s->debug_name = "<gc>"; 1338 s->debug_name = "<gc>";
1339 } 1339 }
1340 DEBUGSPEW_CODE( fprintf( stderr, "** Joined ok **")); 1340 DEBUGSPEW_CODE( fprintf( stderr, "** Joined ok **"));
1341 } 1341 }
1342 else if( s->status < DONE) 1342 else if( s->status < DONE)
1343 { 1343 {
1344 // still running: will have to be cleaned up later 1344 // still running: will have to be cleaned up later
1345 selfdestruct_add( s); 1345 selfdestruct_add( s);
1346 assert( s->selfdestruct_next); 1346 assert( s->selfdestruct_next);
1347 if( have_gc_cb) 1347 if( have_gc_cb)
1348 { 1348 {
1349 lua_pushliteral( L, "selfdestruct"); // ud gc_cb name status 1349 lua_pushliteral( L, "selfdestruct"); // ud gc_cb name status
1350 lua_call( L, 2, 0); // ud 1350 lua_call( L, 2, 0); // ud
1351 } 1351 }
1352 return 0; 1352 return 0;
1353 } 1353 }
1354 else if( s->L) 1354 else if( s->L)
1355 { 1355 {
1356 // no longer accessing the Lua VM: we can close right now 1356 // no longer accessing the Lua VM: we can close right now
1357 lua_close( s->L); 1357 lua_close( s->L);
1358 s->L = 0; 1358 s->L = 0;
1359 // just in case, but s will be freed soon so... 1359 // just in case, but s will be freed soon so...
1360 s->debug_name = "<gc>"; 1360 s->debug_name = "<gc>";
1361 } 1361 }
1362 1362
1363 // Clean up after a (finished) thread 1363 // Clean up after a (finished) thread
1364 lane_cleanup( s); 1364 lane_cleanup( s);
1365 1365
1366 // do this after lane cleanup in case the callback triggers an error 1366 // do this after lane cleanup in case the callback triggers an error
1367 if( have_gc_cb) 1367 if( have_gc_cb)
1368 { 1368 {
1369 lua_pushliteral( L, "closed"); // ud gc_cb name status 1369 lua_pushliteral( L, "closed"); // ud gc_cb name status
1370 lua_call( L, 2, 0); // ud 1370 lua_call( L, 2, 0); // ud
1371 } 1371 }
1372 return 0; 1372 return 0;
1373} 1373}
1374 1374
1375//--- 1375//---
@@ -1384,25 +1384,25 @@ LUAG_FUNC( thread_gc)
1384// 1384//
1385static char const * thread_status_string( Lane* s) 1385static char const * thread_status_string( Lane* s)
1386{ 1386{
1387 enum e_status st = s->status; // read just once (volatile) 1387 enum e_status st = s->status; // read just once (volatile)
1388 char const* str = 1388 char const* str =
1389 (s->mstatus == KILLED) ? "killed" : // new to v3.3.0! 1389 (s->mstatus == KILLED) ? "killed" : // new to v3.3.0!
1390 (st == PENDING) ? "pending" : 1390 (st == PENDING) ? "pending" :
1391 (st == RUNNING) ? "running" : // like in 'co.status()' 1391 (st == RUNNING) ? "running" : // like in 'co.status()'
1392 (st == WAITING) ? "waiting" : 1392 (st == WAITING) ? "waiting" :
1393 (st == DONE) ? "done" : 1393 (st == DONE) ? "done" :
1394 (st == ERROR_ST) ? "error" : 1394 (st == ERROR_ST) ? "error" :
1395 (st == CANCELLED) ? "cancelled" : NULL; 1395 (st == CANCELLED) ? "cancelled" : NULL;
1396 return str; 1396 return str;
1397} 1397}
1398 1398
1399int push_thread_status( lua_State* L, Lane* s) 1399int push_thread_status( lua_State* L, Lane* s)
1400{ 1400{
1401 char const* const str = thread_status_string( s); 1401 char const* const str = thread_status_string( s);
1402 ASSERT_L( str); 1402 ASSERT_L( str);
1403 1403
1404 lua_pushstring( L, str); 1404 lua_pushstring( L, str);
1405 return 1; 1405 return 1;
1406} 1406}
1407 1407
1408 1408
@@ -1416,77 +1416,77 @@ int push_thread_status( lua_State* L, Lane* s)
1416// 1416//
1417LUAG_FUNC( thread_join) 1417LUAG_FUNC( thread_join)
1418{ 1418{
1419 Lane* const s = lua_toLane( L, 1); 1419 Lane* const s = lua_toLane( L, 1);
1420 double wait_secs = luaL_optnumber( L, 2, -1.0); 1420 double wait_secs = luaL_optnumber( L, 2, -1.0);
1421 lua_State* L2 = s->L; 1421 lua_State* L2 = s->L;
1422 int ret; 1422 int ret;
1423 bool_t done = THREAD_ISNULL( s->thread) || THREAD_WAIT( &s->thread, wait_secs, &s->done_signal, &s->done_lock, &s->status); 1423 bool_t done = THREAD_ISNULL( s->thread) || THREAD_WAIT( &s->thread, wait_secs, &s->done_signal, &s->done_lock, &s->status);
1424 if( !done || !L2) 1424 if( !done || !L2)
1425 { 1425 {
1426 STACK_GROW( L, 2); 1426 STACK_GROW( L, 2);
1427 lua_pushnil( L); 1427 lua_pushnil( L);
1428 lua_pushliteral( L, "timeout"); 1428 lua_pushliteral( L, "timeout");
1429 return 2; 1429 return 2;
1430 } 1430 }
1431 1431
1432 STACK_CHECK( L, 0); 1432 STACK_CHECK( L, 0);
1433 // Thread is DONE/ERROR_ST/CANCELLED; all ours now 1433 // Thread is DONE/ERROR_ST/CANCELLED; all ours now
1434 1434
1435 if( s->mstatus == KILLED) // OS thread was killed if thread_cancel was forced 1435 if( s->mstatus == KILLED) // OS thread was killed if thread_cancel was forced
1436 { 1436 {
1437 // in that case, even if the thread was killed while DONE/ERROR_ST/CANCELLED, ignore regular return values 1437 // in that case, even if the thread was killed while DONE/ERROR_ST/CANCELLED, ignore regular return values
1438 STACK_GROW( L, 2); 1438 STACK_GROW( L, 2);
1439 lua_pushnil( L); 1439 lua_pushnil( L);
1440 lua_pushliteral( L, "killed"); 1440 lua_pushliteral( L, "killed");
1441 ret = 2; 1441 ret = 2;
1442 } 1442 }
1443 else 1443 else
1444 { 1444 {
1445 Universe* U = universe_get( L); 1445 Universe* U = universe_get( L);
1446 // debug_name is a pointer to string possibly interned in the lane's state, that no longer exists when the state is closed 1446 // debug_name is a pointer to string possibly interned in the lane's state, that no longer exists when the state is closed
1447 // so store it in the userdata uservalue at a key that can't possibly collide 1447 // so store it in the userdata uservalue at a key that can't possibly collide
1448 securize_debug_threadname( L, s); 1448 securize_debug_threadname( L, s);
1449 switch( s->status) 1449 switch( s->status)
1450 { 1450 {
1451 case DONE: 1451 case DONE:
1452 { 1452 {
1453 uint_t n = lua_gettop( L2); // whole L2 stack 1453 uint_t n = lua_gettop( L2); // whole L2 stack
1454 if( (n > 0) && (luaG_inter_move( U, L2, L, n, eLM_LaneBody) != 0)) 1454 if( (n > 0) && (luaG_inter_move( U, L2, L, n, eLM_LaneBody) != 0))
1455 { 1455 {
1456 return luaL_error( L, "tried to copy unsupported types"); 1456 return luaL_error( L, "tried to copy unsupported types");
1457 } 1457 }
1458 ret = n; 1458 ret = n;
1459 } 1459 }
1460 break; 1460 break;
1461 1461
1462 case ERROR_ST: 1462 case ERROR_ST:
1463 { 1463 {
1464 int const n = lua_gettop( L2); 1464 int const n = lua_gettop( L2);
1465 STACK_GROW( L, 3); 1465 STACK_GROW( L, 3);
1466 lua_pushnil( L); 1466 lua_pushnil( L);
1467 // even when ERROR_FULL_STACK, if the error is not LUA_ERRRUN, the handler wasn't called, and we only have 1 error message on the stack ... 1467 // even when ERROR_FULL_STACK, if the error is not LUA_ERRRUN, the handler wasn't called, and we only have 1 error message on the stack ...
1468 if( luaG_inter_move( U, L2, L, n, eLM_LaneBody) != 0) // nil "err" [trace] 1468 if( luaG_inter_move( U, L2, L, n, eLM_LaneBody) != 0) // nil "err" [trace]
1469 { 1469 {
1470 return luaL_error( L, "tried to copy unsupported types: %s", lua_tostring( L, -n)); 1470 return luaL_error( L, "tried to copy unsupported types: %s", lua_tostring( L, -n));
1471 } 1471 }
1472 ret = 1 + n; 1472 ret = 1 + n;
1473 } 1473 }
1474 break; 1474 break;
1475 1475
1476 case CANCELLED: 1476 case CANCELLED:
1477 ret = 0; 1477 ret = 0;
1478 break; 1478 break;
1479 1479
1480 default: 1480 default:
1481 DEBUGSPEW_CODE( fprintf( stderr, "Status: %d\n", s->status)); 1481 DEBUGSPEW_CODE( fprintf( stderr, "Status: %d\n", s->status));
1482 ASSERT_L( FALSE); 1482 ASSERT_L( FALSE);
1483 ret = 0; 1483 ret = 0;
1484 } 1484 }
1485 lua_close( L2); 1485 lua_close( L2);
1486 } 1486 }
1487 s->L = 0; 1487 s->L = 0;
1488 STACK_END( L, ret); 1488 STACK_END( L, ret);
1489 return ret; 1489 return ret;
1490} 1490}
1491 1491
1492 1492
@@ -1500,150 +1500,150 @@ LUAG_FUNC( thread_join)
1500// Else raise an error 1500// Else raise an error
1501LUAG_FUNC( thread_index) 1501LUAG_FUNC( thread_index)
1502{ 1502{
1503 int const UD = 1; 1503 int const UD = 1;
1504 int const KEY = 2; 1504 int const KEY = 2;
1505 int const USR = 3; 1505 int const USR = 3;
1506 Lane* const s = lua_toLane( L, UD); 1506 Lane* const s = lua_toLane( L, UD);
1507 ASSERT_L( lua_gettop( L) == 2); 1507 ASSERT_L( lua_gettop( L) == 2);
1508 1508
1509 STACK_GROW( L, 8); // up to 8 positions are needed in case of error propagation 1509 STACK_GROW( L, 8); // up to 8 positions are needed in case of error propagation
1510 1510
1511 // If key is numeric, wait until the thread returns and populate the environment with the return values 1511 // If key is numeric, wait until the thread returns and populate the environment with the return values
1512 if( lua_type( L, KEY) == LUA_TNUMBER) 1512 if( lua_type( L, KEY) == LUA_TNUMBER)
1513 { 1513 {
1514 // first, check that we don't already have an environment that holds the requested value 1514 // first, check that we don't already have an environment that holds the requested value
1515 { 1515 {
1516 // If key is found in the uservalue, return it 1516 // If key is found in the uservalue, return it
1517 lua_getiuservalue( L, UD, 1); 1517 lua_getiuservalue( L, UD, 1);
1518 lua_pushvalue( L, KEY); 1518 lua_pushvalue( L, KEY);
1519 lua_rawget( L, USR); 1519 lua_rawget( L, USR);
1520 if( !lua_isnil( L, -1)) 1520 if( !lua_isnil( L, -1))
1521 { 1521 {
1522 return 1; 1522 return 1;
1523 } 1523 }
1524 lua_pop( L, 1); 1524 lua_pop( L, 1);
1525 } 1525 }
1526 { 1526 {
1527 // check if we already fetched the values from the thread or not 1527 // check if we already fetched the values from the thread or not
1528 bool_t fetched; 1528 bool_t fetched;
1529 lua_Integer key = lua_tointeger( L, KEY); 1529 lua_Integer key = lua_tointeger( L, KEY);
1530 lua_pushinteger( L, 0); 1530 lua_pushinteger( L, 0);
1531 lua_rawget( L, USR); 1531 lua_rawget( L, USR);
1532 fetched = !lua_isnil( L, -1); 1532 fetched = !lua_isnil( L, -1);
1533 lua_pop( L, 1); // back to our 2 args + uservalue on the stack 1533 lua_pop( L, 1); // back to our 2 args + uservalue on the stack
1534 if( !fetched) 1534 if( !fetched)
1535 { 1535 {
1536 lua_pushinteger( L, 0); 1536 lua_pushinteger( L, 0);
1537 lua_pushboolean( L, 1); 1537 lua_pushboolean( L, 1);
1538 lua_rawset( L, USR); 1538 lua_rawset( L, USR);
1539 // wait until thread has completed 1539 // wait until thread has completed
1540 lua_pushcfunction( L, LG_thread_join); 1540 lua_pushcfunction( L, LG_thread_join);
1541 lua_pushvalue( L, UD); 1541 lua_pushvalue( L, UD);
1542 lua_call( L, 1, LUA_MULTRET); // all return values are on the stack, at slots 4+ 1542 lua_call( L, 1, LUA_MULTRET); // all return values are on the stack, at slots 4+
1543 switch( s->status) 1543 switch( s->status)
1544 { 1544 {
1545 default: 1545 default:
1546 if( s->mstatus != KILLED) 1546 if( s->mstatus != KILLED)
1547 { 1547 {
1548 // this is an internal error, we probably never get here 1548 // this is an internal error, we probably never get here
1549 lua_settop( L, 0); 1549 lua_settop( L, 0);
1550 lua_pushliteral( L, "Unexpected status: "); 1550 lua_pushliteral( L, "Unexpected status: ");
1551 lua_pushstring( L, thread_status_string( s)); 1551 lua_pushstring( L, thread_status_string( s));
1552 lua_concat( L, 2); 1552 lua_concat( L, 2);
1553 lua_error( L); 1553 lua_error( L);
1554 break; 1554 break;
1555 } 1555 }
1556 // fall through if we are killed, as we got nil, "killed" on the stack 1556 // fall through if we are killed, as we got nil, "killed" on the stack
1557 1557
1558 case DONE: // got regular return values 1558 case DONE: // got regular return values
1559 { 1559 {
1560 int i, nvalues = lua_gettop( L) - 3; 1560 int i, nvalues = lua_gettop( L) - 3;
1561 for( i = nvalues; i > 0; -- i) 1561 for( i = nvalues; i > 0; -- i)
1562 { 1562 {
1563 // pop the last element of the stack, to store it in the uservalue at its proper index 1563 // pop the last element of the stack, to store it in the uservalue at its proper index
1564 lua_rawseti( L, USR, i); 1564 lua_rawseti( L, USR, i);
1565 } 1565 }
1566 } 1566 }
1567 break; 1567 break;
1568 1568
1569 case ERROR_ST: // got 3 values: nil, errstring, callstack table 1569 case ERROR_ST: // got 3 values: nil, errstring, callstack table
1570 // me[-2] could carry the stack table, but even 1570 // me[-2] could carry the stack table, but even
1571 // me[-1] is rather unnecessary (and undocumented); 1571 // me[-1] is rather unnecessary (and undocumented);
1572 // use ':join()' instead. --AKa 22-Jan-2009 1572 // use ':join()' instead. --AKa 22-Jan-2009
1573 ASSERT_L( lua_isnil( L, 4) && !lua_isnil( L, 5) && lua_istable( L, 6)); 1573 ASSERT_L( lua_isnil( L, 4) && !lua_isnil( L, 5) && lua_istable( L, 6));
1574 // store errstring at key -1 1574 // store errstring at key -1
1575 lua_pushnumber( L, -1); 1575 lua_pushnumber( L, -1);
1576 lua_pushvalue( L, 5); 1576 lua_pushvalue( L, 5);
1577 lua_rawset( L, USR); 1577 lua_rawset( L, USR);
1578 break; 1578 break;
1579 1579
1580 case CANCELLED: 1580 case CANCELLED:
1581 // do nothing 1581 // do nothing
1582 break; 1582 break;
1583 } 1583 }
1584 } 1584 }
1585 lua_settop( L, 3); // UD KEY ENV 1585 lua_settop( L, 3); // UD KEY ENV
1586 if( key != -1) 1586 if( key != -1)
1587 { 1587 {
1588 lua_pushnumber( L, -1); // UD KEY ENV -1 1588 lua_pushnumber( L, -1); // UD KEY ENV -1
1589 lua_rawget( L, USR); // UD KEY ENV "error" 1589 lua_rawget( L, USR); // UD KEY ENV "error"
1590 if( !lua_isnil( L, -1)) // an error was stored 1590 if( !lua_isnil( L, -1)) // an error was stored
1591 { 1591 {
1592 // Note: Lua 5.1 interpreter is not prepared to show 1592 // Note: Lua 5.1 interpreter is not prepared to show
1593 // non-string errors, so we use 'tostring()' here 1593 // non-string errors, so we use 'tostring()' here
1594 // to get meaningful output. --AKa 22-Jan-2009 1594 // to get meaningful output. --AKa 22-Jan-2009
1595 // 1595 //
1596 // Also, the stack dump we get is no good; it only 1596 // Also, the stack dump we get is no good; it only
1597 // lists our internal Lanes functions. There seems 1597 // lists our internal Lanes functions. There seems
1598 // to be no way to switch it off, though. 1598 // to be no way to switch it off, though.
1599 // 1599 //
1600 // Level 3 should show the line where 'h[x]' was read 1600 // Level 3 should show the line where 'h[x]' was read
1601 // but this only seems to work for string messages 1601 // but this only seems to work for string messages
1602 // (Lua 5.1.4). No idea, why. --AKa 22-Jan-2009 1602 // (Lua 5.1.4). No idea, why. --AKa 22-Jan-2009
1603 lua_getmetatable( L, UD); // UD KEY ENV "error" mt 1603 lua_getmetatable( L, UD); // UD KEY ENV "error" mt
1604 lua_getfield( L, -1, "cached_error"); // UD KEY ENV "error" mt error() 1604 lua_getfield( L, -1, "cached_error"); // UD KEY ENV "error" mt error()
1605 lua_getfield( L, -2, "cached_tostring"); // UD KEY ENV "error" mt error() tostring() 1605 lua_getfield( L, -2, "cached_tostring"); // UD KEY ENV "error" mt error() tostring()
1606 lua_pushvalue( L, 4); // UD KEY ENV "error" mt error() tostring() "error" 1606 lua_pushvalue( L, 4); // UD KEY ENV "error" mt error() tostring() "error"
1607 lua_call( L, 1, 1); // tostring( errstring) -- just in case // UD KEY ENV "error" mt error() "error" 1607 lua_call( L, 1, 1); // tostring( errstring) -- just in case // UD KEY ENV "error" mt error() "error"
1608 lua_pushinteger( L, 3); // UD KEY ENV "error" mt error() "error" 3 1608 lua_pushinteger( L, 3); // UD KEY ENV "error" mt error() "error" 3
1609 lua_call( L, 2, 0); // error( tostring( errstring), 3) // UD KEY ENV "error" mt 1609 lua_call( L, 2, 0); // error( tostring( errstring), 3) // UD KEY ENV "error" mt
1610 } 1610 }
1611 else 1611 else
1612 { 1612 {
1613 lua_pop( L, 1); // back to our 3 arguments on the stack 1613 lua_pop( L, 1); // back to our 3 arguments on the stack
1614 } 1614 }
1615 } 1615 }
1616 lua_rawgeti( L, USR, (int)key); 1616 lua_rawgeti( L, USR, (int)key);
1617 } 1617 }
1618 return 1; 1618 return 1;
1619 } 1619 }
1620 if( lua_type( L, KEY) == LUA_TSTRING) 1620 if( lua_type( L, KEY) == LUA_TSTRING)
1621 { 1621 {
1622 char const * const keystr = lua_tostring( L, KEY); 1622 char const * const keystr = lua_tostring( L, KEY);
1623 lua_settop( L, 2); // keep only our original arguments on the stack 1623 lua_settop( L, 2); // keep only our original arguments on the stack
1624 if( strcmp( keystr, "status") == 0) 1624 if( strcmp( keystr, "status") == 0)
1625 { 1625 {
1626 return push_thread_status( L, s); // push the string representing the status 1626 return push_thread_status( L, s); // push the string representing the status
1627 } 1627 }
1628 // return UD.metatable[key] 1628 // return UD.metatable[key]
1629 lua_getmetatable( L, UD); // UD KEY mt 1629 lua_getmetatable( L, UD); // UD KEY mt
1630 lua_replace( L, -3); // mt KEY 1630 lua_replace( L, -3); // mt KEY
1631 lua_rawget( L, -2); // mt value 1631 lua_rawget( L, -2); // mt value
1632 // only "cancel" and "join" are registered as functions, any other string will raise an error 1632 // only "cancel" and "join" are registered as functions, any other string will raise an error
1633 if( lua_iscfunction( L, -1)) 1633 if( lua_iscfunction( L, -1))
1634 { 1634 {
1635 return 1; 1635 return 1;
1636 } 1636 }
1637 return luaL_error( L, "can't index a lane with '%s'", keystr); 1637 return luaL_error( L, "can't index a lane with '%s'", keystr);
1638 } 1638 }
1639 // unknown key 1639 // unknown key
1640 lua_getmetatable( L, UD); 1640 lua_getmetatable( L, UD);
1641 lua_getfield( L, -1, "cached_error"); 1641 lua_getfield( L, -1, "cached_error");
1642 lua_pushliteral( L, "Unknown key: "); 1642 lua_pushliteral( L, "Unknown key: ");
1643 lua_pushvalue( L, KEY); 1643 lua_pushvalue( L, KEY);
1644 lua_concat( L, 2); 1644 lua_concat( L, 2);
1645 lua_call( L, 1, 0); // error( "Unknown key: " .. key) -> doesn't return 1645 lua_call( L, 1, 0); // error( "Unknown key: " .. key) -> doesn't return
1646 return 0; 1646 return 0;
1647} 1647}
1648 1648
1649#if HAVE_LANE_TRACKING 1649#if HAVE_LANE_TRACKING
@@ -1771,36 +1771,36 @@ static const struct luaL_Reg lanes_functions [] = {
1771static void init_once_LOCKED( void) 1771static void init_once_LOCKED( void)
1772{ 1772{
1773#if (defined PLATFORM_WIN32) || (defined PLATFORM_POCKETPC) 1773#if (defined PLATFORM_WIN32) || (defined PLATFORM_POCKETPC)
1774 now_secs(); // initialize 'now_secs()' internal offset 1774 now_secs(); // initialize 'now_secs()' internal offset
1775#endif 1775#endif
1776 1776
1777#if (defined PLATFORM_OSX) && (defined _UTILBINDTHREADTOCPU) 1777#if (defined PLATFORM_OSX) && (defined _UTILBINDTHREADTOCPU)
1778 chudInitialize(); 1778 chudInitialize();
1779#endif 1779#endif
1780 1780
1781 //--- 1781 //---
1782 // Linux needs SCHED_RR to change thread priorities, and that is only 1782 // Linux needs SCHED_RR to change thread priorities, and that is only
1783 // allowed for sudo'ers. SCHED_OTHER (default) has no priorities. 1783 // allowed for sudo'ers. SCHED_OTHER (default) has no priorities.
1784 // SCHED_OTHER threads are always lower priority than SCHED_RR. 1784 // SCHED_OTHER threads are always lower priority than SCHED_RR.
1785 // 1785 //
1786 // ^-- those apply to 2.6 kernel. IF **wishful thinking** these 1786 // ^-- those apply to 2.6 kernel. IF **wishful thinking** these
1787 // constraints will change in the future, non-sudo priorities can 1787 // constraints will change in the future, non-sudo priorities can
1788 // be enabled also for Linux. 1788 // be enabled also for Linux.
1789 // 1789 //
1790#ifdef PLATFORM_LINUX 1790#ifdef PLATFORM_LINUX
1791 sudo = (geteuid() == 0); // we are root? 1791 sudo = (geteuid() == 0); // we are root?
1792 1792
1793 // If lower priorities (-2..-1) are wanted, we need to lift the main 1793 // If lower priorities (-2..-1) are wanted, we need to lift the main
1794 // thread to SCHED_RR and 50 (medium) level. Otherwise, we're always below 1794 // thread to SCHED_RR and 50 (medium) level. Otherwise, we're always below
1795 // the launched threads (even -2). 1795 // the launched threads (even -2).
1796 // 1796 //
1797#ifdef LINUX_SCHED_RR 1797#ifdef LINUX_SCHED_RR
1798 if( sudo) 1798 if( sudo)
1799 { 1799 {
1800 struct sched_param sp; 1800 struct sched_param sp;
1801 sp.sched_priority = _PRIO_0; 1801 sp.sched_priority = _PRIO_0;
1802 PT_CALL( pthread_setschedparam( pthread_self(), SCHED_RR, &sp)); 1802 PT_CALL( pthread_setschedparam( pthread_self(), SCHED_RR, &sp));
1803 } 1803 }
1804#endif // LINUX_SCHED_RR 1804#endif // LINUX_SCHED_RR
1805#endif // PLATFORM_LINUX 1805#endif // PLATFORM_LINUX
1806} 1806}
@@ -1812,210 +1812,210 @@ static volatile long s_initCount = 0;
1812// param 1: settings table 1812// param 1: settings table
1813LUAG_FUNC( configure) 1813LUAG_FUNC( configure)
1814{ 1814{
1815 Universe* U = universe_get( L); 1815 Universe* U = universe_get( L);
1816 bool_t const from_master_state = (U == NULL); 1816 bool_t const from_master_state = (U == NULL);
1817 char const* name = luaL_checkstring( L, lua_upvalueindex( 1)); 1817 char const* name = luaL_checkstring( L, lua_upvalueindex( 1));
1818 _ASSERT_L( L, lua_type( L, 1) == LUA_TTABLE); 1818 _ASSERT_L( L, lua_type( L, 1) == LUA_TTABLE);
1819 1819
1820 /* 1820 /*
1821 ** Making one-time initializations. 1821 ** Making one-time initializations.
1822 ** 1822 **
1823 ** When the host application is single-threaded (and all threading happens via Lanes) 1823 ** When the host application is single-threaded (and all threading happens via Lanes)
1824 ** there is no problem. But if the host is multithreaded, we need to lock around the 1824 ** there is no problem. But if the host is multithreaded, we need to lock around the
1825 ** initializations. 1825 ** initializations.
1826 */ 1826 */
1827#if THREADAPI == THREADAPI_WINDOWS 1827#if THREADAPI == THREADAPI_WINDOWS
1828 { 1828 {
1829 static volatile int /*bool*/ go_ahead; // = 0 1829 static volatile int /*bool*/ go_ahead; // = 0
1830 if( InterlockedCompareExchange( &s_initCount, 1, 0) == 0) 1830 if( InterlockedCompareExchange( &s_initCount, 1, 0) == 0)
1831 { 1831 {
1832 init_once_LOCKED(); 1832 init_once_LOCKED();
1833 go_ahead = 1; // let others pass 1833 go_ahead = 1; // let others pass
1834 } 1834 }
1835 else 1835 else
1836 { 1836 {
1837 while( !go_ahead) { Sleep(1); } // changes threads 1837 while( !go_ahead) { Sleep(1); } // changes threads
1838 } 1838 }
1839 } 1839 }
1840#else // THREADAPI == THREADAPI_PTHREAD 1840#else // THREADAPI == THREADAPI_PTHREAD
1841 if( s_initCount == 0) 1841 if( s_initCount == 0)
1842 { 1842 {
1843 static pthread_mutex_t my_lock = PTHREAD_MUTEX_INITIALIZER; 1843 static pthread_mutex_t my_lock = PTHREAD_MUTEX_INITIALIZER;
1844 pthread_mutex_lock( &my_lock); 1844 pthread_mutex_lock( &my_lock);
1845 { 1845 {
1846 // Recheck now that we're within the lock 1846 // Recheck now that we're within the lock
1847 // 1847 //
1848 if( s_initCount == 0) 1848 if( s_initCount == 0)
1849 { 1849 {
1850 init_once_LOCKED(); 1850 init_once_LOCKED();
1851 s_initCount = 1; 1851 s_initCount = 1;
1852 } 1852 }
1853 } 1853 }
1854 pthread_mutex_unlock( &my_lock); 1854 pthread_mutex_unlock( &my_lock);
1855 } 1855 }
1856#endif // THREADAPI == THREADAPI_PTHREAD 1856#endif // THREADAPI == THREADAPI_PTHREAD
1857 1857
1858 STACK_GROW( L, 4); 1858 STACK_GROW( L, 4);
1859 STACK_CHECK_ABS( L, 1); // settings 1859 STACK_CHECK_ABS( L, 1); // settings
1860 1860
1861 DEBUGSPEW_CODE( fprintf( stderr, INDENT_BEGIN "%p: lanes.configure() BEGIN\n" INDENT_END, L)); 1861 DEBUGSPEW_CODE( fprintf( stderr, INDENT_BEGIN "%p: lanes.configure() BEGIN\n" INDENT_END, L));
1862 DEBUGSPEW_CODE( if( U) ++ U->debugspew_indent_depth); 1862 DEBUGSPEW_CODE( if( U) ++ U->debugspew_indent_depth);
1863 1863
1864 if( U == NULL) 1864 if( U == NULL)
1865 { 1865 {
1866 U = universe_create( L); // settings universe 1866 U = universe_create( L); // settings universe
1867 DEBUGSPEW_CODE( ++ U->debugspew_indent_depth); 1867 DEBUGSPEW_CODE( ++ U->debugspew_indent_depth);
1868 lua_newtable( L); // settings universe mt 1868 lua_newtable( L); // settings universe mt
1869 lua_getfield( L, 1, "shutdown_timeout"); // settings universe mt shutdown_timeout 1869 lua_getfield( L, 1, "shutdown_timeout"); // settings universe mt shutdown_timeout
1870 lua_pushcclosure( L, selfdestruct_gc, 1); // settings universe mt selfdestruct_gc 1870 lua_pushcclosure( L, selfdestruct_gc, 1); // settings universe mt selfdestruct_gc
1871 lua_setfield( L, -2, "__gc"); // settings universe mt 1871 lua_setfield( L, -2, "__gc"); // settings universe mt
1872 lua_setmetatable( L, -2); // settings universe 1872 lua_setmetatable( L, -2); // settings universe
1873 lua_pop( L, 1); // settings 1873 lua_pop( L, 1); // settings
1874 lua_getfield( L, 1, "verbose_errors"); // settings verbose_errors 1874 lua_getfield( L, 1, "verbose_errors"); // settings verbose_errors
1875 U->verboseErrors = lua_toboolean( L, -1); 1875 U->verboseErrors = lua_toboolean( L, -1);
1876 lua_pop( L, 1); // settings 1876 lua_pop( L, 1); // settings
1877 lua_getfield( L, 1, "demote_full_userdata"); // settings demote_full_userdata 1877 lua_getfield( L, 1, "demote_full_userdata"); // settings demote_full_userdata
1878 U->demoteFullUserdata = lua_toboolean( L, -1); 1878 U->demoteFullUserdata = lua_toboolean( L, -1);
1879 lua_pop( L, 1); // settings 1879 lua_pop( L, 1); // settings
1880#if HAVE_LANE_TRACKING 1880#if HAVE_LANE_TRACKING
1881 MUTEX_INIT( &U->tracking_cs); 1881 MUTEX_INIT( &U->tracking_cs);
1882 lua_getfield( L, 1, "track_lanes"); // settings track_lanes 1882 lua_getfield( L, 1, "track_lanes"); // settings track_lanes
1883 U->tracking_first = lua_toboolean( L, -1) ? TRACKING_END : NULL; 1883 U->tracking_first = lua_toboolean( L, -1) ? TRACKING_END : NULL;
1884 lua_pop( L, 1); // settings 1884 lua_pop( L, 1); // settings
1885#endif // HAVE_LANE_TRACKING 1885#endif // HAVE_LANE_TRACKING
1886 // Linked chains handling 1886 // Linked chains handling
1887 MUTEX_INIT( &U->selfdestruct_cs); 1887 MUTEX_INIT( &U->selfdestruct_cs);
1888 MUTEX_RECURSIVE_INIT( &U->require_cs); 1888 MUTEX_RECURSIVE_INIT( &U->require_cs);
1889 // Locks for 'tools.c' inc/dec counters 1889 // Locks for 'tools.c' inc/dec counters
1890 MUTEX_INIT( &U->deep_lock); 1890 MUTEX_INIT( &U->deep_lock);
1891 MUTEX_INIT( &U->mtid_lock); 1891 MUTEX_INIT( &U->mtid_lock);
1892 U->selfdestruct_first = SELFDESTRUCT_END; 1892 U->selfdestruct_first = SELFDESTRUCT_END;
1893 initialize_allocator_function( U, L); 1893 initialize_allocator_function( U, L);
1894 initialize_on_state_create( U, L); 1894 initialize_on_state_create( U, L);
1895 init_keepers( U, L); 1895 init_keepers( U, L);
1896 STACK_MID( L, 1); 1896 STACK_MID( L, 1);
1897 1897
1898 // Initialize 'timer_deep'; a common Linda object shared by all states 1898 // Initialize 'timer_deep'; a common Linda object shared by all states
1899 lua_pushcfunction( L, LG_linda); // settings lanes.linda 1899 lua_pushcfunction( L, LG_linda); // settings lanes.linda
1900 lua_pushliteral( L, "lanes-timer"); // settings lanes.linda "lanes-timer" 1900 lua_pushliteral( L, "lanes-timer"); // settings lanes.linda "lanes-timer"
1901 lua_call( L, 1, 1); // settings linda 1901 lua_call( L, 1, 1); // settings linda
1902 STACK_MID( L, 2); 1902 STACK_MID( L, 2);
1903 1903
1904 // Proxy userdata contents is only a 'DEEP_PRELUDE*' pointer 1904 // Proxy userdata contents is only a 'DEEP_PRELUDE*' pointer
1905 U->timer_deep = *(DeepPrelude**) lua_touserdata( L, -1); 1905 U->timer_deep = *(DeepPrelude**) lua_touserdata( L, -1);
1906 // increment refcount so that this linda remains alive as long as the universe exists. 1906 // increment refcount so that this linda remains alive as long as the universe exists.
1907 ++ U->timer_deep->refcount; 1907 ++ U->timer_deep->refcount;
1908 lua_pop( L, 1); // settings 1908 lua_pop( L, 1); // settings
1909 } 1909 }
1910 STACK_MID( L, 1); 1910 STACK_MID( L, 1);
1911 1911
1912 // Serialize calls to 'require' from now on, also in the primary state 1912 // Serialize calls to 'require' from now on, also in the primary state
1913 serialize_require( DEBUGSPEW_PARAM_COMMA( U) L); 1913 serialize_require( DEBUGSPEW_PARAM_COMMA( U) L);
1914 1914
1915 // Retrieve main module interface table 1915 // Retrieve main module interface table
1916 lua_pushvalue( L, lua_upvalueindex( 2)); // settings M 1916 lua_pushvalue( L, lua_upvalueindex( 2)); // settings M
1917 // remove configure() (this function) from the module interface 1917 // remove configure() (this function) from the module interface
1918 lua_pushnil( L); // settings M nil 1918 lua_pushnil( L); // settings M nil
1919 lua_setfield( L, -2, "configure"); // settings M 1919 lua_setfield( L, -2, "configure"); // settings M
1920 // add functions to the module's table 1920 // add functions to the module's table
1921 luaG_registerlibfuncs( L, lanes_functions); 1921 luaG_registerlibfuncs( L, lanes_functions);
1922#if HAVE_LANE_TRACKING 1922#if HAVE_LANE_TRACKING
1923 // register core.threads() only if settings say it should be available 1923 // register core.threads() only if settings say it should be available
1924 if( U->tracking_first != NULL) 1924 if( U->tracking_first != NULL)
1925 { 1925 {
1926 lua_pushcfunction( L, LG_threads); // settings M LG_threads() 1926 lua_pushcfunction( L, LG_threads); // settings M LG_threads()
1927 lua_setfield( L, -2, "threads"); // settings M 1927 lua_setfield( L, -2, "threads"); // settings M
1928 } 1928 }
1929#endif // HAVE_LANE_TRACKING 1929#endif // HAVE_LANE_TRACKING
1930 STACK_MID( L, 2); 1930 STACK_MID( L, 2);
1931 1931
1932 { 1932 {
1933 char const* errmsg; 1933 char const* errmsg;
1934 errmsg = push_deep_proxy( U, L, (DeepPrelude*) U->timer_deep, 0, eLM_LaneBody); // settings M timer_deep 1934 errmsg = push_deep_proxy( U, L, (DeepPrelude*) U->timer_deep, 0, eLM_LaneBody); // settings M timer_deep
1935 if( errmsg != NULL) 1935 if( errmsg != NULL)
1936 { 1936 {
1937 return luaL_error( L, errmsg); 1937 return luaL_error( L, errmsg);
1938 } 1938 }
1939 lua_setfield( L, -2, "timer_gateway"); // settings M 1939 lua_setfield( L, -2, "timer_gateway"); // settings M
1940 } 1940 }
1941 STACK_MID( L, 2); 1941 STACK_MID( L, 2);
1942 1942
1943 // prepare the metatable for threads 1943 // prepare the metatable for threads
1944 // contains keys: { __gc, __index, cached_error, cached_tostring, cancel, join, get_debug_threadname } 1944 // contains keys: { __gc, __index, cached_error, cached_tostring, cancel, join, get_debug_threadname }
1945 // 1945 //
1946 if( luaL_newmetatable( L, "Lane")) // settings M mt 1946 if( luaL_newmetatable( L, "Lane")) // settings M mt
1947 { 1947 {
1948 lua_pushcfunction( L, LG_thread_gc); // settings M mt LG_thread_gc 1948 lua_pushcfunction( L, LG_thread_gc); // settings M mt LG_thread_gc
1949 lua_setfield( L, -2, "__gc"); // settings M mt 1949 lua_setfield( L, -2, "__gc"); // settings M mt
1950 lua_pushcfunction( L, LG_thread_index); // settings M mt LG_thread_index 1950 lua_pushcfunction( L, LG_thread_index); // settings M mt LG_thread_index
1951 lua_setfield( L, -2, "__index"); // settings M mt 1951 lua_setfield( L, -2, "__index"); // settings M mt
1952 lua_getglobal( L, "error"); // settings M mt error 1952 lua_getglobal( L, "error"); // settings M mt error
1953 ASSERT_L( lua_isfunction( L, -1)); 1953 ASSERT_L( lua_isfunction( L, -1));
1954 lua_setfield( L, -2, "cached_error"); // settings M mt 1954 lua_setfield( L, -2, "cached_error"); // settings M mt
1955 lua_getglobal( L, "tostring"); // settings M mt tostring 1955 lua_getglobal( L, "tostring"); // settings M mt tostring
1956 ASSERT_L( lua_isfunction( L, -1)); 1956 ASSERT_L( lua_isfunction( L, -1));
1957 lua_setfield( L, -2, "cached_tostring"); // settings M mt 1957 lua_setfield( L, -2, "cached_tostring"); // settings M mt
1958 lua_pushcfunction( L, LG_thread_join); // settings M mt LG_thread_join 1958 lua_pushcfunction( L, LG_thread_join); // settings M mt LG_thread_join
1959 lua_setfield( L, -2, "join"); // settings M mt 1959 lua_setfield( L, -2, "join"); // settings M mt
1960 lua_pushcfunction( L, LG_get_debug_threadname); // settings M mt LG_get_debug_threadname 1960 lua_pushcfunction( L, LG_get_debug_threadname); // settings M mt LG_get_debug_threadname
1961 lua_setfield( L, -2, "get_debug_threadname"); // settings M mt 1961 lua_setfield( L, -2, "get_debug_threadname"); // settings M mt
1962 lua_pushcfunction( L, LG_thread_cancel); // settings M mt LG_thread_cancel 1962 lua_pushcfunction( L, LG_thread_cancel); // settings M mt LG_thread_cancel
1963 lua_setfield( L, -2, "cancel"); // settings M mt 1963 lua_setfield( L, -2, "cancel"); // settings M mt
1964 lua_pushliteral( L, "Lane"); // settings M mt "Lane" 1964 lua_pushliteral( L, "Lane"); // settings M mt "Lane"
1965 lua_setfield( L, -2, "__metatable"); // settings M mt 1965 lua_setfield( L, -2, "__metatable"); // settings M mt
1966 } 1966 }
1967 1967
1968 lua_pushcclosure( L, LG_lane_new, 1); // settings M lane_new 1968 lua_pushcclosure( L, LG_lane_new, 1); // settings M lane_new
1969 lua_setfield( L, -2, "lane_new"); // settings M 1969 lua_setfield( L, -2, "lane_new"); // settings M
1970 1970
1971 // we can't register 'lanes.require' normally because we want to create an upvalued closure 1971 // we can't register 'lanes.require' normally because we want to create an upvalued closure
1972 lua_getglobal( L, "require"); // settings M require 1972 lua_getglobal( L, "require"); // settings M require
1973 lua_pushcclosure( L, LG_require, 1); // settings M lanes.require 1973 lua_pushcclosure( L, LG_require, 1); // settings M lanes.require
1974 lua_setfield( L, -2, "require"); // settings M 1974 lua_setfield( L, -2, "require"); // settings M
1975 1975
1976 lua_pushfstring( 1976 lua_pushfstring(
1977 L, "%d.%d.%d" 1977 L, "%d.%d.%d"
1978 , LANES_VERSION_MAJOR, LANES_VERSION_MINOR, LANES_VERSION_PATCH 1978 , LANES_VERSION_MAJOR, LANES_VERSION_MINOR, LANES_VERSION_PATCH
1979 ); // settings M VERSION 1979 ); // settings M VERSION
1980 lua_setfield( L, -2, "version"); // settings M 1980 lua_setfield( L, -2, "version"); // settings M
1981 1981
1982 lua_pushinteger(L, THREAD_PRIO_MAX); // settings M THREAD_PRIO_MAX 1982 lua_pushinteger(L, THREAD_PRIO_MAX); // settings M THREAD_PRIO_MAX
1983 lua_setfield( L, -2, "max_prio"); // settings M 1983 lua_setfield( L, -2, "max_prio"); // settings M
1984 1984
1985 push_unique_key( L, CANCEL_ERROR); // settings M CANCEL_ERROR 1985 push_unique_key( L, CANCEL_ERROR); // settings M CANCEL_ERROR
1986 lua_setfield( L, -2, "cancel_error"); // settings M 1986 lua_setfield( L, -2, "cancel_error"); // settings M
1987 1987
1988 STACK_MID( L, 2); // reference stack contains only the function argument 'settings' 1988 STACK_MID( L, 2); // reference stack contains only the function argument 'settings'
1989 // we'll need this every time we transfer some C function from/to this state 1989 // we'll need this every time we transfer some C function from/to this state
1990 REGISTRY_SET( L, LOOKUP_REGKEY, lua_newtable( L)); 1990 REGISTRY_SET( L, LOOKUP_REGKEY, lua_newtable( L));
1991 STACK_MID( L, 2); 1991 STACK_MID( L, 2);
1992 1992
1993 // register all native functions found in that module in the transferable functions database 1993 // register all native functions found in that module in the transferable functions database
1994 // we process it before _G because we don't want to find the module when scanning _G (this would generate longer names) 1994 // we process it before _G because we don't want to find the module when scanning _G (this would generate longer names)
1995 // for example in package.loaded["lanes.core"].* 1995 // for example in package.loaded["lanes.core"].*
1996 populate_func_lookup_table( L, -1, name); 1996 populate_func_lookup_table( L, -1, name);
1997 STACK_MID( L, 2); 1997 STACK_MID( L, 2);
1998 1998
1999 // record all existing C/JIT-fast functions 1999 // record all existing C/JIT-fast functions
2000 // Lua 5.2 no longer has LUA_GLOBALSINDEX: we must push globals table on the stack 2000 // Lua 5.2 no longer has LUA_GLOBALSINDEX: we must push globals table on the stack
2001 if( from_master_state) 2001 if( from_master_state)
2002 { 2002 {
2003 // don't do this when called during the initialization of a new lane, 2003 // don't do this when called during the initialization of a new lane,
2004 // because we will do it after on_state_create() is called, 2004 // because we will do it after on_state_create() is called,
2005 // and we don't want to skip _G because of caching in case globals are created then 2005 // and we don't want to skip _G because of caching in case globals are created then
2006 lua_pushglobaltable( L); // settings M _G 2006 lua_pushglobaltable( L); // settings M _G
2007 populate_func_lookup_table( L, -1, NULL); 2007 populate_func_lookup_table( L, -1, NULL);
2008 lua_pop( L, 1); // settings M 2008 lua_pop( L, 1); // settings M
2009 } 2009 }
2010 lua_pop( L, 1); // settings 2010 lua_pop( L, 1); // settings
2011 2011
2012 // set _R[CONFIG_REGKEY] = settings 2012 // set _R[CONFIG_REGKEY] = settings
2013 REGISTRY_SET( L, CONFIG_REGKEY, lua_pushvalue( L, -2)); // -2 because CONFIG_REGKEY is pushed before the value itself 2013 REGISTRY_SET( L, CONFIG_REGKEY, lua_pushvalue( L, -2)); // -2 because CONFIG_REGKEY is pushed before the value itself
2014 STACK_END( L, 1); 2014 STACK_END( L, 1);
2015 DEBUGSPEW_CODE( fprintf( stderr, INDENT_BEGIN "%p: lanes.configure() END\n" INDENT_END, L)); 2015 DEBUGSPEW_CODE( fprintf( stderr, INDENT_BEGIN "%p: lanes.configure() END\n" INDENT_END, L));
2016 DEBUGSPEW_CODE( -- U->debugspew_indent_depth); 2016 DEBUGSPEW_CODE( -- U->debugspew_indent_depth);
2017 // Return the settings table 2017 // Return the settings table
2018 return 1; 2018 return 1;
2019} 2019}
2020 2020
2021#if defined PLATFORM_WIN32 && !defined NDEBUG 2021#if defined PLATFORM_WIN32 && !defined NDEBUG
@@ -2024,11 +2024,11 @@ LUAG_FUNC( configure)
2024 2024
2025void signal_handler( int signal) 2025void signal_handler( int signal)
2026{ 2026{
2027 if( signal == SIGABRT) 2027 if( signal == SIGABRT)
2028 { 2028 {
2029 _cprintf( "caught abnormal termination!"); 2029 _cprintf( "caught abnormal termination!");
2030 abort(); 2030 abort();
2031 } 2031 }
2032} 2032}
2033 2033
2034// helper to have correct callstacks when crashing a Win32 running on 64 bits Windows 2034// helper to have correct callstacks when crashing a Win32 running on 64 bits Windows
@@ -2037,88 +2037,88 @@ static volatile long s_ecoc_initCount = 0;
2037static volatile int s_ecoc_go_ahead = 0; 2037static volatile int s_ecoc_go_ahead = 0;
2038static void EnableCrashingOnCrashes( void) 2038static void EnableCrashingOnCrashes( void)
2039{ 2039{
2040 if( InterlockedCompareExchange( &s_ecoc_initCount, 1, 0) == 0) 2040 if( InterlockedCompareExchange( &s_ecoc_initCount, 1, 0) == 0)
2041 { 2041 {
2042 typedef BOOL (WINAPI* tGetPolicy)( LPDWORD lpFlags); 2042 typedef BOOL (WINAPI* tGetPolicy)( LPDWORD lpFlags);
2043 typedef BOOL (WINAPI* tSetPolicy)( DWORD dwFlags); 2043 typedef BOOL (WINAPI* tSetPolicy)( DWORD dwFlags);
2044 const DWORD EXCEPTION_SWALLOWING = 0x1; 2044 const DWORD EXCEPTION_SWALLOWING = 0x1;
2045 2045
2046 HMODULE kernel32 = LoadLibraryA("kernel32.dll"); 2046 HMODULE kernel32 = LoadLibraryA("kernel32.dll");
2047 tGetPolicy pGetPolicy = (tGetPolicy)GetProcAddress(kernel32, "GetProcessUserModeExceptionPolicy"); 2047 tGetPolicy pGetPolicy = (tGetPolicy)GetProcAddress(kernel32, "GetProcessUserModeExceptionPolicy");
2048 tSetPolicy pSetPolicy = (tSetPolicy)GetProcAddress(kernel32, "SetProcessUserModeExceptionPolicy"); 2048 tSetPolicy pSetPolicy = (tSetPolicy)GetProcAddress(kernel32, "SetProcessUserModeExceptionPolicy");
2049 if( pGetPolicy && pSetPolicy) 2049 if( pGetPolicy && pSetPolicy)
2050 { 2050 {
2051 DWORD dwFlags; 2051 DWORD dwFlags;
2052 if( pGetPolicy( &dwFlags)) 2052 if( pGetPolicy( &dwFlags))
2053 { 2053 {
2054 // Turn off the filter 2054 // Turn off the filter
2055 pSetPolicy( dwFlags & ~EXCEPTION_SWALLOWING); 2055 pSetPolicy( dwFlags & ~EXCEPTION_SWALLOWING);
2056 } 2056 }
2057 } 2057 }
2058 //typedef void (* SignalHandlerPointer)( int); 2058 //typedef void (* SignalHandlerPointer)( int);
2059 /*SignalHandlerPointer previousHandler =*/ signal( SIGABRT, signal_handler); 2059 /*SignalHandlerPointer previousHandler =*/ signal( SIGABRT, signal_handler);
2060 2060
2061 s_ecoc_go_ahead = 1; // let others pass 2061 s_ecoc_go_ahead = 1; // let others pass
2062 } 2062 }
2063 else 2063 else
2064 { 2064 {
2065 while( !s_ecoc_go_ahead) { Sleep(1); } // changes threads 2065 while( !s_ecoc_go_ahead) { Sleep(1); } // changes threads
2066 } 2066 }
2067} 2067}
2068#endif // PLATFORM_WIN32 2068#endif // PLATFORM_WIN32
2069 2069
2070int LANES_API luaopen_lanes_core( lua_State* L) 2070int LANES_API luaopen_lanes_core( lua_State* L)
2071{ 2071{
2072#if defined PLATFORM_WIN32 && !defined NDEBUG 2072#if defined PLATFORM_WIN32 && !defined NDEBUG
2073 EnableCrashingOnCrashes(); 2073 EnableCrashingOnCrashes();
2074#endif // defined PLATFORM_WIN32 && !defined NDEBUG 2074#endif // defined PLATFORM_WIN32 && !defined NDEBUG
2075 2075
2076 STACK_GROW( L, 4); 2076 STACK_GROW( L, 4);
2077 STACK_CHECK( L, 0); 2077 STACK_CHECK( L, 0);
2078 2078
2079 // Create main module interface table 2079 // Create main module interface table
2080 // we only have 1 closure, which must be called to configure Lanes 2080 // we only have 1 closure, which must be called to configure Lanes
2081 lua_newtable( L); // M 2081 lua_newtable( L); // M
2082 lua_pushvalue( L, 1); // M "lanes.core" 2082 lua_pushvalue( L, 1); // M "lanes.core"
2083 lua_pushvalue( L, -2); // M "lanes.core" M 2083 lua_pushvalue( L, -2); // M "lanes.core" M
2084 lua_pushcclosure( L, LG_configure, 2); // M LG_configure() 2084 lua_pushcclosure( L, LG_configure, 2); // M LG_configure()
2085 REGISTRY_GET( L, CONFIG_REGKEY); // M LG_configure() settings 2085 REGISTRY_GET( L, CONFIG_REGKEY); // M LG_configure() settings
2086 if( !lua_isnil( L, -1)) // this is not the first require "lanes.core": call configure() immediately 2086 if( !lua_isnil( L, -1)) // this is not the first require "lanes.core": call configure() immediately
2087 { 2087 {
2088 lua_pushvalue( L, -1); // M LG_configure() settings settings 2088 lua_pushvalue( L, -1); // M LG_configure() settings settings
2089 lua_setfield( L, -4, "settings"); // M LG_configure() settings 2089 lua_setfield( L, -4, "settings"); // M LG_configure() settings
2090 lua_call( L, 1, 0); // M 2090 lua_call( L, 1, 0); // M
2091 } 2091 }
2092 else 2092 else
2093 { 2093 {
2094 // will do nothing on first invocation, as we haven't stored settings in the registry yet 2094 // will do nothing on first invocation, as we haven't stored settings in the registry yet
2095 lua_setfield( L, -3, "settings"); // M LG_configure() 2095 lua_setfield( L, -3, "settings"); // M LG_configure()
2096 lua_setfield( L, -2, "configure"); // M 2096 lua_setfield( L, -2, "configure"); // M
2097 } 2097 }
2098 2098
2099 STACK_END( L, 1); 2099 STACK_END( L, 1);
2100 return 1; 2100 return 1;
2101} 2101}
2102 2102
2103static int default_luaopen_lanes( lua_State* L) 2103static int default_luaopen_lanes( lua_State* L)
2104{ 2104{
2105 int rc = luaL_loadfile( L, "lanes.lua") || lua_pcall( L, 0, 1, 0); 2105 int rc = luaL_loadfile( L, "lanes.lua") || lua_pcall( L, 0, 1, 0);
2106 if( rc != LUA_OK) 2106 if( rc != LUA_OK)
2107 { 2107 {
2108 return luaL_error( L, "failed to initialize embedded Lanes"); 2108 return luaL_error( L, "failed to initialize embedded Lanes");
2109 } 2109 }
2110 return 1; 2110 return 1;
2111} 2111}
2112 2112
2113// call this instead of luaopen_lanes_core() when embedding Lua and Lanes in a custom application 2113// call this instead of luaopen_lanes_core() when embedding Lua and Lanes in a custom application
2114void LANES_API luaopen_lanes_embedded( lua_State* L, lua_CFunction _luaopen_lanes) 2114void LANES_API luaopen_lanes_embedded( lua_State* L, lua_CFunction _luaopen_lanes)
2115{ 2115{
2116 STACK_CHECK( L, 0); 2116 STACK_CHECK( L, 0);
2117 // pre-require lanes.core so that when lanes.lua calls require "lanes.core" it finds it is already loaded 2117 // pre-require lanes.core so that when lanes.lua calls require "lanes.core" it finds it is already loaded
2118 luaL_requiref( L, "lanes.core", luaopen_lanes_core, 0); // ... lanes.core 2118 luaL_requiref( L, "lanes.core", luaopen_lanes_core, 0); // ... lanes.core
2119 lua_pop( L, 1); // ... 2119 lua_pop( L, 1); // ...
2120 STACK_MID( L, 0); 2120 STACK_MID( L, 0);
2121 // call user-provided function that runs the chunk "lanes.lua" from wherever they stored it 2121 // call user-provided function that runs the chunk "lanes.lua" from wherever they stored it
2122 luaL_requiref( L, "lanes", _luaopen_lanes ? _luaopen_lanes : default_luaopen_lanes, 0); // ... lanes 2122 luaL_requiref( L, "lanes", _luaopen_lanes ? _luaopen_lanes : default_luaopen_lanes, 0); // ... lanes
2123 STACK_END( L, 1); 2123 STACK_END( L, 1);
2124} 2124}
diff --git a/src/lanes.lua b/src/lanes.lua
index 4d6deac..2f06137 100644
--- a/src/lanes.lua
+++ b/src/lanes.lua
@@ -46,704 +46,704 @@ local lanes = setmetatable( {}, lanesMeta)
46-- this function is available in the public interface until it is called, after which it disappears 46-- this function is available in the public interface until it is called, after which it disappears
47lanes.configure = function( settings_) 47lanes.configure = function( settings_)
48 48
49 -- This check is for sublanes requiring Lanes 49 -- This check is for sublanes requiring Lanes
50 -- 50 --
51 -- TBD: We could also have the C level expose 'string.gmatch' for us. But this is simpler. 51 -- TBD: We could also have the C level expose 'string.gmatch' for us. But this is simpler.
52 -- 52 --
53 if not string then 53 if not string then
54 error( "To use 'lanes', you will also need to have 'string' available.", 2) 54 error( "To use 'lanes', you will also need to have 'string' available.", 2)
55 end 55 end
56 -- Configure called so remove metatable from lanes 56 -- Configure called so remove metatable from lanes
57 setmetatable( lanes, nil) 57 setmetatable( lanes, nil)
58 -- 58 --
59 -- Cache globals for code that might run under sandboxing 59 -- Cache globals for code that might run under sandboxing
60 -- 60 --
61 local assert = assert( assert) 61 local assert = assert( assert)
62 local string_gmatch = assert( string.gmatch) 62 local string_gmatch = assert( string.gmatch)
63 local string_format = assert( string.format) 63 local string_format = assert( string.format)
64 local select = assert( select) 64 local select = assert( select)
65 local type = assert( type) 65 local type = assert( type)
66 local pairs = assert( pairs) 66 local pairs = assert( pairs)
67 local tostring = assert( tostring) 67 local tostring = assert( tostring)
68 local error = assert( error) 68 local error = assert( error)
69 69
70 local default_params = 70 local default_params =
71 { 71 {
72 nb_keepers = 1, 72 nb_keepers = 1,
73 on_state_create = nil, 73 on_state_create = nil,
74 shutdown_timeout = 0.25, 74 shutdown_timeout = 0.25,
75 with_timers = true, 75 with_timers = true,
76 track_lanes = false, 76 track_lanes = false,
77 demote_full_userdata = nil, 77 demote_full_userdata = nil,
78 verbose_errors = false, 78 verbose_errors = false,
79 allocator = nil 79 allocator = nil
80 } 80 }
81 local boolean_param_checker = function( val_) 81 local boolean_param_checker = function( val_)
82 -- non-'boolean-false' should be 'boolean-true' or nil 82 -- non-'boolean-false' should be 'boolean-true' or nil
83 return val_ and (val_ == true) or true 83 return val_ and (val_ == true) or true
84 end 84 end
85 local param_checkers = 85 local param_checkers =
86 { 86 {
87 nb_keepers = function( val_) 87 nb_keepers = function( val_)
88 -- nb_keepers should be a number > 0 88 -- nb_keepers should be a number > 0
89 return type( val_) == "number" and val_ > 0 89 return type( val_) == "number" and val_ > 0
90 end, 90 end,
91 with_timers = boolean_param_checker, 91 with_timers = boolean_param_checker,
92 allocator = function( val_) 92 allocator = function( val_)
93 -- can be nil, "protected", or a function 93 -- can be nil, "protected", or a function
94 return val_ and (type( val_) == "function" or val_ == "protected") or true 94 return val_ and (type( val_) == "function" or val_ == "protected") or true
95 end, 95 end,
96 on_state_create = function( val_) 96 on_state_create = function( val_)
97 -- on_state_create may be nil or a function 97 -- on_state_create may be nil or a function
98 return val_ and type( val_) == "function" or true 98 return val_ and type( val_) == "function" or true
99 end, 99 end,
100 shutdown_timeout = function( val_) 100 shutdown_timeout = function( val_)
101 -- shutdown_timeout should be a number >= 0 101 -- shutdown_timeout should be a number >= 0
102 return type( val_) == "number" and val_ >= 0 102 return type( val_) == "number" and val_ >= 0
103 end, 103 end,
104 track_lanes = boolean_param_checker, 104 track_lanes = boolean_param_checker,
105 demote_full_userdata = boolean_param_checker, 105 demote_full_userdata = boolean_param_checker,
106 verbose_errors = boolean_param_checker 106 verbose_errors = boolean_param_checker
107 } 107 }
108 108
109 local params_checker = function( settings_) 109 local params_checker = function( settings_)
110 if not settings_ then 110 if not settings_ then
111 return default_params 111 return default_params
112 end 112 end
113 -- make a copy of the table to leave the provided one unchanged, *and* to help ensure it won't change behind our back 113 -- make a copy of the table to leave the provided one unchanged, *and* to help ensure it won't change behind our back
114 local settings = {} 114 local settings = {}
115 if type( settings_) ~= "table" then 115 if type( settings_) ~= "table" then
116 error "Bad parameter #1 to lanes.configure(), should be a table" 116 error "Bad parameter #1 to lanes.configure(), should be a table"
117 end 117 end
118 -- any setting unknown to Lanes raises an error 118 -- any setting unknown to Lanes raises an error
119 for setting, _ in pairs( settings_) do 119 for setting, _ in pairs( settings_) do
120 if not param_checkers[setting] then 120 if not param_checkers[setting] then
121 error( "Unknown parameter '" .. setting .. "' in configure options") 121 error( "Unknown parameter '" .. setting .. "' in configure options")
122 end 122 end
123 end 123 end
124 -- any setting not present in the provided parameters takes the default value 124 -- any setting not present in the provided parameters takes the default value
125 for key, checker in pairs( param_checkers) do 125 for key, checker in pairs( param_checkers) do
126 local my_param = settings_[key] 126 local my_param = settings_[key]
127 local param 127 local param
128 if my_param ~= nil then 128 if my_param ~= nil then
129 param = my_param 129 param = my_param
130 else 130 else
131 param = default_params[key] 131 param = default_params[key]
132 end 132 end
133 if not checker( param) then 133 if not checker( param) then
134 error( "Bad " .. key .. ": " .. tostring( param), 2) 134 error( "Bad " .. key .. ": " .. tostring( param), 2)
135 end 135 end
136 settings[key] = param 136 settings[key] = param
137 end 137 end
138 return settings 138 return settings
139 end 139 end
140 local settings = core.configure and core.configure( params_checker( settings_)) or core.settings 140 local settings = core.configure and core.configure( params_checker( settings_)) or core.settings
141 local core_lane_new = assert( core.lane_new) 141 local core_lane_new = assert( core.lane_new)
142 local max_prio = assert( core.max_prio) 142 local max_prio = assert( core.max_prio)
143 143
144 lanes.ABOUT = 144 lanes.ABOUT =
145 { 145 {
146 author= "Asko Kauppi <akauppi@gmail.com>, Benoit Germain <bnt.germain@gmail.com>", 146 author= "Asko Kauppi <akauppi@gmail.com>, Benoit Germain <bnt.germain@gmail.com>",
147 description= "Running multiple Lua states in parallel", 147 description= "Running multiple Lua states in parallel",
148 license= "MIT/X11", 148 license= "MIT/X11",
149 copyright= "Copyright (c) 2007-10, Asko Kauppi; (c) 2011-19, Benoit Germain", 149 copyright= "Copyright (c) 2007-10, Asko Kauppi; (c) 2011-19, Benoit Germain",
150 version = assert( core.version) 150 version = assert( core.version)
151 } 151 }
152 152
153 153
154 -- Making copies of necessary system libs will pass them on as upvalues; 154 -- Making copies of necessary system libs will pass them on as upvalues;
155 -- only the first state doing "require 'lanes'" will need to have 'string' 155 -- only the first state doing "require 'lanes'" will need to have 'string'
156 -- and 'table' visible. 156 -- and 'table' visible.
157 -- 157 --
158 local function WR(str) 158 local function WR(str)
159 io.stderr:write( str.."\n" ) 159 io.stderr:write( str.."\n" )
160 end 160 end
161 161
162 local function DUMP( tbl ) 162 local function DUMP( tbl )
163 if not tbl then return end 163 if not tbl then return end
164 local str="" 164 local str=""
165 for k,v in pairs(tbl) do 165 for k,v in pairs(tbl) do
166 str= str..k.."="..tostring(v).."\n" 166 str= str..k.."="..tostring(v).."\n"
167 end 167 end
168 WR(str) 168 WR(str)
169 end 169 end
170 170
171 171
172 ---=== Laning ===--- 172 ---=== Laning ===---
173 173
174 -- lane_h[1..n]: lane results, same as via 'lane_h:join()' 174 -- lane_h[1..n]: lane results, same as via 'lane_h:join()'
175 -- lane_h[0]: can be read to make sure a thread has finished (always gives 'true') 175 -- lane_h[0]: can be read to make sure a thread has finished (always gives 'true')
176 -- lane_h[-1]: error message, without propagating the error 176 -- lane_h[-1]: error message, without propagating the error
177 -- 177 --
178 -- Reading a Lane result (or [0]) propagates a possible error in the lane 178 -- Reading a Lane result (or [0]) propagates a possible error in the lane
179 -- (and execution does not return). Cancelled lanes give 'nil' values. 179 -- (and execution does not return). Cancelled lanes give 'nil' values.
180 -- 180 --
181 -- lane_h.state: "pending"/"running"/"waiting"/"done"/"error"/"cancelled" 181 -- lane_h.state: "pending"/"running"/"waiting"/"done"/"error"/"cancelled"
182 -- 182 --
183 -- Note: Would be great to be able to have '__ipairs' metamethod, that gets 183 -- Note: Would be great to be able to have '__ipairs' metamethod, that gets
184 -- called by 'ipairs()' function to custom iterate objects. We'd use it 184 -- called by 'ipairs()' function to custom iterate objects. We'd use it
185 -- for making sure a lane has ended (results are available); not requiring 185 -- for making sure a lane has ended (results are available); not requiring
186 -- the user to precede a loop by explicit 'h[0]' or 'h:join()'. 186 -- the user to precede a loop by explicit 'h[0]' or 'h:join()'.
187 -- 187 --
188 -- Or, even better, 'ipairs()' should start valuing '__index' instead 188 -- Or, even better, 'ipairs()' should start valuing '__index' instead
189 -- of using raw reads that bypass it. 189 -- of using raw reads that bypass it.
190 -- 190 --
191 ----- 191 -----
192 -- lanes.gen( [libs_str|opt_tbl [, ...],] lane_func ) ( [...] ) -> h 192 -- lanes.gen( [libs_str|opt_tbl [, ...],] lane_func ) ( [...] ) -> h
193 -- 193 --
194 -- 'libs': nil: no libraries available (default) 194 -- 'libs': nil: no libraries available (default)
195 -- "": only base library ('assert', 'print', 'unpack' etc.) 195 -- "": only base library ('assert', 'print', 'unpack' etc.)
196 -- "math,os": math + os + base libraries (named ones + base) 196 -- "math,os": math + os + base libraries (named ones + base)
197 -- "*": all standard libraries available 197 -- "*": all standard libraries available
198 -- 198 --
199 -- 'opt': .priority: int (-3..+3) smaller is lower priority (0 = default) 199 -- 'opt': .priority: int (-3..+3) smaller is lower priority (0 = default)
200 -- 200 --
201 -- .globals: table of globals to set for a new thread (passed by value) 201 -- .globals: table of globals to set for a new thread (passed by value)
202 -- 202 --
203 -- .required: table of packages to require 203 -- .required: table of packages to require
204 -- 204 --
205 -- .gc_cb: function called when the lane handle is collected 205 -- .gc_cb: function called when the lane handle is collected
206 -- 206 --
207 -- ... (more options may be introduced later) ... 207 -- ... (more options may be introduced later) ...
208 -- 208 --
209 -- Calling with a function parameter ('lane_func') ends the string/table 209 -- Calling with a function parameter ('lane_func') ends the string/table
210 -- modifiers, and prepares a lane generator. 210 -- modifiers, and prepares a lane generator.
211 211
212 local valid_libs = 212 local valid_libs =
213 { 213 {
214 ["package"] = true, 214 ["package"] = true,
215 ["table"] = true, 215 ["table"] = true,
216 ["io"] = true, 216 ["io"] = true,
217 ["os"] = true, 217 ["os"] = true,
218 ["string"] = true, 218 ["string"] = true,
219 ["math"] = true, 219 ["math"] = true,
220 ["debug"] = true, 220 ["debug"] = true,
221 ["bit32"] = true, -- Lua 5.2 only, ignored silently under 5.1 221 ["bit32"] = true, -- Lua 5.2 only, ignored silently under 5.1
222 ["utf8"] = true, -- Lua 5.3 only, ignored silently under 5.1 and 5.2 222 ["utf8"] = true, -- Lua 5.3 only, ignored silently under 5.1 and 5.2
223 ["bit"] = true, -- LuaJIT only, ignored silently under PUC-Lua 223 ["bit"] = true, -- LuaJIT only, ignored silently under PUC-Lua
224 ["jit"] = true, -- LuaJIT only, ignored silently under PUC-Lua 224 ["jit"] = true, -- LuaJIT only, ignored silently under PUC-Lua
225 ["ffi"] = true, -- LuaJIT only, ignored silently under PUC-Lua 225 ["ffi"] = true, -- LuaJIT only, ignored silently under PUC-Lua
226 -- 226 --
227 ["base"] = true, 227 ["base"] = true,
228 ["coroutine"] = true, -- part of "base" in Lua 5.1 228 ["coroutine"] = true, -- part of "base" in Lua 5.1
229 ["lanes.core"] = true 229 ["lanes.core"] = true
230 } 230 }
231 231
232 local raise_option_error = function( name_, tv_, v_) 232 local raise_option_error = function( name_, tv_, v_)
233 error( "Bad '" .. name_ .. "' option: " .. tv_ .. " " .. string_format( "%q", tostring( v_)), 4) 233 error( "Bad '" .. name_ .. "' option: " .. tv_ .. " " .. string_format( "%q", tostring( v_)), 4)
234 end 234 end
235 235
236 local opt_validators = 236 local opt_validators =
237 { 237 {
238 priority = function( v_) 238 priority = function( v_)
239 local tv = type( v_) 239 local tv = type( v_)
240 return (tv == "number") and v_ or raise_option_error( "priority", tv, v_) 240 return (tv == "number") and v_ or raise_option_error( "priority", tv, v_)
241 end, 241 end,
242 globals = function( v_) 242 globals = function( v_)
243 local tv = type( v_) 243 local tv = type( v_)
244 return (tv == "table") and v_ or raise_option_error( "globals", tv, v_) 244 return (tv == "table") and v_ or raise_option_error( "globals", tv, v_)
245 end, 245 end,
246 package = function( v_) 246 package = function( v_)
247 local tv = type( v_) 247 local tv = type( v_)
248 return (tv == "table") and v_ or raise_option_error( "package", tv, v_) 248 return (tv == "table") and v_ or raise_option_error( "package", tv, v_)
249 end, 249 end,
250 required = function( v_) 250 required = function( v_)
251 local tv = type( v_) 251 local tv = type( v_)
252 return (tv == "table") and v_ or raise_option_error( "required", tv, v_) 252 return (tv == "table") and v_ or raise_option_error( "required", tv, v_)
253 end, 253 end,
254 gc_cb = function( v_) 254 gc_cb = function( v_)
255 local tv = type( v_) 255 local tv = type( v_)
256 return (tv == "function") and v_ or raise_option_error( "gc_cb", tv, v_) 256 return (tv == "function") and v_ or raise_option_error( "gc_cb", tv, v_)
257 end 257 end
258 } 258 }
259 259
260 -- PUBLIC LANES API 260 -- PUBLIC LANES API
261 -- receives a sequence of strings and tables, plus a function 261 -- receives a sequence of strings and tables, plus a function
262 local gen = function( ...) 262 local gen = function( ...)
263 -- aggregrate all strings together, separated by "," as well as tables 263 -- aggregrate all strings together, separated by "," as well as tables
264 -- the strings are a list of libraries to open 264 -- the strings are a list of libraries to open
265 -- the tables contain the lane options 265 -- the tables contain the lane options
266 local opt = {} 266 local opt = {}
267 local libs = nil 267 local libs = nil
268 268
269 local n = select( '#', ...) 269 local n = select( '#', ...)
270 270
271 -- we need at least a function 271 -- we need at least a function
272 if n == 0 then 272 if n == 0 then
273 error( "No parameters!", 2) 273 error( "No parameters!", 2)
274 end 274 end
275 275
276 -- all arguments but the last must be nil, strings, or tables 276 -- all arguments but the last must be nil, strings, or tables
277 for i = 1, n - 1 do 277 for i = 1, n - 1 do
278 local v = select( i, ...) 278 local v = select( i, ...)
279 local tv = type( v) 279 local tv = type( v)
280 if tv == "string" then 280 if tv == "string" then
281 libs = libs and libs .. "," .. v or v 281 libs = libs and libs .. "," .. v or v
282 elseif tv == "table" then 282 elseif tv == "table" then
283 for k, vv in pairs( v) do 283 for k, vv in pairs( v) do
284 opt[k]= vv 284 opt[k]= vv
285 end 285 end
286 elseif v == nil then 286 elseif v == nil then
287 -- skip 287 -- skip
288 else 288 else
289 error( "Bad parameter " .. i .. ": " .. tv .. " " .. string_format( "%q", tostring( v)), 2) 289 error( "Bad parameter " .. i .. ": " .. tv .. " " .. string_format( "%q", tostring( v)), 2)
290 end 290 end
291 end 291 end
292 292
293 -- the last argument should be a function or a string 293 -- the last argument should be a function or a string
294 local func = select( n, ...) 294 local func = select( n, ...)
295 local functype = type( func) 295 local functype = type( func)
296 if functype ~= "function" and functype ~= "string" then 296 if functype ~= "function" and functype ~= "string" then
297 error( "Last parameter not function or string: " .. functype .. " " .. string_format( "%q", tostring( func)), 2) 297 error( "Last parameter not function or string: " .. functype .. " " .. string_format( "%q", tostring( func)), 2)
298 end 298 end
299 299
300 -- check that the caller only provides reserved library names, and those only once 300 -- check that the caller only provides reserved library names, and those only once
301 -- "*" is a special case that doesn't require individual checking 301 -- "*" is a special case that doesn't require individual checking
302 if libs and libs ~= "*" then 302 if libs and libs ~= "*" then
303 local found = {} 303 local found = {}
304 for s in string_gmatch(libs, "[%a%d.]+") do 304 for s in string_gmatch(libs, "[%a%d.]+") do
305 if not valid_libs[s] then 305 if not valid_libs[s] then
306 error( "Bad library name: " .. s, 2) 306 error( "Bad library name: " .. s, 2)
307 else 307 else
308 found[s] = (found[s] or 0) + 1 308 found[s] = (found[s] or 0) + 1
309 if found[s] > 1 then 309 if found[s] > 1 then
310 error( "libs specification contains '" .. s .. "' more than once", 2) 310 error( "libs specification contains '" .. s .. "' more than once", 2)
311 end 311 end
312 end 312 end
313 end 313 end
314 end 314 end
315 315
316 -- validate that each option is known and properly valued 316 -- validate that each option is known and properly valued
317 for k, v in pairs( opt) do 317 for k, v in pairs( opt) do
318 local validator = opt_validators[k] 318 local validator = opt_validators[k]
319 if not validator then 319 if not validator then
320 error( (type( k) == "number" and "Unkeyed option: " .. type( v) .. " " .. string_format( "%q", tostring( v)) or "Bad '" .. tostring( k) .. "' option"), 2) 320 error( (type( k) == "number" and "Unkeyed option: " .. type( v) .. " " .. string_format( "%q", tostring( v)) or "Bad '" .. tostring( k) .. "' option"), 2)
321 else 321 else
322 opt[k] = validator( v) 322 opt[k] = validator( v)
323 end 323 end
324 end 324 end
325 325
326 local priority, globals, package, required, gc_cb = opt.priority, opt.globals, opt.package or package, opt.required, opt.gc_cb 326 local priority, globals, package, required, gc_cb = opt.priority, opt.globals, opt.package or package, opt.required, opt.gc_cb
327 return function( ...) 327 return function( ...)
328 -- must pass functions args last else they will be truncated to the first one 328 -- must pass functions args last else they will be truncated to the first one
329 return core_lane_new( func, libs, priority, globals, package, required, gc_cb, ...) 329 return core_lane_new( func, libs, priority, globals, package, required, gc_cb, ...)
330 end 330 end
331 end -- gen() 331 end -- gen()
332 332
333 ---=== Timers ===--- 333 ---=== Timers ===---
334 334
335 -- PUBLIC LANES API 335 -- PUBLIC LANES API
336 local timer = function() error "timers are not active" end 336 local timer = function() error "timers are not active" end
337 local timers = timer 337 local timers = timer
338 local timer_lane = nil 338 local timer_lane = nil
339 339
340 -- timer_gateway should always exist, even when the settings disable the timers 340 -- timer_gateway should always exist, even when the settings disable the timers
341 local timer_gateway = assert( core.timer_gateway) 341 local timer_gateway = assert( core.timer_gateway)
342 342
343 ----- 343 -----
344 -- <void> = sleep( [seconds_]) 344 -- <void> = sleep( [seconds_])
345 -- 345 --
346 -- PUBLIC LANES API 346 -- PUBLIC LANES API
347 local sleep = function( seconds_) 347 local sleep = function( seconds_)
348 seconds_ = seconds_ or 0.0 -- this causes false and nil to be a valid input, equivalent to 0.0, but that's ok 348 seconds_ = seconds_ or 0.0 -- this causes false and nil to be a valid input, equivalent to 0.0, but that's ok
349 if type( seconds_) ~= "number" then 349 if type( seconds_) ~= "number" then
350 error( "invalid duration " .. string_format( "%q", tostring(seconds_))) 350 error( "invalid duration " .. string_format( "%q", tostring(seconds_)))
351 end 351 end
352 -- receive data on a channel no-one ever sends anything, thus blocking for the specified duration 352 -- receive data on a channel no-one ever sends anything, thus blocking for the specified duration
353 return timer_gateway:receive( seconds_, "ac100de1-a696-4619-b2f0-a26de9d58ab8") 353 return timer_gateway:receive( seconds_, "ac100de1-a696-4619-b2f0-a26de9d58ab8")
354 end 354 end
355 355
356 356
357 if settings.with_timers ~= false then 357 if settings.with_timers ~= false then
358 358
359 -- 359 --
360 -- On first 'require "lanes"', a timer lane is spawned that will maintain 360 -- On first 'require "lanes"', a timer lane is spawned that will maintain
361 -- timer tables and sleep in between the timer events. All interaction with 361 -- timer tables and sleep in between the timer events. All interaction with
362 -- the timer lane happens via a 'timer_gateway' Linda, which is common to 362 -- the timer lane happens via a 'timer_gateway' Linda, which is common to
363 -- all that 'require "lanes"'. 363 -- all that 'require "lanes"'.
364 -- 364 --
365 -- Linda protocol to timer lane: 365 -- Linda protocol to timer lane:
366 -- 366 --
367 -- TGW_KEY: linda_h, key, [wakeup_at_secs], [repeat_secs] 367 -- TGW_KEY: linda_h, key, [wakeup_at_secs], [repeat_secs]
368 -- 368 --
369 local TGW_KEY= "(timer control)" -- the key does not matter, a 'weird' key may help debugging 369 local TGW_KEY= "(timer control)" -- the key does not matter, a 'weird' key may help debugging
370 local TGW_QUERY, TGW_REPLY = "(timer query)", "(timer reply)" 370 local TGW_QUERY, TGW_REPLY = "(timer query)", "(timer reply)"
371 local first_time_key= "first time" 371 local first_time_key= "first time"
372 372
373 local first_time = timer_gateway:get( first_time_key) == nil 373 local first_time = timer_gateway:get( first_time_key) == nil
374 timer_gateway:set( first_time_key, true) 374 timer_gateway:set( first_time_key, true)
375 375
376 -- 376 --
377 -- Timer lane; initialize only on the first 'require "lanes"' instance (which naturally 377 -- Timer lane; initialize only on the first 'require "lanes"' instance (which naturally
378 -- has 'table' always declared) 378 -- has 'table' always declared)
379 -- 379 --
380 if first_time then 380 if first_time then
381 381
382 local now_secs = core.now_secs 382 local now_secs = core.now_secs
383 assert( type( now_secs) == "function") 383 assert( type( now_secs) == "function")
384 ----- 384 -----
385 -- Snore loop (run as a lane on the background) 385 -- Snore loop (run as a lane on the background)
386 -- 386 --
387 -- High priority, to get trustworthy timings. 387 -- High priority, to get trustworthy timings.
388 -- 388 --
389 -- We let the timer lane be a "free running" thread; no handle to it 389 -- We let the timer lane be a "free running" thread; no handle to it
390 -- remains. 390 -- remains.
391 -- 391 --
392 local timer_body = function() 392 local timer_body = function()
393 set_debug_threadname( "LanesTimer") 393 set_debug_threadname( "LanesTimer")
394 -- 394 --
395 -- { [deep_linda_lightuserdata]= { [deep_linda_lightuserdata]=linda_h, 395 -- { [deep_linda_lightuserdata]= { [deep_linda_lightuserdata]=linda_h,
396 -- [key]= { wakeup_secs [,period_secs] } [, ...] }, 396 -- [key]= { wakeup_secs [,period_secs] } [, ...] },
397 -- } 397 -- }
398 -- 398 --
399 -- Collection of all running timers, indexed with linda's & key. 399 -- Collection of all running timers, indexed with linda's & key.
400 -- 400 --
401 -- Note that we need to use the deep lightuserdata identifiers, instead 401 -- Note that we need to use the deep lightuserdata identifiers, instead
402 -- of 'linda_h' themselves as table indices. Otherwise, we'd get multiple 402 -- of 'linda_h' themselves as table indices. Otherwise, we'd get multiple
403 -- entries for the same timer. 403 -- entries for the same timer.
404 -- 404 --
405 -- The 'hidden' reference to Linda proxy is used in 'check_timers()' but 405 -- The 'hidden' reference to Linda proxy is used in 'check_timers()' but
406 -- also important to keep the Linda alive, even if all outside world threw 406 -- also important to keep the Linda alive, even if all outside world threw
407 -- away pointers to it (which would ruin uniqueness of the deep pointer). 407 -- away pointers to it (which would ruin uniqueness of the deep pointer).
408 -- Now we're safe. 408 -- Now we're safe.
409 -- 409 --
410 local collection = {} 410 local collection = {}
411 local table_insert = assert( table.insert) 411 local table_insert = assert( table.insert)
412 412
413 local get_timers = function() 413 local get_timers = function()
414 local r = {} 414 local r = {}
415 for deep, t in pairs( collection) do 415 for deep, t in pairs( collection) do
416 -- WR( tostring( deep)) 416 -- WR( tostring( deep))
417 local l = t[deep] 417 local l = t[deep]
418 for key, timer_data in pairs( t) do 418 for key, timer_data in pairs( t) do
419 if key ~= deep then 419 if key ~= deep then
420 table_insert( r, {l, key, timer_data}) 420 table_insert( r, {l, key, timer_data})
421 end 421 end
422 end 422 end
423 end 423 end
424 return r 424 return r
425 end -- get_timers() 425 end -- get_timers()
426 426
427 -- 427 --
428 -- set_timer( linda_h, key [,wakeup_at_secs [,period_secs]] ) 428 -- set_timer( linda_h, key [,wakeup_at_secs [,period_secs]] )
429 -- 429 --
430 local set_timer = function( linda, key, wakeup_at, period) 430 local set_timer = function( linda, key, wakeup_at, period)
431 assert( wakeup_at == nil or wakeup_at > 0.0) 431 assert( wakeup_at == nil or wakeup_at > 0.0)
432 assert( period == nil or period > 0.0) 432 assert( period == nil or period > 0.0)
433 433
434 local linda_deep = linda:deep() 434 local linda_deep = linda:deep()
435 assert( linda_deep) 435 assert( linda_deep)
436 436
437 -- Find or make a lookup for this timer 437 -- Find or make a lookup for this timer
438 -- 438 --
439 local t1 = collection[linda_deep] 439 local t1 = collection[linda_deep]
440 if not t1 then 440 if not t1 then
441 t1 = { [linda_deep] = linda} -- proxy to use the Linda 441 t1 = { [linda_deep] = linda} -- proxy to use the Linda
442 collection[linda_deep] = t1 442 collection[linda_deep] = t1
443 end 443 end
444 444
445 if wakeup_at == nil then 445 if wakeup_at == nil then
446 -- Clear the timer 446 -- Clear the timer
447 -- 447 --
448 t1[key]= nil 448 t1[key]= nil
449 449
450 -- Remove empty tables from collection; speeds timer checks and 450 -- Remove empty tables from collection; speeds timer checks and
451 -- lets our 'safety reference' proxy be gc:ed as well. 451 -- lets our 'safety reference' proxy be gc:ed as well.
452 -- 452 --
453 local empty = true 453 local empty = true
454 for k, _ in pairs( t1) do 454 for k, _ in pairs( t1) do
455 if k ~= linda_deep then 455 if k ~= linda_deep then
456 empty = false 456 empty = false
457 break 457 break
458 end 458 end
459 end 459 end
460 if empty then 460 if empty then
461 collection[linda_deep] = nil 461 collection[linda_deep] = nil
462 end 462 end
463 463
464 -- Note: any unread timer value is left at 'linda[key]' intensionally; 464 -- Note: any unread timer value is left at 'linda[key]' intensionally;
465 -- clearing a timer just stops it. 465 -- clearing a timer just stops it.
466 else 466 else
467 -- New timer or changing the timings 467 -- New timer or changing the timings
468 -- 468 --
469 local t2 = t1[key] 469 local t2 = t1[key]
470 if not t2 then 470 if not t2 then
471 t2= {} 471 t2= {}
472 t1[key]= t2 472 t1[key]= t2
473 end 473 end
474 474
475 t2[1] = wakeup_at 475 t2[1] = wakeup_at
476 t2[2] = period -- can be 'nil' 476 t2[2] = period -- can be 'nil'
477 end 477 end
478 end -- set_timer() 478 end -- set_timer()
479 479
480 ----- 480 -----
481 -- [next_wakeup_at]= check_timers() 481 -- [next_wakeup_at]= check_timers()
482 -- Check timers, and wake up the ones expired (if any) 482 -- Check timers, and wake up the ones expired (if any)
483 -- Returns the closest upcoming (remaining) wakeup time (or 'nil' if none). 483 -- Returns the closest upcoming (remaining) wakeup time (or 'nil' if none).
484 local check_timers = function() 484 local check_timers = function()
485 local now = now_secs() 485 local now = now_secs()
486 local next_wakeup 486 local next_wakeup
487 487
488 for linda_deep,t1 in pairs(collection) do 488 for linda_deep,t1 in pairs(collection) do
489 for key,t2 in pairs(t1) do 489 for key,t2 in pairs(t1) do
490 -- 490 --
491 if key==linda_deep then 491 if key==linda_deep then
492 -- no 'continue' in Lua :/ 492 -- no 'continue' in Lua :/
493 else 493 else
494 -- 't2': { wakeup_at_secs [,period_secs] } 494 -- 't2': { wakeup_at_secs [,period_secs] }
495 -- 495 --
496 local wakeup_at= t2[1] 496 local wakeup_at= t2[1]
497 local period= t2[2] -- may be 'nil' 497 local period= t2[2] -- may be 'nil'
498 498
499 if wakeup_at <= now then 499 if wakeup_at <= now then
500 local linda= t1[linda_deep] 500 local linda= t1[linda_deep]
501 assert(linda) 501 assert(linda)
502 502
503 linda:set( key, now ) 503 linda:set( key, now )
504 504
505 -- 'pairs()' allows the values to be modified (and even 505 -- 'pairs()' allows the values to be modified (and even
506 -- removed) as far as keys are not touched 506 -- removed) as far as keys are not touched
507 507
508 if not period then 508 if not period then
509 -- one-time timer; gone 509 -- one-time timer; gone
510 -- 510 --
511 t1[key]= nil 511 t1[key]= nil
512 wakeup_at= nil -- no 'continue' in Lua :/ 512 wakeup_at= nil -- no 'continue' in Lua :/
513 else 513 else
514 -- repeating timer; find next wakeup (may jump multiple repeats) 514 -- repeating timer; find next wakeup (may jump multiple repeats)
515 -- 515 --
516 repeat 516 repeat
517 wakeup_at= wakeup_at+period 517 wakeup_at= wakeup_at+period
518 until wakeup_at > now 518 until wakeup_at > now
519 519
520 t2[1]= wakeup_at 520 t2[1]= wakeup_at
521 end 521 end
522 end 522 end
523 523
524 if wakeup_at and ((not next_wakeup) or (wakeup_at < next_wakeup)) then 524 if wakeup_at and ((not next_wakeup) or (wakeup_at < next_wakeup)) then
525 next_wakeup= wakeup_at 525 next_wakeup= wakeup_at
526 end 526 end
527 end 527 end
528 end -- t2 loop 528 end -- t2 loop
529 end -- t1 loop 529 end -- t1 loop
530 530
531 return next_wakeup -- may be 'nil' 531 return next_wakeup -- may be 'nil'
532 end -- check_timers() 532 end -- check_timers()
533 533
534 local timer_gateway_batched = timer_gateway.batched 534 local timer_gateway_batched = timer_gateway.batched
535 set_finalizer( function( err, stk) 535 set_finalizer( function( err, stk)
536 if err and type( err) ~= "userdata" then 536 if err and type( err) ~= "userdata" then
537 WR( "LanesTimer error: "..tostring(err)) 537 WR( "LanesTimer error: "..tostring(err))
538 --elseif type( err) == "userdata" then 538 --elseif type( err) == "userdata" then
539 -- WR( "LanesTimer after cancel" ) 539 -- WR( "LanesTimer after cancel" )
540 --else 540 --else
541 -- WR("LanesTimer finalized") 541 -- WR("LanesTimer finalized")
542 end 542 end
543 end) 543 end)
544 while true do 544 while true do
545 local next_wakeup = check_timers() 545 local next_wakeup = check_timers()
546 546
547 -- Sleep until next timer to wake up, or a set/clear command 547 -- Sleep until next timer to wake up, or a set/clear command
548 -- 548 --
549 local secs 549 local secs
550 if next_wakeup then 550 if next_wakeup then
551 secs = next_wakeup - now_secs() 551 secs = next_wakeup - now_secs()
552 if secs < 0 then secs = 0 end 552 if secs < 0 then secs = 0 end
553 end 553 end
554 local key, what = timer_gateway:receive( secs, TGW_KEY, TGW_QUERY) 554 local key, what = timer_gateway:receive( secs, TGW_KEY, TGW_QUERY)
555 555
556 if key == TGW_KEY then 556 if key == TGW_KEY then
557 assert( getmetatable( what) == "Linda") -- 'what' should be a linda on which the client sets a timer 557 assert( getmetatable( what) == "Linda") -- 'what' should be a linda on which the client sets a timer
558 local _, key, wakeup_at, period = timer_gateway:receive( 0, timer_gateway_batched, TGW_KEY, 3) 558 local _, key, wakeup_at, period = timer_gateway:receive( 0, timer_gateway_batched, TGW_KEY, 3)
559 assert( key) 559 assert( key)
560 set_timer( what, key, wakeup_at, period and period > 0 and period or nil) 560 set_timer( what, key, wakeup_at, period and period > 0 and period or nil)
561 elseif key == TGW_QUERY then 561 elseif key == TGW_QUERY then
562 if what == "get_timers" then 562 if what == "get_timers" then
563 timer_gateway:send( TGW_REPLY, get_timers()) 563 timer_gateway:send( TGW_REPLY, get_timers())
564 else 564 else
565 timer_gateway:send( TGW_REPLY, "unknown query " .. what) 565 timer_gateway:send( TGW_REPLY, "unknown query " .. what)
566 end 566 end
567 --elseif secs == nil then -- got no value while block-waiting? 567 --elseif secs == nil then -- got no value while block-waiting?
568 -- WR( "timer lane: no linda, aborted?") 568 -- WR( "timer lane: no linda, aborted?")
569 end 569 end
570 end 570 end
571 end -- timer_body() 571 end -- timer_body()
572 timer_lane = gen( "*", { package= {}, priority = max_prio}, timer_body)() -- "*" instead of "io,package" for LuaJIT compatibility... 572 timer_lane = gen( "*", { package= {}, priority = max_prio}, timer_body)() -- "*" instead of "io,package" for LuaJIT compatibility...
573 end -- first_time 573 end -- first_time
574 574
575 ----- 575 -----
576 -- = timer( linda_h, key_val, date_tbl|first_secs [,period_secs] ) 576 -- = timer( linda_h, key_val, date_tbl|first_secs [,period_secs] )
577 -- 577 --
578 -- PUBLIC LANES API 578 -- PUBLIC LANES API
579 timer = function( linda, key, a, period ) 579 timer = function( linda, key, a, period )
580 if getmetatable( linda) ~= "Linda" then 580 if getmetatable( linda) ~= "Linda" then
581 error "expecting a Linda" 581 error "expecting a Linda"
582 end 582 end
583 if a == 0.0 then 583 if a == 0.0 then
584 -- Caller expects to get current time stamp in Linda, on return 584 -- Caller expects to get current time stamp in Linda, on return
585 -- (like the timer had expired instantly); it would be good to set this 585 -- (like the timer had expired instantly); it would be good to set this
586 -- as late as possible (to give most current time) but also we want it 586 -- as late as possible (to give most current time) but also we want it
587 -- to precede any possible timers that might start striking. 587 -- to precede any possible timers that might start striking.
588 -- 588 --
589 linda:set( key, core.now_secs()) 589 linda:set( key, core.now_secs())
590 590
591 if not period or period==0.0 then 591 if not period or period==0.0 then
592 timer_gateway:send( TGW_KEY, linda, key, nil, nil ) -- clear the timer 592 timer_gateway:send( TGW_KEY, linda, key, nil, nil ) -- clear the timer
593 return -- nothing more to do 593 return -- nothing more to do
594 end 594 end
595 a= period 595 a= period
596 end 596 end
597 597
598 local wakeup_at= type(a)=="table" and core.wakeup_conv(a) -- given point of time 598 local wakeup_at= type(a)=="table" and core.wakeup_conv(a) -- given point of time
599 or (a and core.now_secs()+a or nil) 599 or (a and core.now_secs()+a or nil)
600 -- queue to timer 600 -- queue to timer
601 -- 601 --
602 timer_gateway:send( TGW_KEY, linda, key, wakeup_at, period ) 602 timer_gateway:send( TGW_KEY, linda, key, wakeup_at, period )
603 end 603 end
604 604
605 ----- 605 -----
606 -- {[{linda, slot, when, period}[,...]]} = timers() 606 -- {[{linda, slot, when, period}[,...]]} = timers()
607 -- 607 --
608 -- PUBLIC LANES API 608 -- PUBLIC LANES API
609 timers = function() 609 timers = function()
610 timer_gateway:send( TGW_QUERY, "get_timers") 610 timer_gateway:send( TGW_QUERY, "get_timers")
611 local _, r = timer_gateway:receive( TGW_REPLY) 611 local _, r = timer_gateway:receive( TGW_REPLY)
612 return r 612 return r
613 end 613 end
614 614
615 end -- settings.with_timers 615 end -- settings.with_timers
616 616
617 -- avoid pulling the whole core module as upvalue when cancel_error is enough 617 -- avoid pulling the whole core module as upvalue when cancel_error is enough
618 local cancel_error = assert( core.cancel_error) 618 local cancel_error = assert( core.cancel_error)
619 619
620 ---=== Lock & atomic generators ===--- 620 ---=== Lock & atomic generators ===---
621 621
622 -- These functions are just surface sugar, but make solutions easier to read. 622 -- These functions are just surface sugar, but make solutions easier to read.
623 -- Not many applications should even need explicit locks or atomic counters. 623 -- Not many applications should even need explicit locks or atomic counters.
624 624
625 -- 625 --
626 -- [true [, ...]= trues(uint) 626 -- [true [, ...]= trues(uint)
627 -- 627 --
628 local function trues( n) 628 local function trues( n)
629 if n > 0 then 629 if n > 0 then
630 return true, trues( n - 1) 630 return true, trues( n - 1)
631 end 631 end
632 end 632 end
633 633
634 -- 634 --
635 -- lock_f = lanes.genlock( linda_h, key [,N_uint=1] ) 635 -- lock_f = lanes.genlock( linda_h, key [,N_uint=1] )
636 -- 636 --
637 -- = lock_f( +M ) -- acquire M 637 -- = lock_f( +M ) -- acquire M
638 -- ...locked... 638 -- ...locked...
639 -- = lock_f( -M ) -- release M 639 -- = lock_f( -M ) -- release M
640 -- 640 --
641 -- Returns an access function that allows 'N' simultaneous entries between 641 -- Returns an access function that allows 'N' simultaneous entries between
642 -- acquire (+M) and release (-M). For binary locks, use M==1. 642 -- acquire (+M) and release (-M). For binary locks, use M==1.
643 -- 643 --
644 -- PUBLIC LANES API 644 -- PUBLIC LANES API
645 local genlock = function( linda, key, N) 645 local genlock = function( linda, key, N)
646 -- clear existing data and set the limit 646 -- clear existing data and set the limit
647 N = N or 1 647 N = N or 1
648 if linda:set( key) == cancel_error or linda:limit( key, N) == cancel_error then 648 if linda:set( key) == cancel_error or linda:limit( key, N) == cancel_error then
649 return cancel_error 649 return cancel_error
650 end 650 end
651 651
652 -- use an optimized version for case N == 1 652 -- use an optimized version for case N == 1
653 return (N == 1) and 653 return (N == 1) and
654 function( M, mode_) 654 function( M, mode_)
655 local timeout = (mode_ == "try") and 0 or nil 655 local timeout = (mode_ == "try") and 0 or nil
656 if M > 0 then 656 if M > 0 then
657 -- 'nil' timeout allows 'key' to be numeric 657 -- 'nil' timeout allows 'key' to be numeric
658 return linda:send( timeout, key, true) -- suspends until been able to push them 658 return linda:send( timeout, key, true) -- suspends until been able to push them
659 else 659 else
660 local k = linda:receive( nil, key) 660 local k = linda:receive( nil, key)
661 -- propagate cancel_error if we got it, else return true or false 661 -- propagate cancel_error if we got it, else return true or false
662 return k and ((k ~= cancel_error) and true or k) or false 662 return k and ((k ~= cancel_error) and true or k) or false
663 end 663 end
664 end 664 end
665 or 665 or
666 function( M, mode_) 666 function( M, mode_)
667 local timeout = (mode_ == "try") and 0 or nil 667 local timeout = (mode_ == "try") and 0 or nil
668 if M > 0 then 668 if M > 0 then
669 -- 'nil' timeout allows 'key' to be numeric 669 -- 'nil' timeout allows 'key' to be numeric
670 return linda:send( timeout, key, trues(M)) -- suspends until been able to push them 670 return linda:send( timeout, key, trues(M)) -- suspends until been able to push them
671 else 671 else
672 local k = linda:receive( nil, linda.batched, key, -M) 672 local k = linda:receive( nil, linda.batched, key, -M)
673 -- propagate cancel_error if we got it, else return true or false 673 -- propagate cancel_error if we got it, else return true or false
674 return k and ((k ~= cancel_error) and true or k) or false 674 return k and ((k ~= cancel_error) and true or k) or false
675 end 675 end
676 end 676 end
677 end 677 end
678 678
679 679
680 -- 680 --
681 -- atomic_f = lanes.genatomic( linda_h, key [,initial_num=0.0]) 681 -- atomic_f = lanes.genatomic( linda_h, key [,initial_num=0.0])
682 -- 682 --
683 -- int|cancel_error = atomic_f( [diff_num = 1.0]) 683 -- int|cancel_error = atomic_f( [diff_num = 1.0])
684 -- 684 --
685 -- Returns an access function that allows atomic increment/decrement of the 685 -- Returns an access function that allows atomic increment/decrement of the
686 -- number in 'key'. 686 -- number in 'key'.
687 -- 687 --
688 -- PUBLIC LANES API 688 -- PUBLIC LANES API
689 local genatomic = function( linda, key, initial_val) 689 local genatomic = function( linda, key, initial_val)
690 -- clears existing data (also queue). the slot may contain the stored value, and an additional boolean value 690 -- clears existing data (also queue). the slot may contain the stored value, and an additional boolean value
691 if linda:limit( key, 2) == cancel_error or linda:set( key, initial_val or 0.0) == cancel_error then 691 if linda:limit( key, 2) == cancel_error or linda:set( key, initial_val or 0.0) == cancel_error then
692 return cancel_error 692 return cancel_error
693 end 693 end
694 694
695 return function( diff) 695 return function( diff)
696 -- 'nil' allows 'key' to be numeric 696 -- 'nil' allows 'key' to be numeric
697 -- suspends until our 'true' is in 697 -- suspends until our 'true' is in
698 if linda:send( nil, key, true) == cancel_error then 698 if linda:send( nil, key, true) == cancel_error then
699 return cancel_error 699 return cancel_error
700 end 700 end
701 local val = linda:get( key) 701 local val = linda:get( key)
702 if val ~= cancel_error then 702 if val ~= cancel_error then
703 val = val + (diff or 1.0) 703 val = val + (diff or 1.0)
704 -- set() releases the lock by emptying queue 704 -- set() releases the lock by emptying queue
705 if linda:set( key, val) == cancel_error then 705 if linda:set( key, val) == cancel_error then
706 val = cancel_error 706 val = cancel_error
707 end 707 end
708 end 708 end
709 return val 709 return val
710 end 710 end
711 end 711 end
712 712
713 -- activate full interface 713 -- activate full interface
714 lanes.require = core.require 714 lanes.require = core.require
715 lanes.register = core.register 715 lanes.register = core.register
716 lanes.gen = gen 716 lanes.gen = gen
717 lanes.linda = core.linda 717 lanes.linda = core.linda
718 lanes.cancel_error = core.cancel_error 718 lanes.cancel_error = core.cancel_error
719 lanes.nameof = core.nameof 719 lanes.nameof = core.nameof
720 lanes.set_singlethreaded = core.set_singlethreaded 720 lanes.set_singlethreaded = core.set_singlethreaded
721 lanes.threads = core.threads or function() error "lane tracking is not available" end -- core.threads isn't registered if settings.track_lanes is false 721 lanes.threads = core.threads or function() error "lane tracking is not available" end -- core.threads isn't registered if settings.track_lanes is false
722 lanes.set_thread_priority = core.set_thread_priority 722 lanes.set_thread_priority = core.set_thread_priority
723 lanes.set_thread_affinity = core.set_thread_affinity 723 lanes.set_thread_affinity = core.set_thread_affinity
724 lanes.timer = timer 724 lanes.timer = timer
725 lanes.timer_lane = timer_lane 725 lanes.timer_lane = timer_lane
726 lanes.timers = timers 726 lanes.timers = timers
727 lanes.sleep = sleep 727 lanes.sleep = sleep
728 lanes.genlock = genlock 728 lanes.genlock = genlock
729 lanes.now_secs = core.now_secs 729 lanes.now_secs = core.now_secs
730 lanes.genatomic = genatomic 730 lanes.genatomic = genatomic
731 lanes.configure = nil -- no need to call configure() ever again 731 lanes.configure = nil -- no need to call configure() ever again
732 return lanes 732 return lanes
733end -- lanes.configure 733end -- lanes.configure
734 734
735lanesMeta.__index = function( t, k) 735lanesMeta.__index = function( t, k)
736 -- This is called when some functionality is accessed without calling configure() 736 -- This is called when some functionality is accessed without calling configure()
737 lanes.configure() -- initialize with default settings 737 lanes.configure() -- initialize with default settings
738 -- Access the required key 738 -- Access the required key
739 return lanes[k] 739 return lanes[k]
740end 740end
741 741
742-- no need to force calling configure() manually excepted the first time (other times will reuse the internally stored settings of the first call) 742-- no need to force calling configure() manually excepted the first time (other times will reuse the internally stored settings of the first call)
743if core.settings then 743if core.settings then
744 return lanes.configure() 744 return lanes.configure()
745else 745else
746 return lanes 746 return lanes
747end 747end
748 748
749--the end 749--the end
diff --git a/src/lanes_private.h b/src/lanes_private.h
index 1a15969..7da3286 100644
--- a/src/lanes_private.h
+++ b/src/lanes_private.h
@@ -9,65 +9,65 @@
9// 9//
10struct s_Lane 10struct s_Lane
11{ 11{
12 THREAD_T thread; 12 THREAD_T thread;
13 // 13 //
14 // M: sub-thread OS thread 14 // M: sub-thread OS thread
15 // S: not used 15 // S: not used
16 16
17 char const* debug_name; 17 char const* debug_name;
18 18
19 lua_State* L; 19 lua_State* L;
20 Universe* U; 20 Universe* U;
21 // 21 //
22 // M: prepares the state, and reads results 22 // M: prepares the state, and reads results
23 // S: while S is running, M must keep out of modifying the state 23 // S: while S is running, M must keep out of modifying the state
24 24
25 volatile enum e_status status; 25 volatile enum e_status status;
26 // 26 //
27 // M: sets to PENDING (before launching) 27 // M: sets to PENDING (before launching)
28 // S: updates -> RUNNING/WAITING -> DONE/ERROR_ST/CANCELLED 28 // S: updates -> RUNNING/WAITING -> DONE/ERROR_ST/CANCELLED
29 29
30 SIGNAL_T* volatile waiting_on; 30 SIGNAL_T* volatile waiting_on;
31 // 31 //
32 // When status is WAITING, points on the linda's signal the thread waits on, else NULL 32 // When status is WAITING, points on the linda's signal the thread waits on, else NULL
33 33
34 volatile enum e_cancel_request cancel_request; 34 volatile enum e_cancel_request cancel_request;
35 // 35 //
36 // M: sets to FALSE, flags TRUE for cancel request 36 // M: sets to FALSE, flags TRUE for cancel request
37 // S: reads to see if cancel is requested 37 // S: reads to see if cancel is requested
38 38
39#if THREADWAIT_METHOD == THREADWAIT_CONDVAR 39#if THREADWAIT_METHOD == THREADWAIT_CONDVAR
40 SIGNAL_T done_signal; 40 SIGNAL_T done_signal;
41 // 41 //
42 // M: Waited upon at lane ending (if Posix with no PTHREAD_TIMEDJOIN) 42 // M: Waited upon at lane ending (if Posix with no PTHREAD_TIMEDJOIN)
43 // S: sets the signal once cancellation is noticed (avoids a kill) 43 // S: sets the signal once cancellation is noticed (avoids a kill)
44 44
45 MUTEX_T done_lock; 45 MUTEX_T done_lock;
46 // 46 //
47 // Lock required by 'done_signal' condition variable, protecting 47 // Lock required by 'done_signal' condition variable, protecting
48 // lane status changes to DONE/ERROR_ST/CANCELLED. 48 // lane status changes to DONE/ERROR_ST/CANCELLED.
49#endif // THREADWAIT_METHOD == THREADWAIT_CONDVAR 49#endif // THREADWAIT_METHOD == THREADWAIT_CONDVAR
50 50
51 volatile enum 51 volatile enum
52 { 52 {
53 NORMAL, // normal master side state 53 NORMAL, // normal master side state
54 KILLED // issued an OS kill 54 KILLED // issued an OS kill
55 } mstatus; 55 } mstatus;
56 // 56 //
57 // M: sets to NORMAL, if issued a kill changes to KILLED 57 // M: sets to NORMAL, if issued a kill changes to KILLED
58 // S: not used 58 // S: not used
59 59
60 struct s_Lane* volatile selfdestruct_next; 60 struct s_Lane* volatile selfdestruct_next;
61 // 61 //
62 // M: sets to non-NULL if facing lane handle '__gc' cycle but the lane 62 // M: sets to non-NULL if facing lane handle '__gc' cycle but the lane
63 // is still running 63 // is still running
64 // S: cleans up after itself if non-NULL at lane exit 64 // S: cleans up after itself if non-NULL at lane exit
65 65
66#if HAVE_LANE_TRACKING 66#if HAVE_LANE_TRACKING
67 struct s_Lane* volatile tracking_next; 67 struct s_Lane* volatile tracking_next;
68#endif // HAVE_LANE_TRACKING 68#endif // HAVE_LANE_TRACKING
69 // 69 //
70 // For tracking only 70 // For tracking only
71}; 71};
72typedef struct s_Lane Lane; 72typedef struct s_Lane Lane;
73 73
@@ -79,14 +79,14 @@ typedef struct s_Lane Lane;
79 79
80static inline Lane* get_lane_from_registry( lua_State* L) 80static inline Lane* get_lane_from_registry( lua_State* L)
81{ 81{
82 Lane* s; 82 Lane* s;
83 STACK_GROW( L, 1); 83 STACK_GROW( L, 1);
84 STACK_CHECK( L, 0); 84 STACK_CHECK( L, 0);
85 REGISTRY_GET( L, CANCEL_TEST_KEY); 85 REGISTRY_GET( L, CANCEL_TEST_KEY);
86 s = lua_touserdata( L, -1); // lightuserdata (true 's_lane' pointer) / nil 86 s = lua_touserdata( L, -1); // lightuserdata (true 's_lane' pointer) / nil
87 lua_pop( L, 1); 87 lua_pop( L, 1);
88 STACK_END( L, 0); 88 STACK_END( L, 0);
89 return s; 89 return s;
90} 90}
91 91
92int push_thread_status( lua_State* L, Lane* s); 92int push_thread_status( lua_State* L, Lane* s);
diff --git a/src/linda.c b/src/linda.c
index d3ed8a0..a9c9710 100644
--- a/src/linda.c
+++ b/src/linda.c
@@ -48,13 +48,13 @@ THE SOFTWARE.
48*/ 48*/
49struct s_Linda 49struct s_Linda
50{ 50{
51 DeepPrelude prelude; // Deep userdata MUST start with this header 51 DeepPrelude prelude; // Deep userdata MUST start with this header
52 SIGNAL_T read_happened; 52 SIGNAL_T read_happened;
53 SIGNAL_T write_happened; 53 SIGNAL_T write_happened;
54 Universe* U; // the universe this linda belongs to 54 Universe* U; // the universe this linda belongs to
55 ptrdiff_t group; // a group to control keeper allocation between lindas 55 ptrdiff_t group; // a group to control keeper allocation between lindas
56 enum e_cancel_request simulate_cancel; 56 enum e_cancel_request simulate_cancel;
57 char name[1]; 57 char name[1];
58}; 58};
59#define LINDA_KEEPER_HASHSEED( linda) (linda->group ? linda->group : (ptrdiff_t)linda) 59#define LINDA_KEEPER_HASHSEED( linda) (linda->group ? linda->group : (ptrdiff_t)linda)
60 60
@@ -62,51 +62,51 @@ static void* linda_id( lua_State*, DeepOp);
62 62
63static inline struct s_Linda* lua_toLinda( lua_State* L, int idx_) 63static inline struct s_Linda* lua_toLinda( lua_State* L, int idx_)
64{ 64{
65 struct s_Linda* linda = (struct s_Linda*) luaG_todeep( L, linda_id, idx_); 65 struct s_Linda* linda = (struct s_Linda*) luaG_todeep( L, linda_id, idx_);
66 luaL_argcheck( L, linda != NULL, idx_, "expecting a linda object"); 66 luaL_argcheck( L, linda != NULL, idx_, "expecting a linda object");
67 return linda; 67 return linda;
68} 68}
69 69
70static void check_key_types( lua_State* L, int start_, int end_) 70static void check_key_types( lua_State* L, int start_, int end_)
71{ 71{
72 int i; 72 int i;
73 for( i = start_; i <= end_; ++ i) 73 for( i = start_; i <= end_; ++ i)
74 { 74 {
75 int t = lua_type( L, i); 75 int t = lua_type( L, i);
76 if( t == LUA_TBOOLEAN || t == LUA_TNUMBER || t == LUA_TSTRING || t == LUA_TLIGHTUSERDATA) 76 if( t == LUA_TBOOLEAN || t == LUA_TNUMBER || t == LUA_TSTRING || t == LUA_TLIGHTUSERDATA)
77 { 77 {
78 continue; 78 continue;
79 } 79 }
80 (void) luaL_error( L, "argument #%d: invalid key type (not a boolean, string, number or light userdata)", i); 80 (void) luaL_error( L, "argument #%d: invalid key type (not a boolean, string, number or light userdata)", i);
81 } 81 }
82} 82}
83 83
84LUAG_FUNC( linda_protected_call) 84LUAG_FUNC( linda_protected_call)
85{ 85{
86 int rc = LUA_OK; 86 int rc = LUA_OK;
87 struct s_Linda* linda = lua_toLinda( L, 1); 87 struct s_Linda* linda = lua_toLinda( L, 1);
88 88
89 // acquire the keeper 89 // acquire the keeper
90 Keeper* K = keeper_acquire( linda->U->keepers, LINDA_KEEPER_HASHSEED(linda)); 90 Keeper* K = keeper_acquire( linda->U->keepers, LINDA_KEEPER_HASHSEED(linda));
91 lua_State* KL = K ? K->L : NULL; // need to do this for 'STACK_CHECK' 91 lua_State* KL = K ? K->L : NULL; // need to do this for 'STACK_CHECK'
92 if( KL == NULL) return 0; 92 if( KL == NULL) return 0;
93 93
94 // retrieve the actual function to be called and move it before the arguments 94 // retrieve the actual function to be called and move it before the arguments
95 lua_pushvalue( L, lua_upvalueindex( 1)); 95 lua_pushvalue( L, lua_upvalueindex( 1));
96 lua_insert( L, 1); 96 lua_insert( L, 1);
97 // do a protected call 97 // do a protected call
98 rc = lua_pcall( L, lua_gettop( L) - 1, LUA_MULTRET, 0); 98 rc = lua_pcall( L, lua_gettop( L) - 1, LUA_MULTRET, 0);
99 99
100 // release the keeper 100 // release the keeper
101 keeper_release( K); 101 keeper_release( K);
102 102
103 // if there was an error, forward it 103 // if there was an error, forward it
104 if( rc != LUA_OK) 104 if( rc != LUA_OK)
105 { 105 {
106 return lua_error( L); 106 return lua_error( L);
107 } 107 }
108 // return whatever the actual operation provided 108 // return whatever the actual operation provided
109 return lua_gettop( L); 109 return lua_gettop( L);
110} 110}
111 111
112/* 112/*
@@ -120,142 +120,142 @@ LUAG_FUNC( linda_protected_call)
120*/ 120*/
121LUAG_FUNC( linda_send) 121LUAG_FUNC( linda_send)
122{ 122{
123 struct s_Linda* linda = lua_toLinda( L, 1); 123 struct s_Linda* linda = lua_toLinda( L, 1);
124 bool_t ret = FALSE; 124 bool_t ret = FALSE;
125 enum e_cancel_request cancel = CANCEL_NONE; 125 enum e_cancel_request cancel = CANCEL_NONE;
126 int pushed; 126 int pushed;
127 time_d timeout = -1.0; 127 time_d timeout = -1.0;
128 uint_t key_i = 2; // index of first key, if timeout not there 128 uint_t key_i = 2; // index of first key, if timeout not there
129 bool_t as_nil_sentinel; // if not NULL, send() will silently send a single nil if nothing is provided 129 bool_t as_nil_sentinel; // if not NULL, send() will silently send a single nil if nothing is provided
130 130
131 if( lua_type( L, 2) == LUA_TNUMBER) // we don't want to use lua_isnumber() because of autocoercion 131 if( lua_type( L, 2) == LUA_TNUMBER) // we don't want to use lua_isnumber() because of autocoercion
132 { 132 {
133 timeout = SIGNAL_TIMEOUT_PREPARE( lua_tonumber( L, 2)); 133 timeout = SIGNAL_TIMEOUT_PREPARE( lua_tonumber( L, 2));
134 ++ key_i; 134 ++ key_i;
135 } 135 }
136 else if( lua_isnil( L, 2)) // alternate explicit "no timeout" by passing nil before the key 136 else if( lua_isnil( L, 2)) // alternate explicit "no timeout" by passing nil before the key
137 { 137 {
138 ++ key_i; 138 ++ key_i;
139 } 139 }
140 140
141 as_nil_sentinel = equal_unique_key( L, key_i, NIL_SENTINEL); 141 as_nil_sentinel = equal_unique_key( L, key_i, NIL_SENTINEL);
142 if( as_nil_sentinel) 142 if( as_nil_sentinel)
143 { 143 {
144 // the real key to send data to is after the NIL_SENTINEL marker 144 // the real key to send data to is after the NIL_SENTINEL marker
145 ++ key_i; 145 ++ key_i;
146 } 146 }
147 147
148 // make sure the key is of a valid type 148 // make sure the key is of a valid type
149 check_key_types( L, key_i, key_i); 149 check_key_types( L, key_i, key_i);
150 150
151 STACK_GROW( L, 1); 151 STACK_GROW( L, 1);
152 152
153 // make sure there is something to send 153 // make sure there is something to send
154 if( (uint_t)lua_gettop( L) == key_i) 154 if( (uint_t)lua_gettop( L) == key_i)
155 { 155 {
156 if( as_nil_sentinel) 156 if( as_nil_sentinel)
157 { 157 {
158 // send a single nil if nothing is provided 158 // send a single nil if nothing is provided
159 push_unique_key( L, NIL_SENTINEL); 159 push_unique_key( L, NIL_SENTINEL);
160 } 160 }
161 else 161 else
162 { 162 {
163 return luaL_error( L, "no data to send"); 163 return luaL_error( L, "no data to send");
164 } 164 }
165 } 165 }
166 166
167 // convert nils to some special non-nil sentinel in sent values 167 // convert nils to some special non-nil sentinel in sent values
168 keeper_toggle_nil_sentinels( L, key_i + 1, eLM_ToKeeper); 168 keeper_toggle_nil_sentinels( L, key_i + 1, eLM_ToKeeper);
169 169
170 { 170 {
171 bool_t try_again = TRUE; 171 bool_t try_again = TRUE;
172 Lane* const s = get_lane_from_registry( L); 172 Lane* const s = get_lane_from_registry( L);
173 Keeper* K = which_keeper( linda->U->keepers, LINDA_KEEPER_HASHSEED( linda)); 173 Keeper* K = which_keeper( linda->U->keepers, LINDA_KEEPER_HASHSEED( linda));
174 lua_State* KL = K ? K->L : NULL; // need to do this for 'STACK_CHECK' 174 lua_State* KL = K ? K->L : NULL; // need to do this for 'STACK_CHECK'
175 if( KL == NULL) return 0; 175 if( KL == NULL) return 0;
176 STACK_CHECK( KL, 0); 176 STACK_CHECK( KL, 0);
177 for( ;;) 177 for( ;;)
178 { 178 {
179 if( s != NULL) 179 if( s != NULL)
180 { 180 {
181 cancel = s->cancel_request; 181 cancel = s->cancel_request;
182 } 182 }
183 cancel = (cancel != CANCEL_NONE) ? cancel : linda->simulate_cancel; 183 cancel = (cancel != CANCEL_NONE) ? cancel : linda->simulate_cancel;
184 // if user wants to cancel, or looped because of a timeout, the call returns without sending anything 184 // if user wants to cancel, or looped because of a timeout, the call returns without sending anything
185 if( !try_again || cancel != CANCEL_NONE) 185 if( !try_again || cancel != CANCEL_NONE)
186 { 186 {
187 pushed = 0; 187 pushed = 0;
188 break; 188 break;
189 } 189 }
190 190
191 STACK_MID( KL, 0); 191 STACK_MID( KL, 0);
192 pushed = keeper_call( linda->U, KL, KEEPER_API( send), L, linda, key_i); 192 pushed = keeper_call( linda->U, KL, KEEPER_API( send), L, linda, key_i);
193 if( pushed < 0) 193 if( pushed < 0)
194 { 194 {
195 break; 195 break;
196 } 196 }
197 ASSERT_L( pushed == 1); 197 ASSERT_L( pushed == 1);
198 198
199 ret = lua_toboolean( L, -1); 199 ret = lua_toboolean( L, -1);
200 lua_pop( L, 1); 200 lua_pop( L, 1);
201 201
202 if( ret) 202 if( ret)
203 { 203 {
204 // Wake up ALL waiting threads 204 // Wake up ALL waiting threads
205 SIGNAL_ALL( &linda->write_happened); 205 SIGNAL_ALL( &linda->write_happened);
206 break; 206 break;
207 } 207 }
208 208
209 // instant timout to bypass the wait syscall 209 // instant timout to bypass the wait syscall
210 if( timeout == 0.0) 210 if( timeout == 0.0)
211 { 211 {
212 break; /* no wait; instant timeout */ 212 break; /* no wait; instant timeout */
213 } 213 }
214 214
215 // storage limit hit, wait until timeout or signalled that we should try again 215 // storage limit hit, wait until timeout or signalled that we should try again
216 { 216 {
217 enum e_status prev_status = ERROR_ST; // prevent 'might be used uninitialized' warnings 217 enum e_status prev_status = ERROR_ST; // prevent 'might be used uninitialized' warnings
218 if( s != NULL) 218 if( s != NULL)
219 { 219 {
220 // change status of lane to "waiting" 220 // change status of lane to "waiting"
221 prev_status = s->status; // RUNNING, most likely 221 prev_status = s->status; // RUNNING, most likely
222 ASSERT_L( prev_status == RUNNING); // but check, just in case 222 ASSERT_L( prev_status == RUNNING); // but check, just in case
223 s->status = WAITING; 223 s->status = WAITING;
224 ASSERT_L( s->waiting_on == NULL); 224 ASSERT_L( s->waiting_on == NULL);
225 s->waiting_on = &linda->read_happened; 225 s->waiting_on = &linda->read_happened;
226 } 226 }
227 // could not send because no room: wait until some data was read before trying again, or until timeout is reached 227 // could not send because no room: wait until some data was read before trying again, or until timeout is reached
228 try_again = SIGNAL_WAIT( &linda->read_happened, &K->keeper_cs, timeout); 228 try_again = SIGNAL_WAIT( &linda->read_happened, &K->keeper_cs, timeout);
229 if( s != NULL) 229 if( s != NULL)
230 { 230 {
231 s->waiting_on = NULL; 231 s->waiting_on = NULL;
232 s->status = prev_status; 232 s->status = prev_status;
233 } 233 }
234 } 234 }
235 } 235 }
236 STACK_END( KL, 0); 236 STACK_END( KL, 0);
237 } 237 }
238 238
239 if( pushed < 0) 239 if( pushed < 0)
240 { 240 {
241 return luaL_error( L, "tried to copy unsupported types"); 241 return luaL_error( L, "tried to copy unsupported types");
242 } 242 }
243 243
244 switch( cancel) 244 switch( cancel)
245 { 245 {
246 case CANCEL_SOFT: 246 case CANCEL_SOFT:
247 // if user wants to soft-cancel, the call returns lanes.cancel_error 247 // if user wants to soft-cancel, the call returns lanes.cancel_error
248 push_unique_key( L, CANCEL_ERROR); 248 push_unique_key( L, CANCEL_ERROR);
249 return 1; 249 return 1;
250 250
251 case CANCEL_HARD: 251 case CANCEL_HARD:
252 // raise an error interrupting execution only in case of hard cancel 252 // raise an error interrupting execution only in case of hard cancel
253 return cancel_error( L); // raises an error and doesn't return 253 return cancel_error( L); // raises an error and doesn't return
254 254
255 default: 255 default:
256 lua_pushboolean( L, ret); // true (success) or false (timeout) 256 lua_pushboolean( L, ret); // true (success) or false (timeout)
257 return 1; 257 return 1;
258 } 258 }
259} 259}
260 260
261 261
@@ -273,143 +273,143 @@ LUAG_FUNC( linda_send)
273#define BATCH_SENTINEL "270e6c9d-280f-4983-8fee-a7ecdda01475" 273#define BATCH_SENTINEL "270e6c9d-280f-4983-8fee-a7ecdda01475"
274LUAG_FUNC( linda_receive) 274LUAG_FUNC( linda_receive)
275{ 275{
276 struct s_Linda* linda = lua_toLinda( L, 1); 276 struct s_Linda* linda = lua_toLinda( L, 1);
277 int pushed, expected_pushed_min, expected_pushed_max; 277 int pushed, expected_pushed_min, expected_pushed_max;
278 enum e_cancel_request cancel = CANCEL_NONE; 278 enum e_cancel_request cancel = CANCEL_NONE;
279 keeper_api_t keeper_receive; 279 keeper_api_t keeper_receive;
280 280
281 time_d timeout = -1.0; 281 time_d timeout = -1.0;
282 uint_t key_i = 2; 282 uint_t key_i = 2;
283 283
284 if( lua_type( L, 2) == LUA_TNUMBER) // we don't want to use lua_isnumber() because of autocoercion 284 if( lua_type( L, 2) == LUA_TNUMBER) // we don't want to use lua_isnumber() because of autocoercion
285 { 285 {
286 timeout = SIGNAL_TIMEOUT_PREPARE( lua_tonumber( L, 2)); 286 timeout = SIGNAL_TIMEOUT_PREPARE( lua_tonumber( L, 2));
287 ++ key_i; 287 ++ key_i;
288 } 288 }
289 else if( lua_isnil( L, 2)) // alternate explicit "no timeout" by passing nil before the key 289 else if( lua_isnil( L, 2)) // alternate explicit "no timeout" by passing nil before the key
290 { 290 {
291 ++ key_i; 291 ++ key_i;
292 } 292 }
293 293
294 // are we in batched mode? 294 // are we in batched mode?
295 { 295 {
296 int is_batched; 296 int is_batched;
297 lua_pushliteral( L, BATCH_SENTINEL); 297 lua_pushliteral( L, BATCH_SENTINEL);
298 is_batched = lua501_equal( L, key_i, -1); 298 is_batched = lua501_equal( L, key_i, -1);
299 lua_pop( L, 1); 299 lua_pop( L, 1);
300 if( is_batched) 300 if( is_batched)
301 { 301 {
302 // no need to pass linda.batched in the keeper state 302 // no need to pass linda.batched in the keeper state
303 ++ key_i; 303 ++ key_i;
304 // make sure the keys are of a valid type 304 // make sure the keys are of a valid type
305 check_key_types( L, key_i, key_i); 305 check_key_types( L, key_i, key_i);
306 // receive multiple values from a single slot 306 // receive multiple values from a single slot
307 keeper_receive = KEEPER_API( receive_batched); 307 keeper_receive = KEEPER_API( receive_batched);
308 // we expect a user-defined amount of return value 308 // we expect a user-defined amount of return value
309 expected_pushed_min = (int)luaL_checkinteger( L, key_i + 1); 309 expected_pushed_min = (int)luaL_checkinteger( L, key_i + 1);
310 expected_pushed_max = (int)luaL_optinteger( L, key_i + 2, expected_pushed_min); 310 expected_pushed_max = (int)luaL_optinteger( L, key_i + 2, expected_pushed_min);
311 // don't forget to count the key in addition to the values 311 // don't forget to count the key in addition to the values
312 ++ expected_pushed_min; 312 ++ expected_pushed_min;
313 ++ expected_pushed_max; 313 ++ expected_pushed_max;
314 if( expected_pushed_min > expected_pushed_max) 314 if( expected_pushed_min > expected_pushed_max)
315 { 315 {
316 return luaL_error( L, "batched min/max error"); 316 return luaL_error( L, "batched min/max error");
317 } 317 }
318 } 318 }
319 else 319 else
320 { 320 {
321 // make sure the keys are of a valid type 321 // make sure the keys are of a valid type
322 check_key_types( L, key_i, lua_gettop( L)); 322 check_key_types( L, key_i, lua_gettop( L));
323 // receive a single value, checking multiple slots 323 // receive a single value, checking multiple slots
324 keeper_receive = KEEPER_API( receive); 324 keeper_receive = KEEPER_API( receive);
325 // we expect a single (value, key) pair of returned values 325 // we expect a single (value, key) pair of returned values
326 expected_pushed_min = expected_pushed_max = 2; 326 expected_pushed_min = expected_pushed_max = 2;
327 } 327 }
328 } 328 }
329 329
330 { 330 {
331 bool_t try_again = TRUE; 331 bool_t try_again = TRUE;
332 Lane* const s = get_lane_from_registry( L); 332 Lane* const s = get_lane_from_registry( L);
333 Keeper* K = which_keeper( linda->U->keepers, LINDA_KEEPER_HASHSEED( linda)); 333 Keeper* K = which_keeper( linda->U->keepers, LINDA_KEEPER_HASHSEED( linda));
334 if( K == NULL) return 0; 334 if( K == NULL) return 0;
335 for( ;;) 335 for( ;;)
336 { 336 {
337 if( s != NULL) 337 if( s != NULL)
338 { 338 {
339 cancel = s->cancel_request; 339 cancel = s->cancel_request;
340 } 340 }
341 cancel = (cancel != CANCEL_NONE) ? cancel : linda->simulate_cancel; 341 cancel = (cancel != CANCEL_NONE) ? cancel : linda->simulate_cancel;
342 // if user wants to cancel, or looped because of a timeout, the call returns without sending anything 342 // if user wants to cancel, or looped because of a timeout, the call returns without sending anything
343 if( !try_again || cancel != CANCEL_NONE) 343 if( !try_again || cancel != CANCEL_NONE)
344 { 344 {
345 pushed = 0; 345 pushed = 0;
346 break; 346 break;
347 } 347 }
348 348
349 // all arguments of receive() but the first are passed to the keeper's receive function 349 // all arguments of receive() but the first are passed to the keeper's receive function
350 pushed = keeper_call( linda->U, K->L, keeper_receive, L, linda, key_i); 350 pushed = keeper_call( linda->U, K->L, keeper_receive, L, linda, key_i);
351 if( pushed < 0) 351 if( pushed < 0)
352 { 352 {
353 break; 353 break;
354 } 354 }
355 if( pushed > 0) 355 if( pushed > 0)
356 { 356 {
357 ASSERT_L( pushed >= expected_pushed_min && pushed <= expected_pushed_max); 357 ASSERT_L( pushed >= expected_pushed_min && pushed <= expected_pushed_max);
358 // replace sentinels with real nils 358 // replace sentinels with real nils
359 keeper_toggle_nil_sentinels( L, lua_gettop( L) - pushed, eLM_FromKeeper); 359 keeper_toggle_nil_sentinels( L, lua_gettop( L) - pushed, eLM_FromKeeper);
360 // To be done from within the 'K' locking area 360 // To be done from within the 'K' locking area
361 // 361 //
362 SIGNAL_ALL( &linda->read_happened); 362 SIGNAL_ALL( &linda->read_happened);
363 break; 363 break;
364 } 364 }
365 365
366 if( timeout == 0.0) 366 if( timeout == 0.0)
367 { 367 {
368 break; /* instant timeout */ 368 break; /* instant timeout */
369 } 369 }
370 370
371 // nothing received, wait until timeout or signalled that we should try again 371 // nothing received, wait until timeout or signalled that we should try again
372 { 372 {
373 enum e_status prev_status = ERROR_ST; // prevent 'might be used uninitialized' warnings 373 enum e_status prev_status = ERROR_ST; // prevent 'might be used uninitialized' warnings
374 if( s != NULL) 374 if( s != NULL)
375 { 375 {
376 // change status of lane to "waiting" 376 // change status of lane to "waiting"
377 prev_status = s->status; // RUNNING, most likely 377 prev_status = s->status; // RUNNING, most likely
378 ASSERT_L( prev_status == RUNNING); // but check, just in case 378 ASSERT_L( prev_status == RUNNING); // but check, just in case
379 s->status = WAITING; 379 s->status = WAITING;
380 ASSERT_L( s->waiting_on == NULL); 380 ASSERT_L( s->waiting_on == NULL);
381 s->waiting_on = &linda->write_happened; 381 s->waiting_on = &linda->write_happened;
382 } 382 }
383 // not enough data to read: wakeup when data was sent, or when timeout is reached 383 // not enough data to read: wakeup when data was sent, or when timeout is reached
384 try_again = SIGNAL_WAIT( &linda->write_happened, &K->keeper_cs, timeout); 384 try_again = SIGNAL_WAIT( &linda->write_happened, &K->keeper_cs, timeout);
385 if( s != NULL) 385 if( s != NULL)
386 { 386 {
387 s->waiting_on = NULL; 387 s->waiting_on = NULL;
388 s->status = prev_status; 388 s->status = prev_status;
389 } 389 }
390 } 390 }
391 } 391 }
392 } 392 }
393 393
394 if( pushed < 0) 394 if( pushed < 0)
395 { 395 {
396 return luaL_error( L, "tried to copy unsupported types"); 396 return luaL_error( L, "tried to copy unsupported types");
397 } 397 }
398 398
399 switch( cancel) 399 switch( cancel)
400 { 400 {
401 case CANCEL_SOFT: 401 case CANCEL_SOFT:
402 // if user wants to soft-cancel, the call returns CANCEL_ERROR 402 // if user wants to soft-cancel, the call returns CANCEL_ERROR
403 push_unique_key( L, CANCEL_ERROR); 403 push_unique_key( L, CANCEL_ERROR);
404 return 1; 404 return 1;
405 405
406 case CANCEL_HARD: 406 case CANCEL_HARD:
407 // raise an error interrupting execution only in case of hard cancel 407 // raise an error interrupting execution only in case of hard cancel
408 return cancel_error( L); // raises an error and doesn't return 408 return cancel_error( L); // raises an error and doesn't return
409 409
410 default: 410 default:
411 return pushed; 411 return pushed;
412 } 412 }
413} 413}
414 414
415 415
@@ -423,51 +423,51 @@ LUAG_FUNC( linda_receive)
423*/ 423*/
424LUAG_FUNC( linda_set) 424LUAG_FUNC( linda_set)
425{ 425{
426 struct s_Linda* const linda = lua_toLinda( L, 1); 426 struct s_Linda* const linda = lua_toLinda( L, 1);
427 int pushed; 427 int pushed;
428 bool_t has_value = lua_gettop( L) > 2; 428 bool_t has_value = lua_gettop( L) > 2;
429 429
430 // make sure the key is of a valid type (throws an error if not the case) 430 // make sure the key is of a valid type (throws an error if not the case)
431 check_key_types( L, 2, 2); 431 check_key_types( L, 2, 2);
432 432
433 { 433 {
434 Keeper* K = which_keeper( linda->U->keepers, LINDA_KEEPER_HASHSEED( linda)); 434 Keeper* K = which_keeper( linda->U->keepers, LINDA_KEEPER_HASHSEED( linda));
435 435
436 if( linda->simulate_cancel == CANCEL_NONE) 436 if( linda->simulate_cancel == CANCEL_NONE)
437 { 437 {
438 if( has_value) 438 if( has_value)
439 { 439 {
440 // convert nils to some special non-nil sentinel in sent values 440 // convert nils to some special non-nil sentinel in sent values
441 keeper_toggle_nil_sentinels( L, 3, eLM_ToKeeper); 441 keeper_toggle_nil_sentinels( L, 3, eLM_ToKeeper);
442 } 442 }
443 pushed = keeper_call( linda->U, K->L, KEEPER_API( set), L, linda, 2); 443 pushed = keeper_call( linda->U, K->L, KEEPER_API( set), L, linda, 2);
444 if( pushed >= 0) // no error? 444 if( pushed >= 0) // no error?
445 { 445 {
446 ASSERT_L( pushed == 0 || pushed == 1); 446 ASSERT_L( pushed == 0 || pushed == 1);
447 447
448 if( has_value) 448 if( has_value)
449 { 449 {
450 // we put some data in the slot, tell readers that they should wake 450 // we put some data in the slot, tell readers that they should wake
451 SIGNAL_ALL( &linda->write_happened); // To be done from within the 'K' locking area 451 SIGNAL_ALL( &linda->write_happened); // To be done from within the 'K' locking area
452 } 452 }
453 if( pushed == 1) 453 if( pushed == 1)
454 { 454 {
455 // the key was full, but it is no longer the case, tell writers they should wake 455 // the key was full, but it is no longer the case, tell writers they should wake
456 ASSERT_L( lua_type( L, -1) == LUA_TBOOLEAN && lua_toboolean( L, -1) == 1); 456 ASSERT_L( lua_type( L, -1) == LUA_TBOOLEAN && lua_toboolean( L, -1) == 1);
457 SIGNAL_ALL( &linda->read_happened); // To be done from within the 'K' locking area 457 SIGNAL_ALL( &linda->read_happened); // To be done from within the 'K' locking area
458 } 458 }
459 } 459 }
460 } 460 }
461 else // linda is cancelled 461 else // linda is cancelled
462 { 462 {
463 // do nothing and return lanes.cancel_error 463 // do nothing and return lanes.cancel_error
464 push_unique_key( L, CANCEL_ERROR); 464 push_unique_key( L, CANCEL_ERROR);
465 pushed = 1; 465 pushed = 1;
466 } 466 }
467 } 467 }
468 468
469 // must trigger any error after keeper state has been released 469 // must trigger any error after keeper state has been released
470 return (pushed < 0) ? luaL_error( L, "tried to copy unsupported types") : pushed; 470 return (pushed < 0) ? luaL_error( L, "tried to copy unsupported types") : pushed;
471} 471}
472 472
473 473
@@ -478,21 +478,21 @@ LUAG_FUNC( linda_set)
478 */ 478 */
479LUAG_FUNC( linda_count) 479LUAG_FUNC( linda_count)
480{ 480{
481 struct s_Linda* linda = lua_toLinda( L, 1); 481 struct s_Linda* linda = lua_toLinda( L, 1);
482 int pushed; 482 int pushed;
483 483
484 // make sure the keys are of a valid type 484 // make sure the keys are of a valid type
485 check_key_types( L, 2, lua_gettop( L)); 485 check_key_types( L, 2, lua_gettop( L));
486 486
487 { 487 {
488 Keeper* K = which_keeper( linda->U->keepers, LINDA_KEEPER_HASHSEED( linda)); 488 Keeper* K = which_keeper( linda->U->keepers, LINDA_KEEPER_HASHSEED( linda));
489 pushed = keeper_call( linda->U, K->L, KEEPER_API( count), L, linda, 2); 489 pushed = keeper_call( linda->U, K->L, KEEPER_API( count), L, linda, 2);
490 if( pushed < 0) 490 if( pushed < 0)
491 { 491 {
492 return luaL_error( L, "tried to count an invalid key"); 492 return luaL_error( L, "tried to count an invalid key");
493 } 493 }
494 } 494 }
495 return pushed; 495 return pushed;
496} 496}
497 497
498 498
@@ -503,39 +503,39 @@ LUAG_FUNC( linda_count)
503*/ 503*/
504LUAG_FUNC( linda_get) 504LUAG_FUNC( linda_get)
505{ 505{
506 struct s_Linda* const linda = lua_toLinda( L, 1); 506 struct s_Linda* const linda = lua_toLinda( L, 1);
507 int pushed; 507 int pushed;
508 lua_Integer count = luaL_optinteger( L, 3, 1); 508 lua_Integer count = luaL_optinteger( L, 3, 1);
509 luaL_argcheck( L, count >= 1, 3, "count should be >= 1"); 509 luaL_argcheck( L, count >= 1, 3, "count should be >= 1");
510 luaL_argcheck( L, lua_gettop( L) <= 3, 4, "too many arguments"); 510 luaL_argcheck( L, lua_gettop( L) <= 3, 4, "too many arguments");
511 511
512 // make sure the key is of a valid type (throws an error if not the case) 512 // make sure the key is of a valid type (throws an error if not the case)
513 check_key_types( L, 2, 2); 513 check_key_types( L, 2, 2);
514 { 514 {
515 Keeper* K = which_keeper( linda->U->keepers, LINDA_KEEPER_HASHSEED( linda)); 515 Keeper* K = which_keeper( linda->U->keepers, LINDA_KEEPER_HASHSEED( linda));
516 516
517 if( linda->simulate_cancel == CANCEL_NONE) 517 if( linda->simulate_cancel == CANCEL_NONE)
518 { 518 {
519 pushed = keeper_call( linda->U, K->L, KEEPER_API( get), L, linda, 2); 519 pushed = keeper_call( linda->U, K->L, KEEPER_API( get), L, linda, 2);
520 if( pushed > 0) 520 if( pushed > 0)
521 { 521 {
522 keeper_toggle_nil_sentinels( L, lua_gettop( L) - pushed, eLM_FromKeeper); 522 keeper_toggle_nil_sentinels( L, lua_gettop( L) - pushed, eLM_FromKeeper);
523 } 523 }
524 } 524 }
525 else // linda is cancelled 525 else // linda is cancelled
526 { 526 {
527 // do nothing and return lanes.cancel_error 527 // do nothing and return lanes.cancel_error
528 push_unique_key( L, CANCEL_ERROR); 528 push_unique_key( L, CANCEL_ERROR);
529 pushed = 1; 529 pushed = 1;
530 } 530 }
531 // an error can be raised if we attempt to read an unregistered function 531 // an error can be raised if we attempt to read an unregistered function
532 if( pushed < 0) 532 if( pushed < 0)
533 { 533 {
534 return luaL_error( L, "tried to copy unsupported types"); 534 return luaL_error( L, "tried to copy unsupported types");
535 } 535 }
536 } 536 }
537 537
538 return pushed; 538 return pushed;
539} 539}
540 540
541 541
@@ -547,38 +547,38 @@ LUAG_FUNC( linda_get)
547*/ 547*/
548LUAG_FUNC( linda_limit) 548LUAG_FUNC( linda_limit)
549{ 549{
550 struct s_Linda* linda = lua_toLinda( L, 1); 550 struct s_Linda* linda = lua_toLinda( L, 1);
551 int pushed; 551 int pushed;
552 552
553 // make sure we got 3 arguments: the linda, a key and a limit 553 // make sure we got 3 arguments: the linda, a key and a limit
554 luaL_argcheck( L, lua_gettop( L) == 3, 2, "wrong number of arguments"); 554 luaL_argcheck( L, lua_gettop( L) == 3, 2, "wrong number of arguments");
555 // make sure we got a numeric limit 555 // make sure we got a numeric limit
556 luaL_checknumber( L, 3); 556 luaL_checknumber( L, 3);
557 // make sure the key is of a valid type 557 // make sure the key is of a valid type
558 check_key_types( L, 2, 2); 558 check_key_types( L, 2, 2);
559 559
560 { 560 {
561 Keeper* K = which_keeper( linda->U->keepers, LINDA_KEEPER_HASHSEED( linda)); 561 Keeper* K = which_keeper( linda->U->keepers, LINDA_KEEPER_HASHSEED( linda));
562 562
563 if( linda->simulate_cancel == CANCEL_NONE) 563 if( linda->simulate_cancel == CANCEL_NONE)
564 { 564 {
565 pushed = keeper_call( linda->U, K->L, KEEPER_API( limit), L, linda, 2); 565 pushed = keeper_call( linda->U, K->L, KEEPER_API( limit), L, linda, 2);
566 ASSERT_L( pushed == 0 || pushed == 1); // no error, optional boolean value saying if we should wake blocked writer threads 566 ASSERT_L( pushed == 0 || pushed == 1); // no error, optional boolean value saying if we should wake blocked writer threads
567 if( pushed == 1) 567 if( pushed == 1)
568 { 568 {
569 ASSERT_L( lua_type( L, -1) == LUA_TBOOLEAN && lua_toboolean( L, -1) == 1); 569 ASSERT_L( lua_type( L, -1) == LUA_TBOOLEAN && lua_toboolean( L, -1) == 1);
570 SIGNAL_ALL( &linda->read_happened); // To be done from within the 'K' locking area 570 SIGNAL_ALL( &linda->read_happened); // To be done from within the 'K' locking area
571 } 571 }
572 } 572 }
573 else // linda is cancelled 573 else // linda is cancelled
574 { 574 {
575 // do nothing and return lanes.cancel_error 575 // do nothing and return lanes.cancel_error
576 push_unique_key( L, CANCEL_ERROR); 576 push_unique_key( L, CANCEL_ERROR);
577 pushed = 1; 577 pushed = 1;
578 } 578 }
579 } 579 }
580 // propagate pushed boolean if any 580 // propagate pushed boolean if any
581 return pushed; 581 return pushed;
582} 582}
583 583
584 584
@@ -589,35 +589,35 @@ LUAG_FUNC( linda_limit)
589*/ 589*/
590LUAG_FUNC( linda_cancel) 590LUAG_FUNC( linda_cancel)
591{ 591{
592 struct s_Linda* linda = lua_toLinda( L, 1); 592 struct s_Linda* linda = lua_toLinda( L, 1);
593 char const* who = luaL_optstring( L, 2, "both"); 593 char const* who = luaL_optstring( L, 2, "both");
594 594
595 // make sure we got 3 arguments: the linda, a key and a limit 595 // make sure we got 3 arguments: the linda, a key and a limit
596 luaL_argcheck( L, lua_gettop( L) <= 2, 2, "wrong number of arguments"); 596 luaL_argcheck( L, lua_gettop( L) <= 2, 2, "wrong number of arguments");
597 597
598 linda->simulate_cancel = CANCEL_SOFT; 598 linda->simulate_cancel = CANCEL_SOFT;
599 if( strcmp( who, "both") == 0) // tell everyone writers to wake up 599 if( strcmp( who, "both") == 0) // tell everyone writers to wake up
600 { 600 {
601 SIGNAL_ALL( &linda->write_happened); 601 SIGNAL_ALL( &linda->write_happened);
602 SIGNAL_ALL( &linda->read_happened); 602 SIGNAL_ALL( &linda->read_happened);
603 } 603 }
604 else if( strcmp( who, "none") == 0) // reset flag 604 else if( strcmp( who, "none") == 0) // reset flag
605 { 605 {
606 linda->simulate_cancel = CANCEL_NONE; 606 linda->simulate_cancel = CANCEL_NONE;
607 } 607 }
608 else if( strcmp( who, "read") == 0) // tell blocked readers to wake up 608 else if( strcmp( who, "read") == 0) // tell blocked readers to wake up
609 { 609 {
610 SIGNAL_ALL( &linda->write_happened); 610 SIGNAL_ALL( &linda->write_happened);
611 } 611 }
612 else if( strcmp( who, "write") == 0) // tell blocked writers to wake up 612 else if( strcmp( who, "write") == 0) // tell blocked writers to wake up
613 { 613 {
614 SIGNAL_ALL( &linda->read_happened); 614 SIGNAL_ALL( &linda->read_happened);
615 } 615 }
616 else 616 else
617 { 617 {
618 return luaL_error( L, "unknown wake hint '%s'", who); 618 return luaL_error( L, "unknown wake hint '%s'", who);
619 } 619 }
620 return 0; 620 return 0;
621} 621}
622 622
623 623
@@ -633,9 +633,9 @@ LUAG_FUNC( linda_cancel)
633*/ 633*/
634LUAG_FUNC( linda_deep) 634LUAG_FUNC( linda_deep)
635{ 635{
636 struct s_Linda* linda= lua_toLinda( L, 1); 636 struct s_Linda* linda= lua_toLinda( L, 1);
637 lua_pushlightuserdata( L, linda); // just the address 637 lua_pushlightuserdata( L, linda); // just the address
638 return 1; 638 return 1;
639} 639}
640 640
641 641
@@ -649,28 +649,28 @@ LUAG_FUNC( linda_deep)
649 649
650static int linda_tostring( lua_State* L, int idx_, bool_t opt_) 650static int linda_tostring( lua_State* L, int idx_, bool_t opt_)
651{ 651{
652 struct s_Linda* linda = (struct s_Linda*) luaG_todeep( L, linda_id, idx_); 652 struct s_Linda* linda = (struct s_Linda*) luaG_todeep( L, linda_id, idx_);
653 if( !opt_) 653 if( !opt_)
654 { 654 {
655 luaL_argcheck( L, linda, idx_, "expecting a linda object"); 655 luaL_argcheck( L, linda, idx_, "expecting a linda object");
656 } 656 }
657 if( linda != NULL) 657 if( linda != NULL)
658 { 658 {
659 char text[128]; 659 char text[128];
660 int len; 660 int len;
661 if( linda->name[0]) 661 if( linda->name[0])
662 len = sprintf( text, "Linda: %.*s", (int)sizeof(text) - 8, linda->name); 662 len = sprintf( text, "Linda: %.*s", (int)sizeof(text) - 8, linda->name);
663 else 663 else
664 len = sprintf( text, "Linda: %p", linda); 664 len = sprintf( text, "Linda: %p", linda);
665 lua_pushlstring( L, text, len); 665 lua_pushlstring( L, text, len);
666 return 1; 666 return 1;
667 } 667 }
668 return 0; 668 return 0;
669} 669}
670 670
671LUAG_FUNC( linda_tostring) 671LUAG_FUNC( linda_tostring)
672{ 672{
673 return linda_tostring( L, 1, FALSE); 673 return linda_tostring( L, 1, FALSE);
674} 674}
675 675
676 676
@@ -683,24 +683,24 @@ LUAG_FUNC( linda_tostring)
683*/ 683*/
684LUAG_FUNC( linda_concat) 684LUAG_FUNC( linda_concat)
685{ // linda1? linda2? 685{ // linda1? linda2?
686 bool_t atLeastOneLinda = FALSE; 686 bool_t atLeastOneLinda = FALSE;
687 // Lua semantics enforce that one of the 2 arguments is a Linda, but not necessarily both. 687 // Lua semantics enforce that one of the 2 arguments is a Linda, but not necessarily both.
688 if( linda_tostring( L, 1, TRUE)) 688 if( linda_tostring( L, 1, TRUE))
689 { 689 {
690 atLeastOneLinda = TRUE; 690 atLeastOneLinda = TRUE;
691 lua_replace( L, 1); 691 lua_replace( L, 1);
692 } 692 }
693 if( linda_tostring( L, 2, TRUE)) 693 if( linda_tostring( L, 2, TRUE))
694 { 694 {
695 atLeastOneLinda = TRUE; 695 atLeastOneLinda = TRUE;
696 lua_replace( L, 2); 696 lua_replace( L, 2);
697 } 697 }
698 if( !atLeastOneLinda) // should not be possible 698 if( !atLeastOneLinda) // should not be possible
699 { 699 {
700 return luaL_error( L, "internal error: linda_concat called on non-Linda"); 700 return luaL_error( L, "internal error: linda_concat called on non-Linda");
701 } 701 }
702 lua_concat( L, 2); 702 lua_concat( L, 2);
703 return 1; 703 return 1;
704} 704}
705 705
706/* 706/*
@@ -709,9 +709,9 @@ LUAG_FUNC( linda_concat)
709 */ 709 */
710LUAG_FUNC( linda_dump) 710LUAG_FUNC( linda_dump)
711{ 711{
712 struct s_Linda* linda = lua_toLinda( L, 1); 712 struct s_Linda* linda = lua_toLinda( L, 1);
713 ASSERT_L( linda->U == universe_get( L)); 713 ASSERT_L( linda->U == universe_get( L));
714 return keeper_push_linda_storage( linda->U, L, linda, LINDA_KEEPER_HASHSEED( linda)); 714 return keeper_push_linda_storage( linda->U, L, linda, LINDA_KEEPER_HASHSEED( linda));
715} 715}
716 716
717/* 717/*
@@ -720,16 +720,16 @@ LUAG_FUNC( linda_dump)
720 */ 720 */
721LUAG_FUNC( linda_towatch) 721LUAG_FUNC( linda_towatch)
722{ 722{
723 struct s_Linda* linda = lua_toLinda( L, 1); 723 struct s_Linda* linda = lua_toLinda( L, 1);
724 int pushed; 724 int pushed;
725 ASSERT_L( linda->U == universe_get( L)); 725 ASSERT_L( linda->U == universe_get( L));
726 pushed = keeper_push_linda_storage( linda->U, L, linda, LINDA_KEEPER_HASHSEED( linda)); 726 pushed = keeper_push_linda_storage( linda->U, L, linda, LINDA_KEEPER_HASHSEED( linda));
727 if( pushed == 0) 727 if( pushed == 0)
728 { 728 {
729 // if the linda is empty, don't return nil 729 // if the linda is empty, don't return nil
730 pushed = linda_tostring( L, 1, FALSE); 730 pushed = linda_tostring( L, 1, FALSE);
731 } 731 }
732 return pushed; 732 return pushed;
733} 733}
734 734
735/* 735/*
@@ -758,160 +758,160 @@ LUAG_FUNC( linda_towatch)
758*/ 758*/
759static void* linda_id( lua_State* L, DeepOp op_) 759static void* linda_id( lua_State* L, DeepOp op_)
760{ 760{
761 switch( op_) 761 switch( op_)
762 { 762 {
763 case eDO_new: 763 case eDO_new:
764 { 764 {
765 struct s_Linda* s; 765 struct s_Linda* s;
766 size_t name_len = 0; 766 size_t name_len = 0;
767 char const* linda_name = NULL; 767 char const* linda_name = NULL;
768 unsigned long linda_group = 0; 768 unsigned long linda_group = 0;
769 // should have a string and/or a number of the stack as parameters (name and group) 769 // should have a string and/or a number of the stack as parameters (name and group)
770 switch( lua_gettop( L)) 770 switch( lua_gettop( L))
771 { 771 {
772 default: // 0 772 default: // 0
773 break; 773 break;
774 774
775 case 1: // 1 parameter, either a name or a group 775 case 1: // 1 parameter, either a name or a group
776 if( lua_type( L, -1) == LUA_TSTRING) 776 if( lua_type( L, -1) == LUA_TSTRING)
777 { 777 {
778 linda_name = lua_tolstring( L, -1, &name_len); 778 linda_name = lua_tolstring( L, -1, &name_len);
779 } 779 }
780 else 780 else
781 { 781 {
782 linda_group = (unsigned long) lua_tointeger( L, -1); 782 linda_group = (unsigned long) lua_tointeger( L, -1);
783 } 783 }
784 break; 784 break;
785 785
786 case 2: // 2 parameters, a name and group, in that order 786 case 2: // 2 parameters, a name and group, in that order
787 linda_name = lua_tolstring( L, -2, &name_len); 787 linda_name = lua_tolstring( L, -2, &name_len);
788 linda_group = (unsigned long) lua_tointeger( L, -1); 788 linda_group = (unsigned long) lua_tointeger( L, -1);
789 break; 789 break;
790 } 790 }
791 791
792 /* The deep data is allocated separately of Lua stack; we might no 792 /* The deep data is allocated separately of Lua stack; we might no
793 * longer be around when last reference to it is being released. 793 * longer be around when last reference to it is being released.
794 * One can use any memory allocation scheme. 794 * One can use any memory allocation scheme.
795 * just don't use L's allocF because we don't know which state will get the honor of GCing the linda 795 * just don't use L's allocF because we don't know which state will get the honor of GCing the linda
796 */ 796 */
797 s = (struct s_Linda*) malloc( sizeof(struct s_Linda) + name_len); // terminating 0 is already included 797 s = (struct s_Linda*) malloc( sizeof(struct s_Linda) + name_len); // terminating 0 is already included
798 if( s) 798 if( s)
799 { 799 {
800 s->prelude.magic.value = DEEP_VERSION.value; 800 s->prelude.magic.value = DEEP_VERSION.value;
801 SIGNAL_INIT( &s->read_happened); 801 SIGNAL_INIT( &s->read_happened);
802 SIGNAL_INIT( &s->write_happened); 802 SIGNAL_INIT( &s->write_happened);
803 s->U = universe_get( L); 803 s->U = universe_get( L);
804 s->simulate_cancel = CANCEL_NONE; 804 s->simulate_cancel = CANCEL_NONE;
805 s->group = linda_group << KEEPER_MAGIC_SHIFT; 805 s->group = linda_group << KEEPER_MAGIC_SHIFT;
806 s->name[0] = 0; 806 s->name[0] = 0;
807 memcpy( s->name, linda_name, name_len ? name_len + 1 : 0); 807 memcpy( s->name, linda_name, name_len ? name_len + 1 : 0);
808 } 808 }
809 return s; 809 return s;
810 } 810 }
811 811
812 case eDO_delete: 812 case eDO_delete:
813 { 813 {
814 Keeper* K; 814 Keeper* K;
815 struct s_Linda* linda = lua_touserdata( L, 1); 815 struct s_Linda* linda = lua_touserdata( L, 1);
816 ASSERT_L( linda); 816 ASSERT_L( linda);
817 817
818 // Clean associated structures in the keeper state. 818 // Clean associated structures in the keeper state.
819 K = keeper_acquire( linda->U->keepers, LINDA_KEEPER_HASHSEED( linda)); 819 K = keeper_acquire( linda->U->keepers, LINDA_KEEPER_HASHSEED( linda));
820 if( K && K->L) // can be NULL if this happens during main state shutdown (lanes is GC'ed -> no keepers -> no need to cleanup) 820 if( K && K->L) // can be NULL if this happens during main state shutdown (lanes is GC'ed -> no keepers -> no need to cleanup)
821 { 821 {
822 // hopefully this won't ever raise an error as we would jump to the closest pcall site while forgetting to release the keeper mutex... 822 // hopefully this won't ever raise an error as we would jump to the closest pcall site while forgetting to release the keeper mutex...
823 keeper_call( linda->U, K->L, KEEPER_API( clear), L, linda, 0); 823 keeper_call( linda->U, K->L, KEEPER_API( clear), L, linda, 0);
824 } 824 }
825 keeper_release( K); 825 keeper_release( K);
826 826
827 // There aren't any lanes waiting on these lindas, since all proxies have been gc'ed. Right? 827 // There aren't any lanes waiting on these lindas, since all proxies have been gc'ed. Right?
828 SIGNAL_FREE( &linda->read_happened); 828 SIGNAL_FREE( &linda->read_happened);
829 SIGNAL_FREE( &linda->write_happened); 829 SIGNAL_FREE( &linda->write_happened);
830 free( linda); 830 free( linda);
831 return NULL; 831 return NULL;
832 } 832 }
833 833
834 case eDO_metatable: 834 case eDO_metatable:
835 { 835 {
836 836
837 STACK_CHECK( L, 0); 837 STACK_CHECK( L, 0);
838 lua_newtable( L); 838 lua_newtable( L);
839 // metatable is its own index 839 // metatable is its own index
840 lua_pushvalue( L, -1); 840 lua_pushvalue( L, -1);
841 lua_setfield( L, -2, "__index"); 841 lua_setfield( L, -2, "__index");
842 842
843 // protect metatable from external access 843 // protect metatable from external access
844 lua_pushliteral( L, "Linda"); 844 lua_pushliteral( L, "Linda");
845 lua_setfield( L, -2, "__metatable"); 845 lua_setfield( L, -2, "__metatable");
846 846
847 lua_pushcfunction( L, LG_linda_tostring); 847 lua_pushcfunction( L, LG_linda_tostring);
848 lua_setfield( L, -2, "__tostring"); 848 lua_setfield( L, -2, "__tostring");
849 849
850 // Decoda __towatch support 850 // Decoda __towatch support
851 lua_pushcfunction( L, LG_linda_towatch); 851 lua_pushcfunction( L, LG_linda_towatch);
852 lua_setfield( L, -2, "__towatch"); 852 lua_setfield( L, -2, "__towatch");
853 853
854 lua_pushcfunction( L, LG_linda_concat); 854 lua_pushcfunction( L, LG_linda_concat);
855 lua_setfield( L, -2, "__concat"); 855 lua_setfield( L, -2, "__concat");
856 856
857 // protected calls, to ensure associated keeper is always released even in case of error 857 // protected calls, to ensure associated keeper is always released even in case of error
858 // all function are the protected call wrapper, where the actual operation is provided as upvalue 858 // all function are the protected call wrapper, where the actual operation is provided as upvalue
859 // note that this kind of thing can break function lookup as we use the function pointer here and there 859 // note that this kind of thing can break function lookup as we use the function pointer here and there
860 860
861 lua_pushcfunction( L, LG_linda_send); 861 lua_pushcfunction( L, LG_linda_send);
862 lua_pushcclosure( L, LG_linda_protected_call, 1); 862 lua_pushcclosure( L, LG_linda_protected_call, 1);
863 lua_setfield( L, -2, "send"); 863 lua_setfield( L, -2, "send");
864 864
865 lua_pushcfunction( L, LG_linda_receive); 865 lua_pushcfunction( L, LG_linda_receive);
866 lua_pushcclosure( L, LG_linda_protected_call, 1); 866 lua_pushcclosure( L, LG_linda_protected_call, 1);
867 lua_setfield( L, -2, "receive"); 867 lua_setfield( L, -2, "receive");
868 868
869 lua_pushcfunction( L, LG_linda_limit); 869 lua_pushcfunction( L, LG_linda_limit);
870 lua_pushcclosure( L, LG_linda_protected_call, 1); 870 lua_pushcclosure( L, LG_linda_protected_call, 1);
871 lua_setfield( L, -2, "limit"); 871 lua_setfield( L, -2, "limit");
872 872
873 lua_pushcfunction( L, LG_linda_set); 873 lua_pushcfunction( L, LG_linda_set);
874 lua_pushcclosure( L, LG_linda_protected_call, 1); 874 lua_pushcclosure( L, LG_linda_protected_call, 1);
875 lua_setfield( L, -2, "set"); 875 lua_setfield( L, -2, "set");
876 876
877 lua_pushcfunction( L, LG_linda_count); 877 lua_pushcfunction( L, LG_linda_count);
878 lua_pushcclosure( L, LG_linda_protected_call, 1); 878 lua_pushcclosure( L, LG_linda_protected_call, 1);
879 lua_setfield( L, -2, "count"); 879 lua_setfield( L, -2, "count");
880 880
881 lua_pushcfunction( L, LG_linda_get); 881 lua_pushcfunction( L, LG_linda_get);
882 lua_pushcclosure( L, LG_linda_protected_call, 1); 882 lua_pushcclosure( L, LG_linda_protected_call, 1);
883 lua_setfield( L, -2, "get"); 883 lua_setfield( L, -2, "get");
884 884
885 lua_pushcfunction( L, LG_linda_cancel); 885 lua_pushcfunction( L, LG_linda_cancel);
886 lua_setfield( L, -2, "cancel"); 886 lua_setfield( L, -2, "cancel");
887 887
888 lua_pushcfunction( L, LG_linda_deep); 888 lua_pushcfunction( L, LG_linda_deep);
889 lua_setfield( L, -2, "deep"); 889 lua_setfield( L, -2, "deep");
890 890
891 lua_pushcfunction( L, LG_linda_dump); 891 lua_pushcfunction( L, LG_linda_dump);
892 lua_pushcclosure( L, LG_linda_protected_call, 1); 892 lua_pushcclosure( L, LG_linda_protected_call, 1);
893 lua_setfield( L, -2, "dump"); 893 lua_setfield( L, -2, "dump");
894 894
895 // some constants 895 // some constants
896 lua_pushliteral( L, BATCH_SENTINEL); 896 lua_pushliteral( L, BATCH_SENTINEL);
897 lua_setfield( L, -2, "batched"); 897 lua_setfield( L, -2, "batched");
898 898
899 push_unique_key( L, NIL_SENTINEL); 899 push_unique_key( L, NIL_SENTINEL);
900 lua_setfield( L, -2, "null"); 900 lua_setfield( L, -2, "null");
901 901
902 STACK_END( L, 1); 902 STACK_END( L, 1);
903 return NULL; 903 return NULL;
904 } 904 }
905 905
906 case eDO_module: 906 case eDO_module:
907 // linda is a special case because we know lanes must be loaded from the main lua state 907 // linda is a special case because we know lanes must be loaded from the main lua state
908 // to be able to ever get here, so we know it will remain loaded as long a the main state is around 908 // to be able to ever get here, so we know it will remain loaded as long a the main state is around
909 // in other words, forever. 909 // in other words, forever.
910 default: 910 default:
911 { 911 {
912 return NULL; 912 return NULL;
913 } 913 }
914 } 914 }
915} 915}
916 916
917/* 917/*
@@ -921,17 +921,17 @@ static void* linda_id( lua_State* L, DeepOp op_)
921 */ 921 */
922LUAG_FUNC( linda) 922LUAG_FUNC( linda)
923{ 923{
924 int const top = lua_gettop( L); 924 int const top = lua_gettop( L);
925 luaL_argcheck( L, top <= 2, top, "too many arguments"); 925 luaL_argcheck( L, top <= 2, top, "too many arguments");
926 if( top == 1) 926 if( top == 1)
927 { 927 {
928 int const t = lua_type( L, 1); 928 int const t = lua_type( L, 1);
929 luaL_argcheck( L, t == LUA_TSTRING || t == LUA_TNUMBER, 1, "wrong parameter (should be a string or a number)"); 929 luaL_argcheck( L, t == LUA_TSTRING || t == LUA_TNUMBER, 1, "wrong parameter (should be a string or a number)");
930 } 930 }
931 else if( top == 2) 931 else if( top == 2)
932 { 932 {
933 luaL_checktype( L, 1, LUA_TSTRING); 933 luaL_checktype( L, 1, LUA_TSTRING);
934 luaL_checktype( L, 2, LUA_TNUMBER); 934 luaL_checktype( L, 2, LUA_TNUMBER);
935 } 935 }
936 return luaG_newdeepuserdata( L, linda_id, 0); 936 return luaG_newdeepuserdata( L, linda_id, 0);
937} 937}
diff --git a/src/macros_and_utils.h b/src/macros_and_utils.h
index dba0010..b67a7c4 100644
--- a/src/macros_and_utils.h
+++ b/src/macros_and_utils.h
@@ -40,40 +40,40 @@ extern char const* debugspew_indent;
40#define _ASSERT_L( L, cond_) if( (cond_) == 0) { (void) luaL_error( L, "ASSERT failed: %s:%d '%s'", __FILE__, __LINE__, #cond_);} 40#define _ASSERT_L( L, cond_) if( (cond_) == 0) { (void) luaL_error( L, "ASSERT failed: %s:%d '%s'", __FILE__, __LINE__, #cond_);}
41 41
42#define STACK_CHECK( L, offset_) \ 42#define STACK_CHECK( L, offset_) \
43 { \ 43 { \
44 int const L##_delta = offset_; \ 44 int const L##_delta = offset_; \
45 if( (L##_delta < 0) || (lua_gettop( L) < L##_delta)) \ 45 if( (L##_delta < 0) || (lua_gettop( L) < L##_delta)) \
46 { \ 46 { \
47 assert( FALSE); \ 47 assert( FALSE); \
48 (void) luaL_error( L, "STACK INIT ASSERT failed (%d not %d): %s:%d", lua_gettop( L), L##_delta, __FILE__, __LINE__); \ 48 (void) luaL_error( L, "STACK INIT ASSERT failed (%d not %d): %s:%d", lua_gettop( L), L##_delta, __FILE__, __LINE__); \
49 } \ 49 } \
50 int const L##_oldtop = lua_gettop( L) - L##_delta 50 int const L##_oldtop = lua_gettop( L) - L##_delta
51 51
52#define STACK_CHECK_ABS( L, offset_) \ 52#define STACK_CHECK_ABS( L, offset_) \
53 { \ 53 { \
54 int const L##_pos = offset_; \ 54 int const L##_pos = offset_; \
55 if( lua_gettop( L) < L##_pos) \ 55 if( lua_gettop( L) < L##_pos) \
56 { \ 56 { \
57 assert( FALSE); \ 57 assert( FALSE); \
58 (void) luaL_error( L, "STACK INIT ASSERT failed (%d not %d): %s:%d", lua_gettop( L), L##_pos, __FILE__, __LINE__); \ 58 (void) luaL_error( L, "STACK INIT ASSERT failed (%d not %d): %s:%d", lua_gettop( L), L##_pos, __FILE__, __LINE__); \
59 } \ 59 } \
60 int const L##_oldtop = 0 60 int const L##_oldtop = 0
61 61
62#define STACK_MID( L, change) \ 62#define STACK_MID( L, change) \
63 do if( change != LUA_MULTRET) \ 63 do if( change != LUA_MULTRET) \
64 { \ 64 { \
65 int stack_check_a = lua_gettop( L) - L##_oldtop; \ 65 int stack_check_a = lua_gettop( L) - L##_oldtop; \
66 int stack_check_b = (change); \ 66 int stack_check_b = (change); \
67 if( stack_check_a != stack_check_b) \ 67 if( stack_check_a != stack_check_b) \
68 { \ 68 { \
69 assert( FALSE); \ 69 assert( FALSE); \
70 luaL_error( L, "STACK ASSERT failed (%d not %d): %s:%d", stack_check_a, stack_check_b, __FILE__, __LINE__); \ 70 luaL_error( L, "STACK ASSERT failed (%d not %d): %s:%d", stack_check_a, stack_check_b, __FILE__, __LINE__); \
71 } \ 71 } \
72 } while( 0) 72 } while( 0)
73 73
74#define STACK_END( L, change) \ 74#define STACK_END( L, change) \
75 STACK_MID( L, change); \ 75 STACK_MID( L, change); \
76 } 76 }
77 77
78#define STACK_DUMP( L) luaG_dump( L) 78#define STACK_DUMP( L) luaG_dump( L)
79 79
@@ -86,15 +86,15 @@ extern char const* debugspew_indent;
86// non-string keyed registry access 86// non-string keyed registry access
87#define REGISTRY_SET( L, key_, value_) \ 87#define REGISTRY_SET( L, key_, value_) \
88{ \ 88{ \
89 push_unique_key( L, key_); \ 89 push_unique_key( L, key_); \
90 value_; \ 90 value_; \
91 lua_rawset( L, LUA_REGISTRYINDEX); \ 91 lua_rawset( L, LUA_REGISTRYINDEX); \
92} 92}
93 93
94#define REGISTRY_GET( L, key_) \ 94#define REGISTRY_GET( L, key_) \
95{ \ 95{ \
96 push_unique_key( L, key_); \ 96 push_unique_key( L, key_); \
97 lua_rawget( L, LUA_REGISTRYINDEX); \ 97 lua_rawget( L, LUA_REGISTRYINDEX); \
98} 98}
99 99
100#define LUAG_FUNC( func_name) int LG_##func_name( lua_State* L) 100#define LUAG_FUNC( func_name) int LG_##func_name( lua_State* L)
diff --git a/src/state.c b/src/state.c
index 81371b7..9075c02 100644
--- a/src/state.c
+++ b/src/state.c
@@ -59,32 +59,32 @@ THE SOFTWARE.
59// 59//
60static int luaG_new_require( lua_State* L) 60static int luaG_new_require( lua_State* L)
61{ 61{
62 int rc; 62 int rc;
63 int const args = lua_gettop( L); // args 63 int const args = lua_gettop( L); // args
64 Universe* U = universe_get( L); 64 Universe* U = universe_get( L);
65 //char const* modname = luaL_checkstring( L, 1); 65 //char const* modname = luaL_checkstring( L, 1);
66 66
67 STACK_GROW( L, 1); 67 STACK_GROW( L, 1);
68 68
69 lua_pushvalue( L, lua_upvalueindex( 1)); // args require 69 lua_pushvalue( L, lua_upvalueindex( 1)); // args require
70 lua_insert( L, 1); // require args 70 lua_insert( L, 1); // require args
71 71
72 // Using 'lua_pcall()' to catch errors; otherwise a failing 'require' would 72 // Using 'lua_pcall()' to catch errors; otherwise a failing 'require' would
73 // leave us locked, blocking any future 'require' calls from other lanes. 73 // leave us locked, blocking any future 'require' calls from other lanes.
74 74
75 MUTEX_LOCK( &U->require_cs); 75 MUTEX_LOCK( &U->require_cs);
76 // starting with Lua 5.4, require may return a second optional value, so we need LUA_MULTRET 76 // starting with Lua 5.4, require may return a second optional value, so we need LUA_MULTRET
77 rc = lua_pcall( L, args, LUA_MULTRET, 0 /*errfunc*/ ); // err|result(s) 77 rc = lua_pcall( L, args, LUA_MULTRET, 0 /*errfunc*/ ); // err|result(s)
78 MUTEX_UNLOCK( &U->require_cs); 78 MUTEX_UNLOCK( &U->require_cs);
79 79
80 // the required module (or an error message) is left on the stack as returned value by original require function 80 // the required module (or an error message) is left on the stack as returned value by original require function
81 81
82 if( rc != LUA_OK) // LUA_ERRRUN / LUA_ERRMEM ? 82 if( rc != LUA_OK) // LUA_ERRRUN / LUA_ERRMEM ?
83 { 83 {
84 return lua_error( L); 84 return lua_error( L);
85 } 85 }
86 // should be 1 for Lua <= 5.3, 1 or 2 starting with Lua 5.4 86 // should be 1 for Lua <= 5.3, 1 or 2 starting with Lua 5.4
87 return lua_gettop(L); // result(s) 87 return lua_gettop(L); // result(s)
88} 88}
89 89
90/* 90/*
@@ -92,26 +92,26 @@ static int luaG_new_require( lua_State* L)
92*/ 92*/
93void serialize_require( DEBUGSPEW_PARAM_COMMA( Universe* U) lua_State* L) 93void serialize_require( DEBUGSPEW_PARAM_COMMA( Universe* U) lua_State* L)
94{ 94{
95 STACK_GROW( L, 1); 95 STACK_GROW( L, 1);
96 STACK_CHECK( L, 0); 96 STACK_CHECK( L, 0);
97 DEBUGSPEW_CODE( fprintf( stderr, INDENT_BEGIN "serializing require()\n" INDENT_END)); 97 DEBUGSPEW_CODE( fprintf( stderr, INDENT_BEGIN "serializing require()\n" INDENT_END));
98 98
99 // Check 'require' is there and not already wrapped; if not, do nothing 99 // Check 'require' is there and not already wrapped; if not, do nothing
100 // 100 //
101 lua_getglobal( L, "require"); 101 lua_getglobal( L, "require");
102 if( lua_isfunction( L, -1) && lua_tocfunction( L, -1) != luaG_new_require) 102 if( lua_isfunction( L, -1) && lua_tocfunction( L, -1) != luaG_new_require)
103 { 103 {
104 // [-1]: original 'require' function 104 // [-1]: original 'require' function
105 lua_pushcclosure( L, luaG_new_require, 1 /*upvalues*/); 105 lua_pushcclosure( L, luaG_new_require, 1 /*upvalues*/);
106 lua_setglobal( L, "require"); 106 lua_setglobal( L, "require");
107 } 107 }
108 else 108 else
109 { 109 {
110 // [-1]: nil 110 // [-1]: nil
111 lua_pop( L, 1); 111 lua_pop( L, 1);
112 } 112 }
113 113
114 STACK_END( L, 0); 114 STACK_END( L, 0);
115} 115}
116 116
117// ################################################################################################ 117// ################################################################################################
@@ -120,15 +120,15 @@ void serialize_require( DEBUGSPEW_PARAM_COMMA( Universe* U) lua_State* L)
120 120
121static int require_lanes_core( lua_State* L) 121static int require_lanes_core( lua_State* L)
122{ 122{
123 // leaves a copy of 'lanes.core' module table on the stack 123 // leaves a copy of 'lanes.core' module table on the stack
124 luaL_requiref( L, "lanes.core", luaopen_lanes_core, 0); 124 luaL_requiref( L, "lanes.core", luaopen_lanes_core, 0);
125 return 1; 125 return 1;
126} 126}
127 127
128 128
129static const luaL_Reg libs[] = 129static const luaL_Reg libs[] =
130{ 130{
131 { LUA_LOADLIBNAME, luaopen_package}, 131 { LUA_LOADLIBNAME, luaopen_package},
132{ LUA_TABLIBNAME, luaopen_table}, 132{ LUA_TABLIBNAME, luaopen_table},
133{ LUA_STRLIBNAME, luaopen_string}, 133{ LUA_STRLIBNAME, luaopen_string},
134{ LUA_MATHLIBNAME, luaopen_math}, 134{ LUA_MATHLIBNAME, luaopen_math},
@@ -157,152 +157,152 @@ static const luaL_Reg libs[] =
157 157
158{ LUA_DBLIBNAME, luaopen_debug}, 158{ LUA_DBLIBNAME, luaopen_debug},
159{ "lanes.core", require_lanes_core}, // So that we can open it like any base library (possible since we have access to the init function) 159{ "lanes.core", require_lanes_core}, // So that we can open it like any base library (possible since we have access to the init function)
160 // 160 //
161{ "base", NULL}, // ignore "base" (already acquired it) 161{ "base", NULL}, // ignore "base" (already acquired it)
162{ NULL, NULL } 162{ NULL, NULL }
163}; 163};
164 164
165static void open1lib( DEBUGSPEW_PARAM_COMMA( Universe* U) lua_State* L, char const* name_, size_t len_) 165static void open1lib( DEBUGSPEW_PARAM_COMMA( Universe* U) lua_State* L, char const* name_, size_t len_)
166{ 166{
167 int i; 167 int i;
168 for( i = 0; libs[i].name; ++ i) 168 for( i = 0; libs[i].name; ++ i)
169 { 169 {
170 if( strncmp( name_, libs[i].name, len_) == 0) 170 if( strncmp( name_, libs[i].name, len_) == 0)
171 { 171 {
172 lua_CFunction libfunc = libs[i].func; 172 lua_CFunction libfunc = libs[i].func;
173 name_ = libs[i].name; // note that the provided name_ doesn't necessarily ends with '\0', hence len_ 173 name_ = libs[i].name; // note that the provided name_ doesn't necessarily ends with '\0', hence len_
174 if( libfunc != NULL) 174 if( libfunc != NULL)
175 { 175 {
176 bool_t const isLanesCore = (libfunc == require_lanes_core) ? TRUE : FALSE; // don't want to create a global for "lanes.core" 176 bool_t const isLanesCore = (libfunc == require_lanes_core) ? TRUE : FALSE; // don't want to create a global for "lanes.core"
177 DEBUGSPEW_CODE( fprintf( stderr, INDENT_BEGIN "opening %.*s library\n" INDENT_END, (int) len_, name_)); 177 DEBUGSPEW_CODE( fprintf( stderr, INDENT_BEGIN "opening %.*s library\n" INDENT_END, (int) len_, name_));
178 STACK_CHECK( L, 0); 178 STACK_CHECK( L, 0);
179 // open the library as if through require(), and create a global as well if necessary (the library table is left on the stack) 179 // open the library as if through require(), and create a global as well if necessary (the library table is left on the stack)
180 luaL_requiref( L, name_, libfunc, !isLanesCore); 180 luaL_requiref( L, name_, libfunc, !isLanesCore);
181 // lanes.core doesn't declare a global, so scan it here and now 181 // lanes.core doesn't declare a global, so scan it here and now
182 if( isLanesCore == TRUE) 182 if( isLanesCore == TRUE)
183 { 183 {
184 populate_func_lookup_table( L, -1, name_); 184 populate_func_lookup_table( L, -1, name_);
185 } 185 }
186 lua_pop( L, 1); 186 lua_pop( L, 1);
187 STACK_END( L, 0); 187 STACK_END( L, 0);
188 } 188 }
189 break; 189 break;
190 } 190 }
191 } 191 }
192} 192}
193 193
194 194
195// just like lua_xmove, args are (from, to) 195// just like lua_xmove, args are (from, to)
196static void copy_one_time_settings( Universe* U, lua_State* L, lua_State* L2) 196static void copy_one_time_settings( Universe* U, lua_State* L, lua_State* L2)
197{ 197{
198 STACK_GROW( L, 2); 198 STACK_GROW( L, 2);
199 STACK_CHECK( L, 0); 199 STACK_CHECK( L, 0);
200 STACK_CHECK( L2, 0); 200 STACK_CHECK( L2, 0);
201 201
202 DEBUGSPEW_CODE( fprintf( stderr, INDENT_BEGIN "copy_one_time_settings()\n" INDENT_END)); 202 DEBUGSPEW_CODE( fprintf( stderr, INDENT_BEGIN "copy_one_time_settings()\n" INDENT_END));
203 DEBUGSPEW_CODE( ++ U->debugspew_indent_depth); 203 DEBUGSPEW_CODE( ++ U->debugspew_indent_depth);
204 204
205 REGISTRY_GET( L, CONFIG_REGKEY); // config 205 REGISTRY_GET( L, CONFIG_REGKEY); // config
206 // copy settings from from source to destination registry 206 // copy settings from from source to destination registry
207 if( luaG_inter_move( U, L, L2, 1, eLM_LaneBody) < 0) // // config 207 if( luaG_inter_move( U, L, L2, 1, eLM_LaneBody) < 0) // // config
208 { 208 {
209 (void) luaL_error( L, "failed to copy settings when loading lanes.core"); 209 (void) luaL_error( L, "failed to copy settings when loading lanes.core");
210 } 210 }
211 // set L2:_R[CONFIG_REGKEY] = settings 211 // set L2:_R[CONFIG_REGKEY] = settings
212 REGISTRY_SET( L2, CONFIG_REGKEY, lua_insert( L2, -2)); // 212 REGISTRY_SET( L2, CONFIG_REGKEY, lua_insert( L2, -2)); //
213 STACK_END( L2, 0); 213 STACK_END( L2, 0);
214 STACK_END( L, 0); 214 STACK_END( L, 0);
215 DEBUGSPEW_CODE( -- U->debugspew_indent_depth); 215 DEBUGSPEW_CODE( -- U->debugspew_indent_depth);
216} 216}
217 217
218void initialize_on_state_create( Universe* U, lua_State* L) 218void initialize_on_state_create( Universe* U, lua_State* L)
219{ 219{
220 STACK_CHECK( L, 0); 220 STACK_CHECK( L, 0);
221 lua_getfield( L, -1, "on_state_create"); // settings on_state_create|nil 221 lua_getfield( L, -1, "on_state_create"); // settings on_state_create|nil
222 if( !lua_isnil( L, -1)) 222 if( !lua_isnil( L, -1))
223 { 223 {
224 // store C function pointer in an internal variable 224 // store C function pointer in an internal variable
225 U->on_state_create_func = lua_tocfunction( L, -1); // settings on_state_create 225 U->on_state_create_func = lua_tocfunction( L, -1); // settings on_state_create
226 if( U->on_state_create_func != NULL) 226 if( U->on_state_create_func != NULL)
227 { 227 {
228 // make sure the function doesn't have upvalues 228 // make sure the function doesn't have upvalues
229 char const* upname = lua_getupvalue( L, -1, 1); // settings on_state_create upval? 229 char const* upname = lua_getupvalue( L, -1, 1); // settings on_state_create upval?
230 if( upname != NULL) // should be "" for C functions with upvalues if any 230 if( upname != NULL) // should be "" for C functions with upvalues if any
231 { 231 {
232 (void) luaL_error( L, "on_state_create shouldn't have upvalues"); 232 (void) luaL_error( L, "on_state_create shouldn't have upvalues");
233 } 233 }
234 // remove this C function from the config table so that it doesn't cause problems 234 // remove this C function from the config table so that it doesn't cause problems
235 // when we transfer the config table in newly created Lua states 235 // when we transfer the config table in newly created Lua states
236 lua_pushnil( L); // settings on_state_create nil 236 lua_pushnil( L); // settings on_state_create nil
237 lua_setfield( L, -3, "on_state_create"); // settings on_state_create 237 lua_setfield( L, -3, "on_state_create"); // settings on_state_create
238 } 238 }
239 else 239 else
240 { 240 {
241 // optim: store marker saying we have such a function in the config table 241 // optim: store marker saying we have such a function in the config table
242 U->on_state_create_func = (lua_CFunction) initialize_on_state_create; 242 U->on_state_create_func = (lua_CFunction) initialize_on_state_create;
243 } 243 }
244 } 244 }
245 lua_pop( L, 1); // settings 245 lua_pop( L, 1); // settings
246 STACK_END( L, 0); 246 STACK_END( L, 0);
247} 247}
248 248
249lua_State* create_state( Universe* U, lua_State* from_) 249lua_State* create_state( Universe* U, lua_State* from_)
250{ 250{
251 lua_State* L; 251 lua_State* L;
252 if( U->provide_allocator != NULL) 252 if( U->provide_allocator != NULL)
253 { 253 {
254 lua_pushcclosure( from_, U->provide_allocator, 0); 254 lua_pushcclosure( from_, U->provide_allocator, 0);
255 lua_call( from_, 0, 1); 255 lua_call( from_, 0, 1);
256 { 256 {
257 AllocatorDefinition* def = lua_touserdata( from_, -1); 257 AllocatorDefinition* def = lua_touserdata( from_, -1);
258 L = lua_newstate( def->allocF, def->allocUD); 258 L = lua_newstate( def->allocF, def->allocUD);
259 } 259 }
260 lua_pop( from_, 1); 260 lua_pop( from_, 1);
261 } 261 }
262 else 262 else
263 { 263 {
264 L = luaL_newstate(); 264 L = luaL_newstate();
265 } 265 }
266 266
267 if( L == NULL) 267 if( L == NULL)
268 { 268 {
269 (void) luaL_error( from_, "luaG_newstate() failed while creating state; out of memory"); 269 (void) luaL_error( from_, "luaG_newstate() failed while creating state; out of memory");
270 } 270 }
271 return L; 271 return L;
272} 272}
273 273
274void call_on_state_create( Universe* U, lua_State* L, lua_State* from_, LookupMode mode_) 274void call_on_state_create( Universe* U, lua_State* L, lua_State* from_, LookupMode mode_)
275{ 275{
276 if( U->on_state_create_func != NULL) 276 if( U->on_state_create_func != NULL)
277 { 277 {
278 STACK_CHECK( L, 0); 278 STACK_CHECK( L, 0);
279 DEBUGSPEW_CODE( fprintf( stderr, INDENT_BEGIN "calling on_state_create()\n" INDENT_END)); 279 DEBUGSPEW_CODE( fprintf( stderr, INDENT_BEGIN "calling on_state_create()\n" INDENT_END));
280 if( U->on_state_create_func != (lua_CFunction) initialize_on_state_create) 280 if( U->on_state_create_func != (lua_CFunction) initialize_on_state_create)
281 { 281 {
282 // C function: recreate a closure in the new state, bypassing the lookup scheme 282 // C function: recreate a closure in the new state, bypassing the lookup scheme
283 lua_pushcfunction( L, U->on_state_create_func); // on_state_create() 283 lua_pushcfunction( L, U->on_state_create_func); // on_state_create()
284 } 284 }
285 else // Lua function located in the config table, copied when we opened "lanes.core" 285 else // Lua function located in the config table, copied when we opened "lanes.core"
286 { 286 {
287 if( mode_ != eLM_LaneBody) 287 if( mode_ != eLM_LaneBody)
288 { 288 {
289 // if attempting to call in a keeper state, do nothing because the function doesn't exist there 289 // if attempting to call in a keeper state, do nothing because the function doesn't exist there
290 // this doesn't count as an error though 290 // this doesn't count as an error though
291 return; 291 return;
292 } 292 }
293 REGISTRY_GET( L, CONFIG_REGKEY); // {} 293 REGISTRY_GET( L, CONFIG_REGKEY); // {}
294 STACK_MID( L, 1); 294 STACK_MID( L, 1);
295 lua_getfield( L, -1, "on_state_create"); // {} on_state_create() 295 lua_getfield( L, -1, "on_state_create"); // {} on_state_create()
296 lua_remove( L, -2); // on_state_create() 296 lua_remove( L, -2); // on_state_create()
297 } 297 }
298 STACK_MID( L, 1); 298 STACK_MID( L, 1);
299 // capture error and raise it in caller state 299 // capture error and raise it in caller state
300 if( lua_pcall( L, 0, 0, 0) != LUA_OK) 300 if( lua_pcall( L, 0, 0, 0) != LUA_OK)
301 { 301 {
302 luaL_error( from_, "on_state_create failed: \"%s\"", lua_isstring( L, -1) ? lua_tostring( L, -1) : lua_typename( L, lua_type( L, -1))); 302 luaL_error( from_, "on_state_create failed: \"%s\"", lua_isstring( L, -1) ? lua_tostring( L, -1) : lua_typename( L, lua_type( L, -1)));
303 } 303 }
304 STACK_END( L, 0); 304 STACK_END( L, 0);
305 } 305 }
306} 306}
307 307
308/* 308/*
@@ -320,116 +320,116 @@ void call_on_state_create( Universe* U, lua_State* L, lua_State* from_, LookupMo
320*/ 320*/
321lua_State* luaG_newstate( Universe* U, lua_State* from_, char const* libs_) 321lua_State* luaG_newstate( Universe* U, lua_State* from_, char const* libs_)
322{ 322{
323 lua_State* L = create_state( U, from_); 323 lua_State* L = create_state( U, from_);
324 324
325 STACK_GROW( L, 2); 325 STACK_GROW( L, 2);
326 STACK_CHECK_ABS( L, 0); 326 STACK_CHECK_ABS( L, 0);
327 327
328 // copy the universe as a light userdata (only the master state holds the full userdata) 328 // copy the universe as a light userdata (only the master state holds the full userdata)
329 // that way, if Lanes is required in this new state, we'll know we are part of this universe 329 // that way, if Lanes is required in this new state, we'll know we are part of this universe
330 universe_store( L, U); 330 universe_store( L, U);
331 STACK_MID( L, 0); 331 STACK_MID( L, 0);
332 332
333 // we'll need this every time we transfer some C function from/to this state 333 // we'll need this every time we transfer some C function from/to this state
334 REGISTRY_SET( L, LOOKUP_REGKEY, lua_newtable( L)); 334 REGISTRY_SET( L, LOOKUP_REGKEY, lua_newtable( L));
335 STACK_MID( L, 0); 335 STACK_MID( L, 0);
336 336
337 // neither libs (not even 'base') nor special init func: we are done 337 // neither libs (not even 'base') nor special init func: we are done
338 if( libs_ == NULL && U->on_state_create_func == NULL) 338 if( libs_ == NULL && U->on_state_create_func == NULL)
339 { 339 {
340 DEBUGSPEW_CODE( fprintf( stderr, INDENT_BEGIN "luaG_newstate(NULL)\n" INDENT_END)); 340 DEBUGSPEW_CODE( fprintf( stderr, INDENT_BEGIN "luaG_newstate(NULL)\n" INDENT_END));
341 return L; 341 return L;
342 } 342 }
343 343
344 DEBUGSPEW_CODE( fprintf( stderr, INDENT_BEGIN "luaG_newstate()\n" INDENT_END)); 344 DEBUGSPEW_CODE( fprintf( stderr, INDENT_BEGIN "luaG_newstate()\n" INDENT_END));
345 DEBUGSPEW_CODE( ++ U->debugspew_indent_depth); 345 DEBUGSPEW_CODE( ++ U->debugspew_indent_depth);
346 346
347 // copy settings (for example because it may contain a Lua on_state_create function) 347 // copy settings (for example because it may contain a Lua on_state_create function)
348 copy_one_time_settings( U, from_, L); 348 copy_one_time_settings( U, from_, L);
349 349
350 // 'lua.c' stops GC during initialization so perhaps its a good idea. :) 350 // 'lua.c' stops GC during initialization so perhaps its a good idea. :)
351 lua_gc( L, LUA_GCSTOP, 0); 351 lua_gc( L, LUA_GCSTOP, 0);
352 352
353 353
354 // Anything causes 'base' to be taken in 354 // Anything causes 'base' to be taken in
355 // 355 //
356 if( libs_ != NULL) 356 if( libs_ != NULL)
357 { 357 {
358 // special "*" case (mainly to help with LuaJIT compatibility) 358 // special "*" case (mainly to help with LuaJIT compatibility)
359 // as we are called from luaopen_lanes_core() already, and that would deadlock 359 // as we are called from luaopen_lanes_core() already, and that would deadlock
360 if( libs_[0] == '*' && libs_[1] == 0) 360 if( libs_[0] == '*' && libs_[1] == 0)
361 { 361 {
362 DEBUGSPEW_CODE( fprintf( stderr, INDENT_BEGIN "opening ALL standard libraries\n" INDENT_END)); 362 DEBUGSPEW_CODE( fprintf( stderr, INDENT_BEGIN "opening ALL standard libraries\n" INDENT_END));
363 luaL_openlibs( L); 363 luaL_openlibs( L);
364 // don't forget lanes.core for regular lane states 364 // don't forget lanes.core for regular lane states
365 open1lib( DEBUGSPEW_PARAM_COMMA( U) L, "lanes.core", 10); 365 open1lib( DEBUGSPEW_PARAM_COMMA( U) L, "lanes.core", 10);
366 libs_ = NULL; // done with libs 366 libs_ = NULL; // done with libs
367 } 367 }
368 else 368 else
369 { 369 {
370 DEBUGSPEW_CODE( fprintf( stderr, INDENT_BEGIN "opening base library\n" INDENT_END)); 370 DEBUGSPEW_CODE( fprintf( stderr, INDENT_BEGIN "opening base library\n" INDENT_END));
371#if LUA_VERSION_NUM >= 502 371#if LUA_VERSION_NUM >= 502
372 // open base library the same way as in luaL_openlibs() 372 // open base library the same way as in luaL_openlibs()
373 luaL_requiref( L, "_G", luaopen_base, 1); 373 luaL_requiref( L, "_G", luaopen_base, 1);
374 lua_pop( L, 1); 374 lua_pop( L, 1);
375#else // LUA_VERSION_NUM 375#else // LUA_VERSION_NUM
376 lua_pushcfunction( L, luaopen_base); 376 lua_pushcfunction( L, luaopen_base);
377 lua_pushstring( L, ""); 377 lua_pushstring( L, "");
378 lua_call( L, 1, 0); 378 lua_call( L, 1, 0);
379#endif // LUA_VERSION_NUM 379#endif // LUA_VERSION_NUM
380 } 380 }
381 } 381 }
382 STACK_END( L, 0); 382 STACK_END( L, 0);
383 383
384 // scan all libraries, open them one by one 384 // scan all libraries, open them one by one
385 if( libs_) 385 if( libs_)
386 { 386 {
387 char const* p; 387 char const* p;
388 unsigned int len = 0; 388 unsigned int len = 0;
389 for( p = libs_; *p; p += len) 389 for( p = libs_; *p; p += len)
390 { 390 {
391 // skip delimiters ('.' can be part of name for "lanes.core") 391 // skip delimiters ('.' can be part of name for "lanes.core")
392 while( *p && !isalnum( *p) && *p != '.') 392 while( *p && !isalnum( *p) && *p != '.')
393 ++ p; 393 ++ p;
394 // skip name 394 // skip name
395 len = 0; 395 len = 0;
396 while( isalnum( p[len]) || p[len] == '.') 396 while( isalnum( p[len]) || p[len] == '.')
397 ++ len; 397 ++ len;
398 // open library 398 // open library
399 open1lib( DEBUGSPEW_PARAM_COMMA( U) L, p, len); 399 open1lib( DEBUGSPEW_PARAM_COMMA( U) L, p, len);
400 } 400 }
401 } 401 }
402 lua_gc( L, LUA_GCRESTART, 0); 402 lua_gc( L, LUA_GCRESTART, 0);
403 403
404 serialize_require( DEBUGSPEW_PARAM_COMMA( U) L); 404 serialize_require( DEBUGSPEW_PARAM_COMMA( U) L);
405 405
406 // call this after the base libraries are loaded and GC is restarted 406 // call this after the base libraries are loaded and GC is restarted
407 // will raise an error in from_ in case of problem 407 // will raise an error in from_ in case of problem
408 call_on_state_create( U, L, from_, eLM_LaneBody); 408 call_on_state_create( U, L, from_, eLM_LaneBody);
409 409
410 STACK_CHECK( L, 0); 410 STACK_CHECK( L, 0);
411 // after all this, register everything we find in our name<->function database 411 // after all this, register everything we find in our name<->function database
412 lua_pushglobaltable( L); // Lua 5.2 no longer has LUA_GLOBALSINDEX: we must push globals table on the stack 412 lua_pushglobaltable( L); // Lua 5.2 no longer has LUA_GLOBALSINDEX: we must push globals table on the stack
413 populate_func_lookup_table( L, -1, NULL); 413 populate_func_lookup_table( L, -1, NULL);
414 414
415#if 0 && USE_DEBUG_SPEW 415#if 0 && USE_DEBUG_SPEW
416 // dump the lookup database contents 416 // dump the lookup database contents
417 lua_getfield( L, LUA_REGISTRYINDEX, LOOKUP_REGKEY); // {} 417 lua_getfield( L, LUA_REGISTRYINDEX, LOOKUP_REGKEY); // {}
418 lua_pushnil( L); // {} nil 418 lua_pushnil( L); // {} nil
419 while( lua_next( L, -2)) // {} k v 419 while( lua_next( L, -2)) // {} k v
420 { 420 {
421 lua_getglobal( L, "print"); // {} k v print 421 lua_getglobal( L, "print"); // {} k v print
422 lua_pushlstring( L, debugspew_indent, U->debugspew_indent_depth); // {} k v print " " 422 lua_pushlstring( L, debugspew_indent, U->debugspew_indent_depth); // {} k v print " "
423 lua_pushvalue( L, -4); // {} k v print " " k 423 lua_pushvalue( L, -4); // {} k v print " " k
424 lua_pushvalue( L, -4); // {} k v print " " k v 424 lua_pushvalue( L, -4); // {} k v print " " k v
425 lua_call( L, 3, 0); // {} k v 425 lua_call( L, 3, 0); // {} k v
426 lua_pop( L, 1); // {} k 426 lua_pop( L, 1); // {} k
427 } 427 }
428 lua_pop( L, 1); // {} 428 lua_pop( L, 1); // {}
429#endif // USE_DEBUG_SPEW 429#endif // USE_DEBUG_SPEW
430 430
431 lua_pop( L, 1); 431 lua_pop( L, 1);
432 STACK_END( L, 0); 432 STACK_END( L, 0);
433 DEBUGSPEW_CODE( -- U->debugspew_indent_depth); 433 DEBUGSPEW_CODE( -- U->debugspew_indent_depth);
434 return L; 434 return L;
435} 435}
diff --git a/src/threading.c b/src/threading.c
index 84a6fcd..183dc87 100644
--- a/src/threading.c
+++ b/src/threading.c
@@ -104,16 +104,16 @@ THE SOFTWARE.
104static void FAIL( char const* funcname, int rc) 104static void FAIL( char const* funcname, int rc)
105{ 105{
106#if defined( PLATFORM_XBOX) 106#if defined( PLATFORM_XBOX)
107 fprintf( stderr, "%s() failed! (%d)\n", funcname, rc ); 107 fprintf( stderr, "%s() failed! (%d)\n", funcname, rc );
108#else // PLATFORM_XBOX 108#else // PLATFORM_XBOX
109 char buf[256]; 109 char buf[256];
110 FormatMessageA( FORMAT_MESSAGE_FROM_SYSTEM, NULL, rc, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), buf, 256, NULL); 110 FormatMessageA( FORMAT_MESSAGE_FROM_SYSTEM, NULL, rc, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), buf, 256, NULL);
111 fprintf( stderr, "%s() failed! [GetLastError() -> %d] '%s'", funcname, rc, buf); 111 fprintf( stderr, "%s() failed! [GetLastError() -> %d] '%s'", funcname, rc, buf);
112#endif // PLATFORM_XBOX 112#endif // PLATFORM_XBOX
113#ifdef _MSC_VER 113#ifdef _MSC_VER
114 __debugbreak(); // give a chance to the debugger! 114 __debugbreak(); // give a chance to the debugger!
115#endif // _MSC_VER 115#endif // _MSC_VER
116 abort(); 116 abort();
117} 117}
118#endif // win32 build 118#endif // win32 build
119 119
@@ -278,14 +278,14 @@ static void prepare_timeout( struct timespec *ts, time_d abs_secs ) {
278 if (!CloseHandle(*ref)) FAIL( "CloseHandle (mutex)", GetLastError() ); 278 if (!CloseHandle(*ref)) FAIL( "CloseHandle (mutex)", GetLastError() );
279 *ref= NULL; 279 *ref= NULL;
280 } 280 }
281 void MUTEX_LOCK( MUTEX_T *ref ) 281 void MUTEX_LOCK( MUTEX_T *ref )
282 { 282 {
283 DWORD rc = WaitForSingleObject( *ref, INFINITE); 283 DWORD rc = WaitForSingleObject( *ref, INFINITE);
284 // ERROR_WAIT_NO_CHILDREN means a thread was killed (lane terminated because of error raised during a linda transfer for example) while having grabbed this mutex 284 // ERROR_WAIT_NO_CHILDREN means a thread was killed (lane terminated because of error raised during a linda transfer for example) while having grabbed this mutex
285 // this is not a big problem as we will grab it just the same, so ignore this particular error 285 // this is not a big problem as we will grab it just the same, so ignore this particular error
286 if( rc != 0 && rc != ERROR_WAIT_NO_CHILDREN) 286 if( rc != 0 && rc != ERROR_WAIT_NO_CHILDREN)
287 FAIL( "WaitForSingleObject", (rc == WAIT_FAILED) ? GetLastError() : rc); 287 FAIL( "WaitForSingleObject", (rc == WAIT_FAILED) ? GetLastError() : rc);
288 } 288 }
289 void MUTEX_UNLOCK( MUTEX_T *ref ) { 289 void MUTEX_UNLOCK( MUTEX_T *ref ) {
290 if (!ReleaseMutex(*ref)) 290 if (!ReleaseMutex(*ref))
291 FAIL( "ReleaseMutex", GetLastError() ); 291 FAIL( "ReleaseMutex", GetLastError() );
@@ -294,13 +294,13 @@ static void prepare_timeout( struct timespec *ts, time_d abs_secs ) {
294 294
295static int const gs_prio_remap[] = 295static int const gs_prio_remap[] =
296{ 296{
297 THREAD_PRIORITY_IDLE, 297 THREAD_PRIORITY_IDLE,
298 THREAD_PRIORITY_LOWEST, 298 THREAD_PRIORITY_LOWEST,
299 THREAD_PRIORITY_BELOW_NORMAL, 299 THREAD_PRIORITY_BELOW_NORMAL,
300 THREAD_PRIORITY_NORMAL, 300 THREAD_PRIORITY_NORMAL,
301 THREAD_PRIORITY_ABOVE_NORMAL, 301 THREAD_PRIORITY_ABOVE_NORMAL,
302 THREAD_PRIORITY_HIGHEST, 302 THREAD_PRIORITY_HIGHEST,
303 THREAD_PRIORITY_TIME_CRITICAL 303 THREAD_PRIORITY_TIME_CRITICAL
304}; 304};
305 305
306/* MSDN: "If you would like to use the CRT in ThreadProc, use the 306/* MSDN: "If you would like to use the CRT in ThreadProc, use the
@@ -310,43 +310,43 @@ MSDN: "you can create at most 2028 threads"
310// Note: Visual C++ requires '__stdcall' where it is 310// Note: Visual C++ requires '__stdcall' where it is
311void THREAD_CREATE( THREAD_T* ref, THREAD_RETURN_T (__stdcall *func)( void*), void* data, int prio /* -3..+3 */) 311void THREAD_CREATE( THREAD_T* ref, THREAD_RETURN_T (__stdcall *func)( void*), void* data, int prio /* -3..+3 */)
312{ 312{
313 HANDLE h = (HANDLE) _beginthreadex( NULL, // security 313 HANDLE h = (HANDLE) _beginthreadex( NULL, // security
314 _THREAD_STACK_SIZE, 314 _THREAD_STACK_SIZE,
315 func, 315 func,
316 data, 316 data,
317 0, // flags (0/CREATE_SUSPENDED) 317 0, // flags (0/CREATE_SUSPENDED)
318 NULL // thread id (not used) 318 NULL // thread id (not used)
319 ); 319 );
320 320
321 if( h == NULL) // _beginthreadex returns 0L on failure instead of -1L (like _beginthread) 321 if( h == NULL) // _beginthreadex returns 0L on failure instead of -1L (like _beginthread)
322 { 322 {
323 FAIL( "CreateThread", GetLastError()); 323 FAIL( "CreateThread", GetLastError());
324 } 324 }
325 325
326 if (!SetThreadPriority( h, gs_prio_remap[prio + 3])) 326 if (!SetThreadPriority( h, gs_prio_remap[prio + 3]))
327 { 327 {
328 FAIL( "SetThreadPriority", GetLastError()); 328 FAIL( "SetThreadPriority", GetLastError());
329 } 329 }
330 330
331 *ref = h; 331 *ref = h;
332} 332}
333 333
334 334
335void THREAD_SET_PRIORITY( int prio) 335void THREAD_SET_PRIORITY( int prio)
336{ 336{
337 // prio range [-3,+3] was checked by the caller 337 // prio range [-3,+3] was checked by the caller
338 if (!SetThreadPriority( GetCurrentThread(), gs_prio_remap[prio + 3])) 338 if (!SetThreadPriority( GetCurrentThread(), gs_prio_remap[prio + 3]))
339 { 339 {
340 FAIL( "THREAD_SET_PRIORITY", GetLastError()); 340 FAIL( "THREAD_SET_PRIORITY", GetLastError());
341 } 341 }
342} 342}
343 343
344void THREAD_SET_AFFINITY( unsigned int aff) 344void THREAD_SET_AFFINITY( unsigned int aff)
345{ 345{
346 if( !SetThreadAffinityMask( GetCurrentThread(), aff)) 346 if( !SetThreadAffinityMask( GetCurrentThread(), aff))
347 { 347 {
348 FAIL( "THREAD_SET_AFFINITY", GetLastError()); 348 FAIL( "THREAD_SET_AFFINITY", GetLastError());
349 } 349 }
350} 350}
351 351
352bool_t THREAD_WAIT_IMPL( THREAD_T *ref, double secs) 352bool_t THREAD_WAIT_IMPL( THREAD_T *ref, double secs)
@@ -366,200 +366,200 @@ bool_t THREAD_WAIT_IMPL( THREAD_T *ref, double secs)
366 return TRUE; 366 return TRUE;
367 } 367 }
368 // 368 //
369 void THREAD_KILL( THREAD_T *ref ) 369 void THREAD_KILL( THREAD_T *ref )
370 { 370 {
371 // nonexistent on Xbox360, simply disable until a better solution is found 371 // nonexistent on Xbox360, simply disable until a better solution is found
372 #if !defined( PLATFORM_XBOX) 372 #if !defined( PLATFORM_XBOX)
373 // in theory no-one should call this as it is very dangerous (memory and mutex leaks, no notification of DLLs, etc.) 373 // in theory no-one should call this as it is very dangerous (memory and mutex leaks, no notification of DLLs, etc.)
374 if (!TerminateThread( *ref, 0 )) FAIL("TerminateThread", GetLastError()); 374 if (!TerminateThread( *ref, 0 )) FAIL("TerminateThread", GetLastError());
375 #endif // PLATFORM_XBOX 375 #endif // PLATFORM_XBOX
376 *ref= NULL; 376 *ref= NULL;
377 } 377 }
378 378
379 void THREAD_MAKE_ASYNCH_CANCELLABLE() {} // nothing to do for windows threads, we can cancel them anytime we want 379 void THREAD_MAKE_ASYNCH_CANCELLABLE() {} // nothing to do for windows threads, we can cancel them anytime we want
380 380
381#if !defined __GNUC__ 381#if !defined __GNUC__
382 //see http://msdn.microsoft.com/en-us/library/xcb2z8hs.aspx 382 //see http://msdn.microsoft.com/en-us/library/xcb2z8hs.aspx
383 #define MS_VC_EXCEPTION 0x406D1388 383 #define MS_VC_EXCEPTION 0x406D1388
384 #pragma pack(push,8) 384 #pragma pack(push,8)
385 typedef struct tagTHREADNAME_INFO 385 typedef struct tagTHREADNAME_INFO
386 { 386 {
387 DWORD dwType; // Must be 0x1000. 387 DWORD dwType; // Must be 0x1000.
388 LPCSTR szName; // Pointer to name (in user addr space). 388 LPCSTR szName; // Pointer to name (in user addr space).
389 DWORD dwThreadID; // Thread ID (-1=caller thread). 389 DWORD dwThreadID; // Thread ID (-1=caller thread).
390 DWORD dwFlags; // Reserved for future use, must be zero. 390 DWORD dwFlags; // Reserved for future use, must be zero.
391 } THREADNAME_INFO; 391 } THREADNAME_INFO;
392 #pragma pack(pop) 392 #pragma pack(pop)
393#endif // !__GNUC__ 393#endif // !__GNUC__
394 394
395 void THREAD_SETNAME( char const* _name) 395 void THREAD_SETNAME( char const* _name)
396 { 396 {
397#if !defined __GNUC__ 397#if !defined __GNUC__
398 THREADNAME_INFO info; 398 THREADNAME_INFO info;
399 info.dwType = 0x1000; 399 info.dwType = 0x1000;
400 info.szName = _name; 400 info.szName = _name;
401 info.dwThreadID = GetCurrentThreadId(); 401 info.dwThreadID = GetCurrentThreadId();
402 info.dwFlags = 0; 402 info.dwFlags = 0;
403 403
404 __try 404 __try
405 { 405 {
406 RaiseException( MS_VC_EXCEPTION, 0, sizeof(info)/sizeof(ULONG_PTR), (ULONG_PTR*)&info ); 406 RaiseException( MS_VC_EXCEPTION, 0, sizeof(info)/sizeof(ULONG_PTR), (ULONG_PTR*)&info );
407 } 407 }
408 __except(EXCEPTION_EXECUTE_HANDLER) 408 __except(EXCEPTION_EXECUTE_HANDLER)
409 { 409 {
410 } 410 }
411#endif // !__GNUC__ 411#endif // !__GNUC__
412 } 412 }
413 413
414#if _WIN32_WINNT < 0x0600 // CONDITION_VARIABLE aren't available 414#if _WIN32_WINNT < 0x0600 // CONDITION_VARIABLE aren't available
415 415
416 void SIGNAL_INIT( SIGNAL_T* ref) 416 void SIGNAL_INIT( SIGNAL_T* ref)
417 { 417 {
418 InitializeCriticalSection( &ref->signalCS); 418 InitializeCriticalSection( &ref->signalCS);
419 InitializeCriticalSection( &ref->countCS); 419 InitializeCriticalSection( &ref->countCS);
420 if( 0 == (ref->waitEvent = CreateEvent( 0, TRUE, FALSE, 0))) // manual-reset 420 if( 0 == (ref->waitEvent = CreateEvent( 0, TRUE, FALSE, 0))) // manual-reset
421 FAIL( "CreateEvent", GetLastError()); 421 FAIL( "CreateEvent", GetLastError());
422 if( 0 == (ref->waitDoneEvent = CreateEvent( 0, FALSE, FALSE, 0))) // auto-reset 422 if( 0 == (ref->waitDoneEvent = CreateEvent( 0, FALSE, FALSE, 0))) // auto-reset
423 FAIL( "CreateEvent", GetLastError()); 423 FAIL( "CreateEvent", GetLastError());
424 ref->waitersCount = 0; 424 ref->waitersCount = 0;
425 } 425 }
426 426
427 void SIGNAL_FREE( SIGNAL_T* ref) 427 void SIGNAL_FREE( SIGNAL_T* ref)
428 { 428 {
429 CloseHandle( ref->waitDoneEvent); 429 CloseHandle( ref->waitDoneEvent);
430 CloseHandle( ref->waitEvent); 430 CloseHandle( ref->waitEvent);
431 DeleteCriticalSection( &ref->countCS); 431 DeleteCriticalSection( &ref->countCS);
432 DeleteCriticalSection( &ref->signalCS); 432 DeleteCriticalSection( &ref->signalCS);
433 } 433 }
434 434
435 bool_t SIGNAL_WAIT( SIGNAL_T* ref, MUTEX_T* mu_ref, time_d abs_secs) 435 bool_t SIGNAL_WAIT( SIGNAL_T* ref, MUTEX_T* mu_ref, time_d abs_secs)
436 { 436 {
437 DWORD errc; 437 DWORD errc;
438 DWORD ms; 438 DWORD ms;
439 439
440 if( abs_secs < 0.0) 440 if( abs_secs < 0.0)
441 ms = INFINITE; 441 ms = INFINITE;
442 else if( abs_secs == 0.0) 442 else if( abs_secs == 0.0)
443 ms = 0; 443 ms = 0;
444 else 444 else
445 { 445 {
446 time_d msd = (abs_secs - now_secs()) * 1000.0 + 0.5; 446 time_d msd = (abs_secs - now_secs()) * 1000.0 + 0.5;
447 // If the time already passed, still try once (ms==0). A short timeout 447 // If the time already passed, still try once (ms==0). A short timeout
448 // may have turned negative or 0 because of the two time samples done. 448 // may have turned negative or 0 because of the two time samples done.
449 ms = msd <= 0.0 ? 0 : (DWORD)msd; 449 ms = msd <= 0.0 ? 0 : (DWORD)msd;
450 } 450 }
451 451
452 EnterCriticalSection( &ref->signalCS); 452 EnterCriticalSection( &ref->signalCS);
453 EnterCriticalSection( &ref->countCS); 453 EnterCriticalSection( &ref->countCS);
454 ++ ref->waitersCount; 454 ++ ref->waitersCount;
455 LeaveCriticalSection( &ref->countCS); 455 LeaveCriticalSection( &ref->countCS);
456 LeaveCriticalSection( &ref->signalCS); 456 LeaveCriticalSection( &ref->signalCS);
457 457
458 errc = SignalObjectAndWait( *mu_ref, ref->waitEvent, ms, FALSE); 458 errc = SignalObjectAndWait( *mu_ref, ref->waitEvent, ms, FALSE);
459 459
460 EnterCriticalSection( &ref->countCS); 460 EnterCriticalSection( &ref->countCS);
461 if( 0 == -- ref->waitersCount) 461 if( 0 == -- ref->waitersCount)
462 { 462 {
463 // we're the last one leaving... 463 // we're the last one leaving...
464 ResetEvent( ref->waitEvent); 464 ResetEvent( ref->waitEvent);
465 SetEvent( ref->waitDoneEvent); 465 SetEvent( ref->waitDoneEvent);
466 } 466 }
467 LeaveCriticalSection( &ref->countCS); 467 LeaveCriticalSection( &ref->countCS);
468 MUTEX_LOCK( mu_ref); 468 MUTEX_LOCK( mu_ref);
469 469
470 switch( errc) 470 switch( errc)
471 { 471 {
472 case WAIT_TIMEOUT: 472 case WAIT_TIMEOUT:
473 return FALSE; 473 return FALSE;
474 case WAIT_OBJECT_0: 474 case WAIT_OBJECT_0:
475 return TRUE; 475 return TRUE;
476 } 476 }
477 477
478 FAIL( "SignalObjectAndWait", GetLastError()); 478 FAIL( "SignalObjectAndWait", GetLastError());
479 return FALSE; 479 return FALSE;
480 } 480 }
481 481
482 void SIGNAL_ALL( SIGNAL_T* ref) 482 void SIGNAL_ALL( SIGNAL_T* ref)
483 { 483 {
484 DWORD errc = WAIT_OBJECT_0; 484 DWORD errc = WAIT_OBJECT_0;
485 485
486 EnterCriticalSection( &ref->signalCS); 486 EnterCriticalSection( &ref->signalCS);
487 EnterCriticalSection( &ref->countCS); 487 EnterCriticalSection( &ref->countCS);
488 488
489 if( ref->waitersCount > 0) 489 if( ref->waitersCount > 0)
490 { 490 {
491 ResetEvent( ref->waitDoneEvent); 491 ResetEvent( ref->waitDoneEvent);
492 SetEvent( ref->waitEvent); 492 SetEvent( ref->waitEvent);
493 LeaveCriticalSection( &ref->countCS); 493 LeaveCriticalSection( &ref->countCS);
494 errc = WaitForSingleObject( ref->waitDoneEvent, INFINITE); 494 errc = WaitForSingleObject( ref->waitDoneEvent, INFINITE);
495 } 495 }
496 else 496 else
497 { 497 {
498 LeaveCriticalSection( &ref->countCS); 498 LeaveCriticalSection( &ref->countCS);
499 } 499 }
500 500
501 LeaveCriticalSection( &ref->signalCS); 501 LeaveCriticalSection( &ref->signalCS);
502 502
503 if( WAIT_OBJECT_0 != errc) 503 if( WAIT_OBJECT_0 != errc)
504 FAIL( "WaitForSingleObject", GetLastError()); 504 FAIL( "WaitForSingleObject", GetLastError());
505 } 505 }
506 506
507#else // CONDITION_VARIABLE are available, use them 507#else // CONDITION_VARIABLE are available, use them
508 508
509 // 509 //
510 void SIGNAL_INIT( SIGNAL_T *ref ) 510 void SIGNAL_INIT( SIGNAL_T *ref )
511 { 511 {
512 InitializeConditionVariable( ref); 512 InitializeConditionVariable( ref);
513 } 513 }
514 514
515 void SIGNAL_FREE( SIGNAL_T *ref ) 515 void SIGNAL_FREE( SIGNAL_T *ref )
516 { 516 {
517 // nothing to do 517 // nothing to do
518 (void)ref; 518 (void)ref;
519 } 519 }
520 520
521 bool_t SIGNAL_WAIT( SIGNAL_T *ref, MUTEX_T *mu_ref, time_d abs_secs) 521 bool_t SIGNAL_WAIT( SIGNAL_T *ref, MUTEX_T *mu_ref, time_d abs_secs)
522 { 522 {
523 long ms; 523 long ms;
524 524
525 if( abs_secs < 0.0) 525 if( abs_secs < 0.0)
526 ms = INFINITE; 526 ms = INFINITE;
527 else if( abs_secs == 0.0) 527 else if( abs_secs == 0.0)
528 ms = 0; 528 ms = 0;
529 else 529 else
530 { 530 {
531 ms = (long) ((abs_secs - now_secs())*1000.0 + 0.5); 531 ms = (long) ((abs_secs - now_secs())*1000.0 + 0.5);
532 532
533 // If the time already passed, still try once (ms==0). A short timeout 533 // If the time already passed, still try once (ms==0). A short timeout
534 // may have turned negative or 0 because of the two time samples done. 534 // may have turned negative or 0 because of the two time samples done.
535 // 535 //
536 if( ms < 0) 536 if( ms < 0)
537 ms = 0; 537 ms = 0;
538 } 538 }
539 539
540 if( !SleepConditionVariableCS( ref, mu_ref, ms)) 540 if( !SleepConditionVariableCS( ref, mu_ref, ms))
541 { 541 {
542 if( GetLastError() == ERROR_TIMEOUT) 542 if( GetLastError() == ERROR_TIMEOUT)
543 { 543 {
544 return FALSE; 544 return FALSE;
545 } 545 }
546 else 546 else
547 { 547 {
548 FAIL( "SleepConditionVariableCS", GetLastError()); 548 FAIL( "SleepConditionVariableCS", GetLastError());
549 } 549 }
550 } 550 }
551 return TRUE; 551 return TRUE;
552 } 552 }
553 553
554 void SIGNAL_ONE( SIGNAL_T *ref ) 554 void SIGNAL_ONE( SIGNAL_T *ref )
555 { 555 {
556 WakeConditionVariable( ref); 556 WakeConditionVariable( ref);
557 } 557 }
558 558
559 void SIGNAL_ALL( SIGNAL_T *ref ) 559 void SIGNAL_ALL( SIGNAL_T *ref )
560 { 560 {
561 WakeAllConditionVariable( ref); 561 WakeAllConditionVariable( ref);
562 } 562 }
563 563
564#endif // CONDITION_VARIABLE are available 564#endif // CONDITION_VARIABLE are available
565 565
@@ -574,20 +574,20 @@ bool_t THREAD_WAIT_IMPL( THREAD_T *ref, double secs)
574 574
575# if (defined(__MINGW32__) || defined(__MINGW64__)) && defined pthread_attr_setschedpolicy 575# if (defined(__MINGW32__) || defined(__MINGW64__)) && defined pthread_attr_setschedpolicy
576# if pthread_attr_setschedpolicy( A, S) == ENOTSUP 576# if pthread_attr_setschedpolicy( A, S) == ENOTSUP
577 // from the mingw-w64 team: 577 // from the mingw-w64 team:
578 // Well, we support pthread_setschedparam by which you can specify 578 // Well, we support pthread_setschedparam by which you can specify
579 // threading-policy. Nevertheless, yes we lack this function. In 579 // threading-policy. Nevertheless, yes we lack this function. In
580 // general its implementation is pretty much trivial, as on Win32 target 580 // general its implementation is pretty much trivial, as on Win32 target
581 // just SCHED_OTHER can be supported. 581 // just SCHED_OTHER can be supported.
582 #undef pthread_attr_setschedpolicy 582 #undef pthread_attr_setschedpolicy
583 static int pthread_attr_setschedpolicy( pthread_attr_t* attr, int policy) 583 static int pthread_attr_setschedpolicy( pthread_attr_t* attr, int policy)
584 { 584 {
585 if( policy != SCHED_OTHER) 585 if( policy != SCHED_OTHER)
586 { 586 {
587 return ENOTSUP; 587 return ENOTSUP;
588 } 588 }
589 return 0; 589 return 0;
590 } 590 }
591# endif // pthread_attr_setschedpolicy() 591# endif // pthread_attr_setschedpolicy()
592# endif // defined(__MINGW32__) || defined(__MINGW64__) 592# endif // defined(__MINGW32__) || defined(__MINGW64__)
593 593
@@ -646,94 +646,94 @@ bool_t THREAD_WAIT_IMPL( THREAD_T *ref, double secs)
646// array of 7 thread priority values, hand-tuned by platform so that we offer a uniform [-3,+3] public priority range 646// array of 7 thread priority values, hand-tuned by platform so that we offer a uniform [-3,+3] public priority range
647static int const gs_prio_remap[] = 647static int const gs_prio_remap[] =
648{ 648{
649 // NB: PThreads priority handling is about as twisty as one can get it 649 // NB: PThreads priority handling is about as twisty as one can get it
650 // (and then some). DON*T TRUST ANYTHING YOU READ ON THE NET!!! 650 // (and then some). DON*T TRUST ANYTHING YOU READ ON THE NET!!!
651 651
652 //--- 652 //---
653 // "Select the scheduling policy for the thread: one of SCHED_OTHER 653 // "Select the scheduling policy for the thread: one of SCHED_OTHER
654 // (regular, non-real-time scheduling), SCHED_RR (real-time, 654 // (regular, non-real-time scheduling), SCHED_RR (real-time,
655 // round-robin) or SCHED_FIFO (real-time, first-in first-out)." 655 // round-robin) or SCHED_FIFO (real-time, first-in first-out)."
656 // 656 //
657 // "Using the RR policy ensures that all threads having the same 657 // "Using the RR policy ensures that all threads having the same
658 // priority level will be scheduled equally, regardless of their activity." 658 // priority level will be scheduled equally, regardless of their activity."
659 // 659 //
660 // "For SCHED_FIFO and SCHED_RR, the only required member of the 660 // "For SCHED_FIFO and SCHED_RR, the only required member of the
661 // sched_param structure is the priority sched_priority. For SCHED_OTHER, 661 // sched_param structure is the priority sched_priority. For SCHED_OTHER,
662 // the affected scheduling parameters are implementation-defined." 662 // the affected scheduling parameters are implementation-defined."
663 // 663 //
664 // "The priority of a thread is specified as a delta which is added to 664 // "The priority of a thread is specified as a delta which is added to
665 // the priority of the process." 665 // the priority of the process."
666 // 666 //
667 // ".. priority is an integer value, in the range from 1 to 127. 667 // ".. priority is an integer value, in the range from 1 to 127.
668 // 1 is the least-favored priority, 127 is the most-favored." 668 // 1 is the least-favored priority, 127 is the most-favored."
669 // 669 //
670 // "Priority level 0 cannot be used: it is reserved for the system." 670 // "Priority level 0 cannot be used: it is reserved for the system."
671 // 671 //
672 // "When you use specify a priority of -99 in a call to 672 // "When you use specify a priority of -99 in a call to
673 // pthread_setschedparam(), the priority of the target thread is 673 // pthread_setschedparam(), the priority of the target thread is
674 // lowered to the lowest possible value." 674 // lowered to the lowest possible value."
675 // 675 //
676 // ... 676 // ...
677 677
678 // ** CONCLUSION ** 678 // ** CONCLUSION **
679 // 679 //
680 // PThread priorities are _hugely_ system specific, and we need at 680 // PThread priorities are _hugely_ system specific, and we need at
681 // least OS specific settings. Hopefully, Linuxes and OS X versions 681 // least OS specific settings. Hopefully, Linuxes and OS X versions
682 // are uniform enough, among each other... 682 // are uniform enough, among each other...
683 // 683 //
684# if defined PLATFORM_OSX 684# if defined PLATFORM_OSX
685 // AK 10-Apr-07 (OS X PowerPC 10.4.9): 685 // AK 10-Apr-07 (OS X PowerPC 10.4.9):
686 // 686 //
687 // With SCHED_RR, 26 seems to be the "normal" priority, where setting 687 // With SCHED_RR, 26 seems to be the "normal" priority, where setting
688 // it does not seem to affect the order of threads processed. 688 // it does not seem to affect the order of threads processed.
689 // 689 //
690 // With SCHED_OTHER, the range 25..32 is normal (maybe the same 26, 690 // With SCHED_OTHER, the range 25..32 is normal (maybe the same 26,
691 // but the difference is not so clear with OTHER). 691 // but the difference is not so clear with OTHER).
692 // 692 //
693 // 'sched_get_priority_min()' and '..max()' give 15, 47 as the 693 // 'sched_get_priority_min()' and '..max()' give 15, 47 as the
694 // priority limits. This could imply, user mode applications won't 694 // priority limits. This could imply, user mode applications won't
695 // be able to use values outside of that range. 695 // be able to use values outside of that range.
696 // 696 //
697# define _PRIO_MODE SCHED_OTHER 697# define _PRIO_MODE SCHED_OTHER
698 698
699 // OS X 10.4.9 (PowerPC) gives ENOTSUP for process scope 699 // OS X 10.4.9 (PowerPC) gives ENOTSUP for process scope
700 //#define _PRIO_SCOPE PTHREAD_SCOPE_PROCESS 700 //#define _PRIO_SCOPE PTHREAD_SCOPE_PROCESS
701 701
702# define _PRIO_HI 32 // seems to work (_carefully_ picked!) 702# define _PRIO_HI 32 // seems to work (_carefully_ picked!)
703# define _PRIO_0 26 // detected 703# define _PRIO_0 26 // detected
704# define _PRIO_LO 1 // seems to work (tested) 704# define _PRIO_LO 1 // seems to work (tested)
705 705
706# elif defined PLATFORM_LINUX 706# elif defined PLATFORM_LINUX
707 // (based on Ubuntu Linux 2.6.15 kernel) 707 // (based on Ubuntu Linux 2.6.15 kernel)
708 // 708 //
709 // SCHED_OTHER is the default policy, but does not allow for priorities. 709 // SCHED_OTHER is the default policy, but does not allow for priorities.
710 // SCHED_RR allows priorities, all of which (1..99) are higher than 710 // SCHED_RR allows priorities, all of which (1..99) are higher than
711 // a thread with SCHED_OTHER policy. 711 // a thread with SCHED_OTHER policy.
712 // 712 //
713 // <http://kerneltrap.org/node/6080> 713 // <http://kerneltrap.org/node/6080>
714 // <http://en.wikipedia.org/wiki/Native_POSIX_Thread_Library> 714 // <http://en.wikipedia.org/wiki/Native_POSIX_Thread_Library>
715 // <http://www.net.in.tum.de/~gregor/docs/pthread-scheduling.html> 715 // <http://www.net.in.tum.de/~gregor/docs/pthread-scheduling.html>
716 // 716 //
717 // Manuals suggest checking #ifdef _POSIX_THREAD_PRIORITY_SCHEDULING, 717 // Manuals suggest checking #ifdef _POSIX_THREAD_PRIORITY_SCHEDULING,
718 // but even Ubuntu does not seem to define it. 718 // but even Ubuntu does not seem to define it.
719 // 719 //
720# define _PRIO_MODE SCHED_RR 720# define _PRIO_MODE SCHED_RR
721 721
722 // NTLP 2.5: only system scope allowed (being the basic reason why 722 // NTLP 2.5: only system scope allowed (being the basic reason why
723 // root privileges are required..) 723 // root privileges are required..)
724 //#define _PRIO_SCOPE PTHREAD_SCOPE_PROCESS 724 //#define _PRIO_SCOPE PTHREAD_SCOPE_PROCESS
725 725
726# define _PRIO_HI 99 726# define _PRIO_HI 99
727# define _PRIO_0 50 727# define _PRIO_0 50
728# define _PRIO_LO 1 728# define _PRIO_LO 1
729 729
730# elif defined(PLATFORM_BSD) 730# elif defined(PLATFORM_BSD)
731 // 731 //
732 // <http://www.net.in.tum.de/~gregor/docs/pthread-scheduling.html> 732 // <http://www.net.in.tum.de/~gregor/docs/pthread-scheduling.html>
733 // 733 //
734 // "When control over the thread scheduling is desired, then FreeBSD 734 // "When control over the thread scheduling is desired, then FreeBSD
735 // with the libpthread implementation is by far the best choice .." 735 // with the libpthread implementation is by far the best choice .."
736 // 736 //
737# define _PRIO_MODE SCHED_OTHER 737# define _PRIO_MODE SCHED_OTHER
738# define _PRIO_SCOPE PTHREAD_SCOPE_PROCESS 738# define _PRIO_SCOPE PTHREAD_SCOPE_PROCESS
739# define _PRIO_HI 31 739# define _PRIO_HI 31
@@ -741,16 +741,16 @@ static int const gs_prio_remap[] =
741# define _PRIO_LO 1 741# define _PRIO_LO 1
742 742
743# elif defined(PLATFORM_CYGWIN) 743# elif defined(PLATFORM_CYGWIN)
744 // 744 //
745 // TBD: Find right values for Cygwin 745 // TBD: Find right values for Cygwin
746 // 746 //
747# elif defined( PLATFORM_WIN32) || defined( PLATFORM_POCKETPC) 747# elif defined( PLATFORM_WIN32) || defined( PLATFORM_POCKETPC)
748 // any other value not supported by win32-pthread as of version 2.9.1 748 // any other value not supported by win32-pthread as of version 2.9.1
749# define _PRIO_MODE SCHED_OTHER 749# define _PRIO_MODE SCHED_OTHER
750 750
751 // PTHREAD_SCOPE_PROCESS not supported by win32-pthread as of version 2.9.1 751 // PTHREAD_SCOPE_PROCESS not supported by win32-pthread as of version 2.9.1
752 //#define _PRIO_SCOPE PTHREAD_SCOPE_SYSTEM // but do we need this at all to start with? 752 //#define _PRIO_SCOPE PTHREAD_SCOPE_SYSTEM // but do we need this at all to start with?
753 THREAD_PRIORITY_IDLE, THREAD_PRIORITY_LOWEST, THREAD_PRIORITY_BELOW_NORMAL, THREAD_PRIORITY_NORMAL, THREAD_PRIORITY_ABOVE_NORMAL, THREAD_PRIORITY_HIGHEST, THREAD_PRIORITY_TIME_CRITICAL 753 THREAD_PRIORITY_IDLE, THREAD_PRIORITY_LOWEST, THREAD_PRIORITY_BELOW_NORMAL, THREAD_PRIORITY_NORMAL, THREAD_PRIORITY_ABOVE_NORMAL, THREAD_PRIORITY_HIGHEST, THREAD_PRIORITY_TIME_CRITICAL
754 754
755# else 755# else
756# error "Unknown OS: not implemented!" 756# error "Unknown OS: not implemented!"
@@ -760,163 +760,163 @@ static int const gs_prio_remap[] =
760# define _PRIO_AN (_PRIO_0 + ((_PRIO_HI-_PRIO_0)/2)) 760# define _PRIO_AN (_PRIO_0 + ((_PRIO_HI-_PRIO_0)/2))
761# define _PRIO_BN (_PRIO_LO + ((_PRIO_0-_PRIO_LO)/2)) 761# define _PRIO_BN (_PRIO_LO + ((_PRIO_0-_PRIO_LO)/2))
762 762
763 _PRIO_LO, _PRIO_LO, _PRIO_BN, _PRIO_0, _PRIO_AN, _PRIO_HI, _PRIO_HI 763 _PRIO_LO, _PRIO_LO, _PRIO_BN, _PRIO_0, _PRIO_AN, _PRIO_HI, _PRIO_HI
764#endif // _PRIO_0 764#endif // _PRIO_0
765}; 765};
766 766
767void THREAD_CREATE( THREAD_T* ref, THREAD_RETURN_T (*func)( void*), void* data, int prio /* -3..+3 */) 767void THREAD_CREATE( THREAD_T* ref, THREAD_RETURN_T (*func)( void*), void* data, int prio /* -3..+3 */)
768{ 768{
769 pthread_attr_t a; 769 pthread_attr_t a;
770 bool_t const normal = 770 bool_t const normal =
771#if defined(PLATFORM_LINUX) && defined(LINUX_SCHED_RR) 771#if defined(PLATFORM_LINUX) && defined(LINUX_SCHED_RR)
772 !sudo; // with sudo, even normal thread must use SCHED_RR 772 !sudo; // with sudo, even normal thread must use SCHED_RR
773#else 773#else
774 (prio == 0); 774 (prio == 0);
775#endif 775#endif
776 776
777 PT_CALL( pthread_attr_init( &a)); 777 PT_CALL( pthread_attr_init( &a));
778 778
779#ifndef PTHREAD_TIMEDJOIN 779#ifndef PTHREAD_TIMEDJOIN
780 // We create a NON-JOINABLE thread. This is mainly due to the lack of 780 // We create a NON-JOINABLE thread. This is mainly due to the lack of
781 // 'pthread_timedjoin()', but does offer other benefits (s.a. earlier 781 // 'pthread_timedjoin()', but does offer other benefits (s.a. earlier
782 // freeing of the thread's resources). 782 // freeing of the thread's resources).
783 // 783 //
784 PT_CALL( pthread_attr_setdetachstate( &a, PTHREAD_CREATE_DETACHED)); 784 PT_CALL( pthread_attr_setdetachstate( &a, PTHREAD_CREATE_DETACHED));
785#endif // PTHREAD_TIMEDJOIN 785#endif // PTHREAD_TIMEDJOIN
786 786
787 // Use this to find a system's default stack size (DEBUG) 787 // Use this to find a system's default stack size (DEBUG)
788#if 0 788#if 0
789 { 789 {
790 size_t n; 790 size_t n;
791 pthread_attr_getstacksize( &a, &n); 791 pthread_attr_getstacksize( &a, &n);
792 fprintf( stderr, "Getstack: %u\n", (unsigned int)n); 792 fprintf( stderr, "Getstack: %u\n", (unsigned int)n);
793 } 793 }
794 // 524288 on OS X 794 // 524288 on OS X
795 // 2097152 on Linux x86 (Ubuntu 7.04) 795 // 2097152 on Linux x86 (Ubuntu 7.04)
796 // 1048576 on FreeBSD 6.2 SMP i386 796 // 1048576 on FreeBSD 6.2 SMP i386
797#endif // 0 797#endif // 0
798 798
799#if defined _THREAD_STACK_SIZE && _THREAD_STACK_SIZE > 0 799#if defined _THREAD_STACK_SIZE && _THREAD_STACK_SIZE > 0
800 PT_CALL( pthread_attr_setstacksize( &a, _THREAD_STACK_SIZE)); 800 PT_CALL( pthread_attr_setstacksize( &a, _THREAD_STACK_SIZE));
801#endif 801#endif
802 802
803 if( !normal) 803 if( !normal)
804 { 804 {
805 struct sched_param sp; 805 struct sched_param sp;
806 // "The specified scheduling parameters are only used if the scheduling 806 // "The specified scheduling parameters are only used if the scheduling
807 // parameter inheritance attribute is PTHREAD_EXPLICIT_SCHED." 807 // parameter inheritance attribute is PTHREAD_EXPLICIT_SCHED."
808 // 808 //
809#if !defined __ANDROID__ || ( defined __ANDROID__ && __ANDROID_API__ >= 28 ) 809#if !defined __ANDROID__ || ( defined __ANDROID__ && __ANDROID_API__ >= 28 )
810 PT_CALL( pthread_attr_setinheritsched( &a, PTHREAD_EXPLICIT_SCHED)); 810 PT_CALL( pthread_attr_setinheritsched( &a, PTHREAD_EXPLICIT_SCHED));
811#endif 811#endif
812 812
813#ifdef _PRIO_SCOPE 813#ifdef _PRIO_SCOPE
814 PT_CALL( pthread_attr_setscope( &a, _PRIO_SCOPE)); 814 PT_CALL( pthread_attr_setscope( &a, _PRIO_SCOPE));
815#endif // _PRIO_SCOPE 815#endif // _PRIO_SCOPE
816 816
817 PT_CALL( pthread_attr_setschedpolicy( &a, _PRIO_MODE)); 817 PT_CALL( pthread_attr_setschedpolicy( &a, _PRIO_MODE));
818 818
819 // prio range [-3,+3] was checked by the caller 819 // prio range [-3,+3] was checked by the caller
820 sp.sched_priority = gs_prio_remap[ prio + 3]; 820 sp.sched_priority = gs_prio_remap[ prio + 3];
821 PT_CALL( pthread_attr_setschedparam( &a, &sp)); 821 PT_CALL( pthread_attr_setschedparam( &a, &sp));
822 } 822 }
823 823
824 //--- 824 //---
825 // Seems on OS X, _POSIX_THREAD_THREADS_MAX is some kind of system 825 // Seems on OS X, _POSIX_THREAD_THREADS_MAX is some kind of system
826 // thread limit (not userland thread). Actual limit for us is way higher. 826 // thread limit (not userland thread). Actual limit for us is way higher.
827 // PTHREAD_THREADS_MAX is not defined (even though man page refers to it!) 827 // PTHREAD_THREADS_MAX is not defined (even though man page refers to it!)
828 // 828 //
829# ifndef THREAD_CREATE_RETRIES_MAX 829# ifndef THREAD_CREATE_RETRIES_MAX
830 // Don't bother with retries; a failure is a failure 830 // Don't bother with retries; a failure is a failure
831 // 831 //
832 { 832 {
833 int rc = pthread_create( ref, &a, func, data); 833 int rc = pthread_create( ref, &a, func, data);
834 if( rc) _PT_FAIL( rc, "pthread_create()", __FILE__, __LINE__ - 1); 834 if( rc) _PT_FAIL( rc, "pthread_create()", __FILE__, __LINE__ - 1);
835 } 835 }
836# else 836# else
837# error "This code deprecated" 837# error "This code deprecated"
838 /* 838 /*
839 // Wait slightly if thread creation has exchausted the system 839 // Wait slightly if thread creation has exchausted the system
840 // 840 //
841 { uint_t retries; 841 { uint_t retries;
842 for( retries=0; retries<THREAD_CREATE_RETRIES_MAX; retries++ ) { 842 for( retries=0; retries<THREAD_CREATE_RETRIES_MAX; retries++ ) {
843 843
844 int rc= pthread_create( ref, &a, func, data ); 844 int rc= pthread_create( ref, &a, func, data );
845 // 845 //
846 // OS X / Linux: 846 // OS X / Linux:
847 // EAGAIN: ".. lacked the necessary resources to create 847 // EAGAIN: ".. lacked the necessary resources to create
848 // another thread, or the system-imposed limit on the 848 // another thread, or the system-imposed limit on the
849 // total number of threads in a process 849 // total number of threads in a process
850 // [PTHREAD_THREADS_MAX] would be exceeded." 850 // [PTHREAD_THREADS_MAX] would be exceeded."
851 // EINVAL: attr is invalid 851 // EINVAL: attr is invalid
852 // Linux: 852 // Linux:
853 // EPERM: no rights for given parameters or scheduling (no sudo) 853 // EPERM: no rights for given parameters or scheduling (no sudo)
854 // ENOMEM: (known to fail with this code, too - not listed in man) 854 // ENOMEM: (known to fail with this code, too - not listed in man)
855 855
856 if (rc==0) break; // ok! 856 if (rc==0) break; // ok!
857 857
858 // In practise, exhaustion seems to be coming from memory, not a 858 // In practise, exhaustion seems to be coming from memory, not a
859 // maximum number of threads. Keep tuning... ;) 859 // maximum number of threads. Keep tuning... ;)
860 // 860 //
861 if (rc==EAGAIN) { 861 if (rc==EAGAIN) {
862 //fprintf( stderr, "Looping (retries=%d) ", retries ); // DEBUG 862 //fprintf( stderr, "Looping (retries=%d) ", retries ); // DEBUG
863 863
864 // Try again, later. 864 // Try again, later.
865 865
866 Yield(); 866 Yield();
867 } else { 867 } else {
868 _PT_FAIL( rc, "pthread_create()", __FILE__, __LINE__ ); 868 _PT_FAIL( rc, "pthread_create()", __FILE__, __LINE__ );
869 } 869 }
870 } 870 }
871 } 871 }
872 */ 872 */
873# endif 873# endif
874 874
875 PT_CALL( pthread_attr_destroy( &a)); 875 PT_CALL( pthread_attr_destroy( &a));
876} 876}
877 877
878 878
879void THREAD_SET_PRIORITY( int prio) 879void THREAD_SET_PRIORITY( int prio)
880{ 880{
881#if defined PLATFORM_LINUX && defined LINUX_SCHED_RR 881#if defined PLATFORM_LINUX && defined LINUX_SCHED_RR
882 if( sudo) // only root-privileged process can change priorities 882 if( sudo) // only root-privileged process can change priorities
883#endif // defined PLATFORM_LINUX && defined LINUX_SCHED_RR 883#endif // defined PLATFORM_LINUX && defined LINUX_SCHED_RR
884 { 884 {
885 struct sched_param sp; 885 struct sched_param sp;
886 // prio range [-3,+3] was checked by the caller 886 // prio range [-3,+3] was checked by the caller
887 sp.sched_priority = gs_prio_remap[ prio + 3]; 887 sp.sched_priority = gs_prio_remap[ prio + 3];
888 PT_CALL( pthread_setschedparam( pthread_self(), _PRIO_MODE, &sp)); 888 PT_CALL( pthread_setschedparam( pthread_self(), _PRIO_MODE, &sp));
889 } 889 }
890} 890}
891 891
892void THREAD_SET_AFFINITY( unsigned int aff) 892void THREAD_SET_AFFINITY( unsigned int aff)
893{ 893{
894 int bit = 0; 894 int bit = 0;
895#ifdef __NetBSD__ 895#ifdef __NetBSD__
896 cpuset_t *cpuset = cpuset_create(); 896 cpuset_t *cpuset = cpuset_create();
897 if( cpuset == NULL) 897 if( cpuset == NULL)
898 _PT_FAIL( errno, "cpuset_create", __FILE__, __LINE__-2 ); 898 _PT_FAIL( errno, "cpuset_create", __FILE__, __LINE__-2 );
899#define CPU_SET(b, s) cpuset_set(b, *(s)) 899#define CPU_SET(b, s) cpuset_set(b, *(s))
900#else 900#else
901 cpu_set_t cpuset; 901 cpu_set_t cpuset;
902 CPU_ZERO( &cpuset); 902 CPU_ZERO( &cpuset);
903#endif 903#endif
904 while( aff != 0) 904 while( aff != 0)
905 { 905 {
906 if( aff & 1) 906 if( aff & 1)
907 { 907 {
908 CPU_SET( bit, &cpuset); 908 CPU_SET( bit, &cpuset);
909 } 909 }
910 ++ bit; 910 ++ bit;
911 aff >>= 1; 911 aff >>= 1;
912 } 912 }
913#ifdef __ANDROID__ 913#ifdef __ANDROID__
914 PT_CALL( sched_setaffinity( pthread_self(), sizeof(cpu_set_t), &cpuset)); 914 PT_CALL( sched_setaffinity( pthread_self(), sizeof(cpu_set_t), &cpuset));
915#elif defined(__NetBSD__) 915#elif defined(__NetBSD__)
916 PT_CALL( pthread_setaffinity_np( pthread_self(), cpuset_size(cpuset), cpuset)); 916 PT_CALL( pthread_setaffinity_np( pthread_self(), cpuset_size(cpuset), cpuset));
917 cpuset_destroy( cpuset); 917 cpuset_destroy( cpuset);
918#else 918#else
919 PT_CALL( pthread_setaffinity_np( pthread_self(), sizeof(cpu_set_t), &cpuset)); 919 PT_CALL( pthread_setaffinity_np( pthread_self(), sizeof(cpu_set_t), &cpuset));
920#endif 920#endif
921} 921}
922 922
@@ -986,47 +986,47 @@ bool_t THREAD_WAIT( THREAD_T *ref, double secs , SIGNAL_T *signal_ref, MUTEX_T *
986#endif // THREADWAIT_METHOD == THREADWAIT_CONDVAR 986#endif // THREADWAIT_METHOD == THREADWAIT_CONDVAR
987 return done; 987 return done;
988 } 988 }
989 // 989 //
990 void THREAD_KILL( THREAD_T *ref ) { 990 void THREAD_KILL( THREAD_T *ref ) {
991#ifdef __ANDROID__ 991#ifdef __ANDROID__
992 __android_log_print(ANDROID_LOG_WARN, LOG_TAG, "Cannot kill thread!"); 992 __android_log_print(ANDROID_LOG_WARN, LOG_TAG, "Cannot kill thread!");
993#else 993#else
994 pthread_cancel( *ref ); 994 pthread_cancel( *ref );
995#endif 995#endif
996 } 996 }
997 997
998 void THREAD_MAKE_ASYNCH_CANCELLABLE() 998 void THREAD_MAKE_ASYNCH_CANCELLABLE()
999 { 999 {
1000#ifdef __ANDROID__ 1000#ifdef __ANDROID__
1001 __android_log_print(ANDROID_LOG_WARN, LOG_TAG, "Cannot make thread async cancellable!"); 1001 __android_log_print(ANDROID_LOG_WARN, LOG_TAG, "Cannot make thread async cancellable!");
1002#else 1002#else
1003 // that's the default, but just in case... 1003 // that's the default, but just in case...
1004 pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL); 1004 pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL);
1005 // we want cancellation to take effect immediately if possible, instead of waiting for a cancellation point (which is the default) 1005 // we want cancellation to take effect immediately if possible, instead of waiting for a cancellation point (which is the default)
1006 pthread_setcanceltype( PTHREAD_CANCEL_ASYNCHRONOUS, NULL); 1006 pthread_setcanceltype( PTHREAD_CANCEL_ASYNCHRONOUS, NULL);
1007#endif 1007#endif
1008 } 1008 }
1009 1009
1010 void THREAD_SETNAME( char const* _name) 1010 void THREAD_SETNAME( char const* _name)
1011 { 1011 {
1012 // exact API to set the thread name is platform-dependant 1012 // exact API to set the thread name is platform-dependant
1013 // if you need to fix the build, or if you know how to fill a hole, tell me (bnt.germain@gmail.com) so that I can submit the fix in github. 1013 // if you need to fix the build, or if you know how to fill a hole, tell me (bnt.germain@gmail.com) so that I can submit the fix in github.
1014#if defined PLATFORM_BSD && !defined __NetBSD__ 1014#if defined PLATFORM_BSD && !defined __NetBSD__
1015 pthread_set_name_np( pthread_self(), _name); 1015 pthread_set_name_np( pthread_self(), _name);
1016#elif defined PLATFORM_BSD && defined __NetBSD__ 1016#elif defined PLATFORM_BSD && defined __NetBSD__
1017 pthread_setname_np( pthread_self(), "%s", (void *)_name); 1017 pthread_setname_np( pthread_self(), "%s", (void *)_name);
1018#elif defined PLATFORM_LINUX 1018#elif defined PLATFORM_LINUX
1019 #if LINUX_USE_PTHREAD_SETNAME_NP 1019 #if LINUX_USE_PTHREAD_SETNAME_NP
1020 pthread_setname_np( pthread_self(), _name); 1020 pthread_setname_np( pthread_self(), _name);
1021 #else // LINUX_USE_PTHREAD_SETNAME_NP 1021 #else // LINUX_USE_PTHREAD_SETNAME_NP
1022 prctl(PR_SET_NAME, _name, 0, 0, 0); 1022 prctl(PR_SET_NAME, _name, 0, 0, 0);
1023 #endif // LINUX_USE_PTHREAD_SETNAME_NP 1023 #endif // LINUX_USE_PTHREAD_SETNAME_NP
1024#elif defined PLATFORM_QNX || defined PLATFORM_CYGWIN 1024#elif defined PLATFORM_QNX || defined PLATFORM_CYGWIN
1025 pthread_setname_np( pthread_self(), _name); 1025 pthread_setname_np( pthread_self(), _name);
1026#elif defined PLATFORM_OSX 1026#elif defined PLATFORM_OSX
1027 pthread_setname_np(_name); 1027 pthread_setname_np(_name);
1028#elif defined PLATFORM_WIN32 || defined PLATFORM_POCKETPC 1028#elif defined PLATFORM_WIN32 || defined PLATFORM_POCKETPC
1029 PT_CALL( pthread_setname_np( pthread_self(), _name)); 1029 PT_CALL( pthread_setname_np( pthread_self(), _name));
1030#endif 1030#endif
1031 } 1031 }
1032#endif // THREADAPI == THREADAPI_PTHREAD 1032#endif // THREADAPI == THREADAPI_PTHREAD
diff --git a/src/threading.h b/src/threading.h
index 778b6a0..1224e08 100644
--- a/src/threading.h
+++ b/src/threading.h
@@ -66,41 +66,41 @@ enum e_status { PENDING, RUNNING, WAITING, DONE, ERROR_ST, CANCELLED };
66 // needed for use with the SIGNAL system. 66 // needed for use with the SIGNAL system.
67 // 67 //
68 68
69 #if _WIN32_WINNT < 0x0600 // CONDITION_VARIABLE aren't available, use a signal 69 #if _WIN32_WINNT < 0x0600 // CONDITION_VARIABLE aren't available, use a signal
70 70
71 typedef struct 71 typedef struct
72 { 72 {
73 CRITICAL_SECTION signalCS; 73 CRITICAL_SECTION signalCS;
74 CRITICAL_SECTION countCS; 74 CRITICAL_SECTION countCS;
75 HANDLE waitEvent; 75 HANDLE waitEvent;
76 HANDLE waitDoneEvent; 76 HANDLE waitDoneEvent;
77 LONG waitersCount; 77 LONG waitersCount;
78 } SIGNAL_T; 78 } SIGNAL_T;
79 79
80 80
81 #define MUTEX_T HANDLE 81 #define MUTEX_T HANDLE
82 void MUTEX_INIT( MUTEX_T* ref); 82 void MUTEX_INIT( MUTEX_T* ref);
83 void MUTEX_FREE( MUTEX_T* ref); 83 void MUTEX_FREE( MUTEX_T* ref);
84 void MUTEX_LOCK( MUTEX_T* ref); 84 void MUTEX_LOCK( MUTEX_T* ref);
85 void MUTEX_UNLOCK( MUTEX_T* ref); 85 void MUTEX_UNLOCK( MUTEX_T* ref);
86 86
87 #else // CONDITION_VARIABLE are available, use them 87 #else // CONDITION_VARIABLE are available, use them
88 88
89 #define SIGNAL_T CONDITION_VARIABLE 89 #define SIGNAL_T CONDITION_VARIABLE
90 #define MUTEX_T CRITICAL_SECTION 90 #define MUTEX_T CRITICAL_SECTION
91 #define MUTEX_INIT( ref) InitializeCriticalSection( ref) 91 #define MUTEX_INIT( ref) InitializeCriticalSection( ref)
92 #define MUTEX_FREE( ref) DeleteCriticalSection( ref) 92 #define MUTEX_FREE( ref) DeleteCriticalSection( ref)
93 #define MUTEX_LOCK( ref) EnterCriticalSection( ref) 93 #define MUTEX_LOCK( ref) EnterCriticalSection( ref)
94 #define MUTEX_UNLOCK( ref) LeaveCriticalSection( ref) 94 #define MUTEX_UNLOCK( ref) LeaveCriticalSection( ref)
95 95
96 #endif // CONDITION_VARIABLE are available 96 #endif // CONDITION_VARIABLE are available
97 97
98 #define MUTEX_RECURSIVE_INIT(ref) MUTEX_INIT(ref) /* always recursive in Win32 */ 98 #define MUTEX_RECURSIVE_INIT(ref) MUTEX_INIT(ref) /* always recursive in Win32 */
99 99
100 typedef unsigned int THREAD_RETURN_T; 100 typedef unsigned int THREAD_RETURN_T;
101 101
102 #define YIELD() Sleep(0) 102 #define YIELD() Sleep(0)
103 #define THREAD_CALLCONV __stdcall 103 #define THREAD_CALLCONV __stdcall
104#else // THREADAPI == THREADAPI_PTHREAD 104#else // THREADAPI == THREADAPI_PTHREAD
105 // PThread (Linux, OS X, ...) 105 // PThread (Linux, OS X, ...)
106 106
@@ -143,13 +143,10 @@ enum e_status { PENDING, RUNNING, WAITING, DONE, ERROR_ST, CANCELLED };
143 // 143 //
144 #if defined( PLATFORM_OSX) 144 #if defined( PLATFORM_OSX)
145 #define YIELD() pthread_yield_np() 145 #define YIELD() pthread_yield_np()
146#elif defined( PLATFORM_WIN32) || defined( PLATFORM_POCKETPC) || defined(__ANDROID__) || defined(__NetBSD__) // no PTHREAD for PLATFORM_XBOX
147 // for some reason win32-pthread doesn't have pthread_yield(), but sched_yield()
148 #define YIELD() sched_yield()
149 #else 146 #else
150 #define YIELD() sched_yield() 147 #define YIELD() sched_yield()
151 #endif 148 #endif
152 #define THREAD_CALLCONV 149 #define THREAD_CALLCONV
153#endif //THREADAPI == THREADAPI_PTHREAD 150#endif //THREADAPI == THREADAPI_PTHREAD
154 151
155void SIGNAL_INIT( SIGNAL_T *ref ); 152void SIGNAL_INIT( SIGNAL_T *ref );
@@ -174,9 +171,9 @@ bool_t SIGNAL_WAIT( SIGNAL_T *ref, MUTEX_T *mu, time_d timeout );
174 171
175#if THREADAPI == THREADAPI_WINDOWS 172#if THREADAPI == THREADAPI_WINDOWS
176 173
177 typedef HANDLE THREAD_T; 174 typedef HANDLE THREAD_T;
178# define THREAD_ISNULL( _h) (_h == 0) 175# define THREAD_ISNULL( _h) (_h == 0)
179 void THREAD_CREATE( THREAD_T* ref, THREAD_RETURN_T (__stdcall *func)( void*), void* data, int prio /* -3..+3 */); 176 void THREAD_CREATE( THREAD_T* ref, THREAD_RETURN_T (__stdcall *func)( void*), void* data, int prio /* -3..+3 */);
180 177
181# define THREAD_PRIO_MIN (-3) 178# define THREAD_PRIO_MIN (-3)
182# define THREAD_PRIO_MAX (+3) 179# define THREAD_PRIO_MAX (+3)
@@ -186,9 +183,9 @@ bool_t SIGNAL_WAIT( SIGNAL_T *ref, MUTEX_T *mu, time_d timeout );
186 183
187#else // THREADAPI == THREADAPI_PTHREAD 184#else // THREADAPI == THREADAPI_PTHREAD
188 185
189 /* Platforms that have a timed 'pthread_join()' can get away with a simpler 186 /* Platforms that have a timed 'pthread_join()' can get away with a simpler
190 * implementation. Others will use a condition variable. 187 * implementation. Others will use a condition variable.
191 */ 188 */
192# if defined __WINPTHREADS_VERSION 189# if defined __WINPTHREADS_VERSION
193//# define USE_PTHREAD_TIMEDJOIN 190//# define USE_PTHREAD_TIMEDJOIN
194# endif // __WINPTHREADS_VERSION 191# endif // __WINPTHREADS_VERSION
@@ -202,13 +199,13 @@ bool_t SIGNAL_WAIT( SIGNAL_T *ref, MUTEX_T *mu, time_d timeout );
202# endif 199# endif
203# endif 200# endif
204 201
205 typedef pthread_t THREAD_T; 202 typedef pthread_t THREAD_T;
206# define THREAD_ISNULL( _h) 0 // pthread_t may be a structure: never 'null' by itself 203# define THREAD_ISNULL( _h) 0 // pthread_t may be a structure: never 'null' by itself
207 204
208 void THREAD_CREATE( THREAD_T* ref, THREAD_RETURN_T (*func)( void*), void* data, int prio /* -3..+3 */); 205 void THREAD_CREATE( THREAD_T* ref, THREAD_RETURN_T (*func)( void*), void* data, int prio /* -3..+3 */);
209 206
210# if defined(PLATFORM_LINUX) 207# if defined(PLATFORM_LINUX)
211 extern volatile bool_t sudo; 208 extern volatile bool_t sudo;
212# ifdef LINUX_SCHED_RR 209# ifdef LINUX_SCHED_RR
213# define THREAD_PRIO_MIN (sudo ? -3 : 0) 210# define THREAD_PRIO_MIN (sudo ? -3 : 0)
214# else 211# else
diff --git a/src/tools.c b/src/tools.c
index 1436e8d..acb78e6 100644
--- a/src/tools.c
+++ b/src/tools.c
@@ -106,50 +106,50 @@ void push_registry_subtable( lua_State* L, UniqueKey key_)
106#ifdef _DEBUG 106#ifdef _DEBUG
107void luaG_dump( lua_State* L) 107void luaG_dump( lua_State* L)
108{ 108{
109 int top = lua_gettop( L); 109 int top = lua_gettop( L);
110 int i; 110 int i;
111 111
112 fprintf( stderr, "\n\tDEBUG STACK:\n"); 112 fprintf( stderr, "\n\tDEBUG STACK:\n");
113 113
114 if( top == 0) 114 if( top == 0)
115 fprintf( stderr, "\t(none)\n"); 115 fprintf( stderr, "\t(none)\n");
116 116
117 for( i = 1; i <= top; ++ i) 117 for( i = 1; i <= top; ++ i)
118 { 118 {
119 int type = lua_type( L, i); 119 int type = lua_type( L, i);
120 120
121 fprintf( stderr, "\t[%d]= (%s) ", i, lua_typename( L, type)); 121 fprintf( stderr, "\t[%d]= (%s) ", i, lua_typename( L, type));
122 122
123 // Print item contents here... 123 // Print item contents here...
124 // 124 //
125 // Note: this requires 'tostring()' to be defined. If it is NOT, 125 // Note: this requires 'tostring()' to be defined. If it is NOT,
126 // enable it for more debugging. 126 // enable it for more debugging.
127 // 127 //
128 STACK_CHECK( L, 0); 128 STACK_CHECK( L, 0);
129 STACK_GROW( L, 2); 129 STACK_GROW( L, 2);
130 130
131 lua_getglobal( L, "tostring"); 131 lua_getglobal( L, "tostring");
132 // 132 //
133 // [-1]: tostring function, or nil 133 // [-1]: tostring function, or nil
134 134
135 if( !lua_isfunction( L, -1)) 135 if( !lua_isfunction( L, -1))
136 { 136 {
137 fprintf( stderr, "('tostring' not available)"); 137 fprintf( stderr, "('tostring' not available)");
138 } 138 }
139 else 139 else
140 { 140 {
141 lua_pushvalue( L, i); 141 lua_pushvalue( L, i);
142 lua_call( L, 1 /*args*/, 1 /*retvals*/); 142 lua_call( L, 1 /*args*/, 1 /*retvals*/);
143 143
144 // Don't trust the string contents 144 // Don't trust the string contents
145 // 145 //
146 fprintf( stderr, "%s", lua_tostring( L, -1)); 146 fprintf( stderr, "%s", lua_tostring( L, -1));
147 } 147 }
148 lua_pop( L, 1); 148 lua_pop( L, 1);
149 STACK_END( L, 0); 149 STACK_END( L, 0);
150 fprintf( stderr, "\n"); 150 fprintf( stderr, "\n");
151 } 151 }
152 fprintf( stderr, "\n"); 152 fprintf( stderr, "\n");
153} 153}
154#endif // _DEBUG 154#endif // _DEBUG
155 155
@@ -157,79 +157,79 @@ void luaG_dump( lua_State* L)
157 157
158static void* protected_lua_Alloc( void *ud, void *ptr, size_t osize, size_t nsize) 158static void* protected_lua_Alloc( void *ud, void *ptr, size_t osize, size_t nsize)
159{ 159{
160 void* p; 160 void* p;
161 ProtectedAllocator* s = (ProtectedAllocator*) ud; 161 ProtectedAllocator* s = (ProtectedAllocator*) ud;
162 MUTEX_LOCK( &s->lock); 162 MUTEX_LOCK( &s->lock);
163 p = s->definition.allocF( s->definition.allocUD, ptr, osize, nsize); 163 p = s->definition.allocF( s->definition.allocUD, ptr, osize, nsize);
164 MUTEX_UNLOCK( &s->lock); 164 MUTEX_UNLOCK( &s->lock);
165 return p; 165 return p;
166} 166}
167 167
168static int luaG_provide_protected_allocator( lua_State* L) 168static int luaG_provide_protected_allocator( lua_State* L)
169{ 169{
170 Universe* U = universe_get( L); 170 Universe* U = universe_get( L);
171 AllocatorDefinition* def = lua_newuserdatauv( L, sizeof(AllocatorDefinition), 0); 171 AllocatorDefinition* def = lua_newuserdatauv( L, sizeof(AllocatorDefinition), 0);
172 def->allocF = protected_lua_Alloc; 172 def->allocF = protected_lua_Alloc;
173 def->allocUD = &U->protected_allocator; 173 def->allocUD = &U->protected_allocator;
174 return 1; 174 return 1;
175} 175}
176 176
177// Do I need to disable this when compiling for LuaJIT to prevent issues? 177// Do I need to disable this when compiling for LuaJIT to prevent issues?
178void initialize_allocator_function( Universe* U, lua_State* L) 178void initialize_allocator_function( Universe* U, lua_State* L)
179{ 179{
180 STACK_CHECK( L, 0); 180 STACK_CHECK( L, 0);
181 lua_getfield( L, -1, "allocator"); // settings allocator|nil|"protected" 181 lua_getfield( L, -1, "allocator"); // settings allocator|nil|"protected"
182 if( !lua_isnil( L, -1)) 182 if( !lua_isnil( L, -1))
183 { 183 {
184 // store C function pointer in an internal variable 184 // store C function pointer in an internal variable
185 U->provide_allocator = lua_tocfunction( L, -1); // settings allocator 185 U->provide_allocator = lua_tocfunction( L, -1); // settings allocator
186 if( U->provide_allocator != NULL) 186 if( U->provide_allocator != NULL)
187 { 187 {
188 // make sure the function doesn't have upvalues 188 // make sure the function doesn't have upvalues
189 char const* upname = lua_getupvalue( L, -1, 1); // settings allocator upval? 189 char const* upname = lua_getupvalue( L, -1, 1); // settings allocator upval?
190 if( upname != NULL) // should be "" for C functions with upvalues if any 190 if( upname != NULL) // should be "" for C functions with upvalues if any
191 { 191 {
192 (void) luaL_error( L, "config.allocator() shouldn't have upvalues"); 192 (void) luaL_error( L, "config.allocator() shouldn't have upvalues");
193 } 193 }
194 // remove this C function from the config table so that it doesn't cause problems 194 // remove this C function from the config table so that it doesn't cause problems
195 // when we transfer the config table in newly created Lua states 195 // when we transfer the config table in newly created Lua states
196 lua_pushnil( L); // settings allocator nil 196 lua_pushnil( L); // settings allocator nil
197 lua_setfield( L, -3, "allocator"); // settings allocator 197 lua_setfield( L, -3, "allocator"); // settings allocator
198 } 198 }
199 else if( lua_type( L, -1) == LUA_TSTRING) 199 else if( lua_type( L, -1) == LUA_TSTRING)
200 { 200 {
201 // initialize all we need for the protected allocator 201 // initialize all we need for the protected allocator
202 MUTEX_INIT( &U->protected_allocator.lock); // the mutex 202 MUTEX_INIT( &U->protected_allocator.lock); // the mutex
203 // and the original allocator to call from inside protection by the mutex 203 // and the original allocator to call from inside protection by the mutex
204 U->protected_allocator.definition.allocF = lua_getallocf( L, &U->protected_allocator.definition.allocUD); 204 U->protected_allocator.definition.allocF = lua_getallocf( L, &U->protected_allocator.definition.allocUD);
205 // before a state is created, this function will be called to obtain the allocator 205 // before a state is created, this function will be called to obtain the allocator
206 U->provide_allocator = luaG_provide_protected_allocator; 206 U->provide_allocator = luaG_provide_protected_allocator;
207 207
208 lua_setallocf( L, protected_lua_Alloc, &U->protected_allocator); 208 lua_setallocf( L, protected_lua_Alloc, &U->protected_allocator);
209 } 209 }
210 } 210 }
211 lua_pop( L, 1); // settings 211 lua_pop( L, 1); // settings
212 STACK_END( L, 0); 212 STACK_END( L, 0);
213} 213}
214 214
215void cleanup_allocator_function( Universe* U, lua_State* L) 215void cleanup_allocator_function( Universe* U, lua_State* L)
216{ 216{
217 // remove the protected allocator, if any 217 // remove the protected allocator, if any
218 if( U->protected_allocator.definition.allocF != NULL) 218 if( U->protected_allocator.definition.allocF != NULL)
219 { 219 {
220 // install the non-protected allocator 220 // install the non-protected allocator
221 lua_setallocf( L, U->protected_allocator.definition.allocF, U->protected_allocator.definition.allocUD); 221 lua_setallocf( L, U->protected_allocator.definition.allocF, U->protected_allocator.definition.allocUD);
222 // release the mutex 222 // release the mutex
223 MUTEX_FREE( &U->protected_allocator.lock); 223 MUTEX_FREE( &U->protected_allocator.lock);
224 } 224 }
225} 225}
226 226
227// ################################################################################################ 227// ################################################################################################
228 228
229static int dummy_writer( lua_State* L, void const* p, size_t sz, void* ud) 229static int dummy_writer( lua_State* L, void const* p, size_t sz, void* ud)
230{ 230{
231 (void)L; (void)p; (void)sz; (void) ud; // unused 231 (void)L; (void)p; (void)sz; (void) ud; // unused
232 return 666; 232 return 666;
233} 233}
234 234
235 235
@@ -250,42 +250,42 @@ static int dummy_writer( lua_State* L, void const* p, size_t sz, void* ud)
250 250
251typedef enum 251typedef enum
252{ 252{
253 FST_Bytecode, 253 FST_Bytecode,
254 FST_Native, 254 FST_Native,
255 FST_FastJIT 255 FST_FastJIT
256} FuncSubType; 256} FuncSubType;
257 257
258FuncSubType luaG_getfuncsubtype( lua_State *L, int _i) 258FuncSubType luaG_getfuncsubtype( lua_State *L, int _i)
259{ 259{
260 if( lua_tocfunction( L, _i)) 260 if( lua_tocfunction( L, _i))
261 { 261 {
262 return FST_Native; 262 return FST_Native;
263 } 263 }
264 { 264 {
265 int mustpush = 0, dumpres; 265 int mustpush = 0, dumpres;
266 if( lua_absindex( L, _i) != lua_gettop( L)) 266 if( lua_absindex( L, _i) != lua_gettop( L))
267 { 267 {
268 lua_pushvalue( L, _i); 268 lua_pushvalue( L, _i);
269 mustpush = 1; 269 mustpush = 1;
270 } 270 }
271 // the provided writer fails with code 666 271 // the provided writer fails with code 666
272 // therefore, anytime we get 666, this means that lua_dump() attempted a dump 272 // therefore, anytime we get 666, this means that lua_dump() attempted a dump
273 // all other cases mean this is either a C or LuaJIT-fast function 273 // all other cases mean this is either a C or LuaJIT-fast function
274 dumpres = lua504_dump( L, dummy_writer, NULL, 0); 274 dumpres = lua504_dump( L, dummy_writer, NULL, 0);
275 lua_pop( L, mustpush); 275 lua_pop( L, mustpush);
276 if( dumpres == 666) 276 if( dumpres == 666)
277 { 277 {
278 return FST_Bytecode; 278 return FST_Bytecode;
279 } 279 }
280 } 280 }
281 return FST_FastJIT; 281 return FST_FastJIT;
282} 282}
283 283
284static lua_CFunction luaG_tocfunction( lua_State *L, int _i, FuncSubType *_out) 284static lua_CFunction luaG_tocfunction( lua_State *L, int _i, FuncSubType *_out)
285{ 285{
286 lua_CFunction p = lua_tocfunction( L, _i); 286 lua_CFunction p = lua_tocfunction( L, _i);
287 *_out = luaG_getfuncsubtype( L, _i); 287 *_out = luaG_getfuncsubtype( L, _i);
288 return p; 288 return p;
289} 289}
290 290
291// crc64/we of string "LOOKUPCACHE_REGKEY" generated at http://www.nitrxgen.net/hashgen/ 291// crc64/we of string "LOOKUPCACHE_REGKEY" generated at http://www.nitrxgen.net/hashgen/
@@ -294,26 +294,26 @@ static DECLARE_CONST_UNIQUE_KEY( LOOKUPCACHE_REGKEY, 0x837a68dfc6fcb716);
294// inspired from tconcat() in ltablib.c 294// inspired from tconcat() in ltablib.c
295static char const* luaG_pushFQN( lua_State* L, int t, int last, size_t* length) 295static char const* luaG_pushFQN( lua_State* L, int t, int last, size_t* length)
296{ 296{
297 int i = 1; 297 int i = 1;
298 luaL_Buffer b; 298 luaL_Buffer b;
299 STACK_CHECK( L, 0); 299 STACK_CHECK( L, 0);
300 // Lua 5.4 pushes &b as light userdata on the stack. be aware of it... 300 // Lua 5.4 pushes &b as light userdata on the stack. be aware of it...
301 luaL_buffinit( L, &b); // ... {} ... &b? 301 luaL_buffinit( L, &b); // ... {} ... &b?
302 for( ; i < last; ++ i) 302 for( ; i < last; ++ i)
303 { 303 {
304 lua_rawgeti( L, t, i); 304 lua_rawgeti( L, t, i);
305 luaL_addvalue( &b); 305 luaL_addvalue( &b);
306 luaL_addlstring(&b, "/", 1); 306 luaL_addlstring(&b, "/", 1);
307 } 307 }
308 if( i == last) // add last value (if interval was not empty) 308 if( i == last) // add last value (if interval was not empty)
309 { 309 {
310 lua_rawgeti( L, t, i); 310 lua_rawgeti( L, t, i);
311 luaL_addvalue( &b); 311 luaL_addvalue( &b);
312 } 312 }
313 // &b is popped at that point (-> replaced by the result) 313 // &b is popped at that point (-> replaced by the result)
314 luaL_pushresult( &b); // ... {} ... "<result>" 314 luaL_pushresult( &b); // ... {} ... "<result>"
315 STACK_END( L, 1); 315 STACK_END( L, 1);
316 return lua_tolstring( L, -1, length); 316 return lua_tolstring( L, -1, length);
317} 317}
318 318
319/* 319/*
@@ -326,199 +326,199 @@ static char const* luaG_pushFQN( lua_State* L, int t, int last, size_t* length)
326 */ 326 */
327static void update_lookup_entry( DEBUGSPEW_PARAM_COMMA( Universe* U) lua_State* L, int _ctx_base, int _depth) 327static void update_lookup_entry( DEBUGSPEW_PARAM_COMMA( Universe* U) lua_State* L, int _ctx_base, int _depth)
328{ 328{
329 // slot 1 in the stack contains the table that receives everything we found 329 // slot 1 in the stack contains the table that receives everything we found
330 int const dest = _ctx_base; 330 int const dest = _ctx_base;
331 // slot 2 contains a table that, when concatenated, produces the fully qualified name of scanned elements in the table provided at slot _i 331 // slot 2 contains a table that, when concatenated, produces the fully qualified name of scanned elements in the table provided at slot _i
332 int const fqn = _ctx_base + 1; 332 int const fqn = _ctx_base + 1;
333 333
334 size_t prevNameLength, newNameLength; 334 size_t prevNameLength, newNameLength;
335 char const* prevName; 335 char const* prevName;
336 DEBUGSPEW_CODE( char const *newName); 336 DEBUGSPEW_CODE( char const *newName);
337 DEBUGSPEW_CODE( fprintf( stderr, INDENT_BEGIN "update_lookup_entry()\n" INDENT_END)); 337 DEBUGSPEW_CODE( fprintf( stderr, INDENT_BEGIN "update_lookup_entry()\n" INDENT_END));
338 DEBUGSPEW_CODE( ++ U->debugspew_indent_depth); 338 DEBUGSPEW_CODE( ++ U->debugspew_indent_depth);
339 339
340 STACK_CHECK( L, 0); 340 STACK_CHECK( L, 0);
341 // first, raise an error if the function is already known 341 // first, raise an error if the function is already known
342 lua_pushvalue( L, -1); // ... {bfc} k o o 342 lua_pushvalue( L, -1); // ... {bfc} k o o
343 lua_rawget( L, dest); // ... {bfc} k o name? 343 lua_rawget( L, dest); // ... {bfc} k o name?
344 prevName = lua_tolstring( L, -1, &prevNameLength); // NULL if we got nil (first encounter of this object) 344 prevName = lua_tolstring( L, -1, &prevNameLength); // NULL if we got nil (first encounter of this object)
345 // push name in fqn stack (note that concatenation will crash if name is a not string or a number) 345 // push name in fqn stack (note that concatenation will crash if name is a not string or a number)
346 lua_pushvalue( L, -3); // ... {bfc} k o name? k 346 lua_pushvalue( L, -3); // ... {bfc} k o name? k
347 ASSERT_L( lua_type( L, -1) == LUA_TNUMBER || lua_type( L, -1) == LUA_TSTRING); 347 ASSERT_L( lua_type( L, -1) == LUA_TNUMBER || lua_type( L, -1) == LUA_TSTRING);
348 ++ _depth; 348 ++ _depth;
349 lua_rawseti( L, fqn, _depth); // ... {bfc} k o name? 349 lua_rawseti( L, fqn, _depth); // ... {bfc} k o name?
350 // generate name 350 // generate name
351 DEBUGSPEW_CODE( newName =) luaG_pushFQN( L, fqn, _depth, &newNameLength); // ... {bfc} k o name? "f.q.n" 351 DEBUGSPEW_CODE( newName =) luaG_pushFQN( L, fqn, _depth, &newNameLength); // ... {bfc} k o name? "f.q.n"
352 // Lua 5.2 introduced a hash randomizer seed which causes table iteration to yield a different key order 352 // Lua 5.2 introduced a hash randomizer seed which causes table iteration to yield a different key order
353 // on different VMs even when the tables are populated the exact same way. 353 // on different VMs even when the tables are populated the exact same way.
354 // When Lua is built with compatibility options (such as LUA_COMPAT_ALL), 354 // When Lua is built with compatibility options (such as LUA_COMPAT_ALL),
355 // this causes several base libraries to register functions under multiple names. 355 // this causes several base libraries to register functions under multiple names.
356 // This, with the randomizer, can cause the first generated name of an object to be different on different VMs, 356 // This, with the randomizer, can cause the first generated name of an object to be different on different VMs,
357 // which breaks function transfer. 357 // which breaks function transfer.
358 // Also, nothing prevents any external module from exposing a given object under several names, so... 358 // Also, nothing prevents any external module from exposing a given object under several names, so...
359 // Therefore, when we encounter an object for which a name was previously registered, we need to select the names 359 // Therefore, when we encounter an object for which a name was previously registered, we need to select the names
360 // based on some sorting order so that we end up with the same name in all databases whatever order the table walk yielded 360 // based on some sorting order so that we end up with the same name in all databases whatever order the table walk yielded
361 if( prevName != NULL && (prevNameLength < newNameLength || lua_lessthan( L, -2, -1))) 361 if( prevName != NULL && (prevNameLength < newNameLength || lua_lessthan( L, -2, -1)))
362 { 362 {
363 DEBUGSPEW_CODE( fprintf( stderr, INDENT_BEGIN "%s '%s' remained named '%s'\n" INDENT_END, lua_typename( L, lua_type( L, -3)), newName, prevName)); 363 DEBUGSPEW_CODE( fprintf( stderr, INDENT_BEGIN "%s '%s' remained named '%s'\n" INDENT_END, lua_typename( L, lua_type( L, -3)), newName, prevName));
364 // the previous name is 'smaller' than the one we just generated: keep it! 364 // the previous name is 'smaller' than the one we just generated: keep it!
365 lua_pop( L, 3); // ... {bfc} k 365 lua_pop( L, 3); // ... {bfc} k
366 } 366 }
367 else 367 else
368 { 368 {
369 // the name we generated is either the first one, or a better fit for our purposes 369 // the name we generated is either the first one, or a better fit for our purposes
370 if( prevName) 370 if( prevName)
371 { 371 {
372 // clear the previous name for the database to avoid clutter 372 // clear the previous name for the database to avoid clutter
373 lua_insert( L, -2); // ... {bfc} k o "f.q.n" prevName 373 lua_insert( L, -2); // ... {bfc} k o "f.q.n" prevName
374 // t[prevName] = nil 374 // t[prevName] = nil
375 lua_pushnil( L); // ... {bfc} k o "f.q.n" prevName nil 375 lua_pushnil( L); // ... {bfc} k o "f.q.n" prevName nil
376 lua_rawset( L, dest); // ... {bfc} k o "f.q.n" 376 lua_rawset( L, dest); // ... {bfc} k o "f.q.n"
377 } 377 }
378 else 378 else
379 { 379 {
380 lua_remove( L, -2); // ... {bfc} k o "f.q.n" 380 lua_remove( L, -2); // ... {bfc} k o "f.q.n"
381 } 381 }
382 DEBUGSPEW_CODE( fprintf( stderr, INDENT_BEGIN "%s '%s'\n" INDENT_END, lua_typename( L, lua_type( L, -2)), newName)); 382 DEBUGSPEW_CODE( fprintf( stderr, INDENT_BEGIN "%s '%s'\n" INDENT_END, lua_typename( L, lua_type( L, -2)), newName));
383 // prepare the stack for database feed 383 // prepare the stack for database feed
384 lua_pushvalue( L, -1); // ... {bfc} k o "f.q.n" "f.q.n" 384 lua_pushvalue( L, -1); // ... {bfc} k o "f.q.n" "f.q.n"
385 lua_pushvalue( L, -3); // ... {bfc} k o "f.q.n" "f.q.n" o 385 lua_pushvalue( L, -3); // ... {bfc} k o "f.q.n" "f.q.n" o
386 ASSERT_L( lua_rawequal( L, -1, -4)); 386 ASSERT_L( lua_rawequal( L, -1, -4));
387 ASSERT_L( lua_rawequal( L, -2, -3)); 387 ASSERT_L( lua_rawequal( L, -2, -3));
388 // t["f.q.n"] = o 388 // t["f.q.n"] = o
389 lua_rawset( L, dest); // ... {bfc} k o "f.q.n" 389 lua_rawset( L, dest); // ... {bfc} k o "f.q.n"
390 // t[o] = "f.q.n" 390 // t[o] = "f.q.n"
391 lua_rawset( L, dest); // ... {bfc} k 391 lua_rawset( L, dest); // ... {bfc} k
392 // remove table name from fqn stack 392 // remove table name from fqn stack
393 lua_pushnil( L); // ... {bfc} k nil 393 lua_pushnil( L); // ... {bfc} k nil
394 lua_rawseti( L, fqn, _depth); // ... {bfc} k 394 lua_rawseti( L, fqn, _depth); // ... {bfc} k
395 } 395 }
396 -- _depth; 396 -- _depth;
397 STACK_END( L, -1); 397 STACK_END( L, -1);
398 DEBUGSPEW_CODE( -- U->debugspew_indent_depth); 398 DEBUGSPEW_CODE( -- U->debugspew_indent_depth);
399} 399}
400 400
401static void populate_func_lookup_table_recur( DEBUGSPEW_PARAM_COMMA( Universe* U) lua_State* L, int _ctx_base, int _i, int _depth) 401static void populate_func_lookup_table_recur( DEBUGSPEW_PARAM_COMMA( Universe* U) lua_State* L, int _ctx_base, int _i, int _depth)
402{ 402{
403 lua_Integer visit_count; 403 lua_Integer visit_count;
404 // slot 2 contains a table that, when concatenated, produces the fully qualified name of scanned elements in the table provided at slot _i 404 // slot 2 contains a table that, when concatenated, produces the fully qualified name of scanned elements in the table provided at slot _i
405 int const fqn = _ctx_base + 1; 405 int const fqn = _ctx_base + 1;
406 // slot 3 contains a cache that stores all already visited tables to avoid infinite recursion loops 406 // slot 3 contains a cache that stores all already visited tables to avoid infinite recursion loops
407 int const cache = _ctx_base + 2; 407 int const cache = _ctx_base + 2;
408 // we need to remember subtables to process them after functions encountered at the current depth (breadth-first search) 408 // we need to remember subtables to process them after functions encountered at the current depth (breadth-first search)
409 int const breadth_first_cache = lua_gettop( L) + 1; 409 int const breadth_first_cache = lua_gettop( L) + 1;
410 DEBUGSPEW_CODE( fprintf( stderr, INDENT_BEGIN "populate_func_lookup_table_recur()\n" INDENT_END)); 410 DEBUGSPEW_CODE( fprintf( stderr, INDENT_BEGIN "populate_func_lookup_table_recur()\n" INDENT_END));
411 DEBUGSPEW_CODE( ++ U->debugspew_indent_depth); 411 DEBUGSPEW_CODE( ++ U->debugspew_indent_depth);
412 412
413 STACK_GROW( L, 6); 413 STACK_GROW( L, 6);
414 // slot _i contains a table where we search for functions (or a full userdata with a metatable) 414 // slot _i contains a table where we search for functions (or a full userdata with a metatable)
415 STACK_CHECK( L, 0); // ... {_i} 415 STACK_CHECK( L, 0); // ... {_i}
416 416
417 // if object is a userdata, replace it by its metatable 417 // if object is a userdata, replace it by its metatable
418 if( lua_type( L, _i) == LUA_TUSERDATA) 418 if( lua_type( L, _i) == LUA_TUSERDATA)
419 { 419 {
420 lua_getmetatable( L, _i); // ... {_i} mt 420 lua_getmetatable( L, _i); // ... {_i} mt
421 lua_replace( L, _i); // ... {_i} 421 lua_replace( L, _i); // ... {_i}
422 } 422 }
423 423
424 // if table is already visited, we are done 424 // if table is already visited, we are done
425 lua_pushvalue( L, _i); // ... {_i} {} 425 lua_pushvalue( L, _i); // ... {_i} {}
426 lua_rawget( L, cache); // ... {_i} nil|n 426 lua_rawget( L, cache); // ... {_i} nil|n
427 visit_count = lua_tointeger( L, -1); // 0 if nil, else n 427 visit_count = lua_tointeger( L, -1); // 0 if nil, else n
428 lua_pop( L, 1); // ... {_i} 428 lua_pop( L, 1); // ... {_i}
429 STACK_MID( L, 0); 429 STACK_MID( L, 0);
430 if( visit_count > 0) 430 if( visit_count > 0)
431 { 431 {
432 DEBUGSPEW_CODE( fprintf( stderr, INDENT_BEGIN "already visited\n" INDENT_END)); 432 DEBUGSPEW_CODE( fprintf( stderr, INDENT_BEGIN "already visited\n" INDENT_END));
433 DEBUGSPEW_CODE( -- U->debugspew_indent_depth); 433 DEBUGSPEW_CODE( -- U->debugspew_indent_depth);
434 return; 434 return;
435 } 435 }
436 436
437 // remember we visited this table (1-visit count) 437 // remember we visited this table (1-visit count)
438 lua_pushvalue( L, _i); // ... {_i} {} 438 lua_pushvalue( L, _i); // ... {_i} {}
439 lua_pushinteger( L, visit_count + 1); // ... {_i} {} 1 439 lua_pushinteger( L, visit_count + 1); // ... {_i} {} 1
440 lua_rawset( L, cache); // ... {_i} 440 lua_rawset( L, cache); // ... {_i}
441 STACK_MID( L, 0); 441 STACK_MID( L, 0);
442 442
443 // this table is at breadth_first_cache index 443 // this table is at breadth_first_cache index
444 lua_newtable( L); // ... {_i} {bfc} 444 lua_newtable( L); // ... {_i} {bfc}
445 ASSERT_L( lua_gettop( L) == breadth_first_cache); 445 ASSERT_L( lua_gettop( L) == breadth_first_cache);
446 // iterate over all entries in the processed table 446 // iterate over all entries in the processed table
447 lua_pushnil( L); // ... {_i} {bfc} nil 447 lua_pushnil( L); // ... {_i} {bfc} nil
448 while( lua_next( L, _i) != 0) // ... {_i} {bfc} k v 448 while( lua_next( L, _i) != 0) // ... {_i} {bfc} k v
449 { 449 {
450 // just for debug, not actually needed 450 // just for debug, not actually needed
451 //char const* key = (lua_type( L, -2) == LUA_TSTRING) ? lua_tostring( L, -2) : "not a string"; 451 //char const* key = (lua_type( L, -2) == LUA_TSTRING) ? lua_tostring( L, -2) : "not a string";
452 // subtable: process it recursively 452 // subtable: process it recursively
453 if( lua_istable( L, -1)) // ... {_i} {bfc} k {} 453 if( lua_istable( L, -1)) // ... {_i} {bfc} k {}
454 { 454 {
455 // increment visit count to make sure we will actually scan it at this recursive level 455 // increment visit count to make sure we will actually scan it at this recursive level
456 lua_pushvalue( L, -1); // ... {_i} {bfc} k {} {} 456 lua_pushvalue( L, -1); // ... {_i} {bfc} k {} {}
457 lua_pushvalue( L, -1); // ... {_i} {bfc} k {} {} {} 457 lua_pushvalue( L, -1); // ... {_i} {bfc} k {} {} {}
458 lua_rawget( L, cache); // ... {_i} {bfc} k {} {} n? 458 lua_rawget( L, cache); // ... {_i} {bfc} k {} {} n?
459 visit_count = lua_tointeger( L, -1) + 1; // 1 if we got nil, else n+1 459 visit_count = lua_tointeger( L, -1) + 1; // 1 if we got nil, else n+1
460 lua_pop( L, 1); // ... {_i} {bfc} k {} {} 460 lua_pop( L, 1); // ... {_i} {bfc} k {} {}
461 lua_pushinteger( L, visit_count); // ... {_i} {bfc} k {} {} n 461 lua_pushinteger( L, visit_count); // ... {_i} {bfc} k {} {} n
462 lua_rawset( L, cache); // ... {_i} {bfc} k {} 462 lua_rawset( L, cache); // ... {_i} {bfc} k {}
463 // store the table in the breadth-first cache 463 // store the table in the breadth-first cache
464 lua_pushvalue( L, -2); // ... {_i} {bfc} k {} k 464 lua_pushvalue( L, -2); // ... {_i} {bfc} k {} k
465 lua_pushvalue( L, -2); // ... {_i} {bfc} k {} k {} 465 lua_pushvalue( L, -2); // ... {_i} {bfc} k {} k {}
466 lua_rawset( L, breadth_first_cache); // ... {_i} {bfc} k {} 466 lua_rawset( L, breadth_first_cache); // ... {_i} {bfc} k {}
467 // generate a name, and if we already had one name, keep whichever is the shorter 467 // generate a name, and if we already had one name, keep whichever is the shorter
468 update_lookup_entry( DEBUGSPEW_PARAM_COMMA( U) L, _ctx_base, _depth); // ... {_i} {bfc} k 468 update_lookup_entry( DEBUGSPEW_PARAM_COMMA( U) L, _ctx_base, _depth); // ... {_i} {bfc} k
469 } 469 }
470 else if( lua_isfunction( L, -1) && (luaG_getfuncsubtype( L, -1) != FST_Bytecode)) // ... {_i} {bfc} k func 470 else if( lua_isfunction( L, -1) && (luaG_getfuncsubtype( L, -1) != FST_Bytecode)) // ... {_i} {bfc} k func
471 { 471 {
472 // generate a name, and if we already had one name, keep whichever is the shorter 472 // generate a name, and if we already had one name, keep whichever is the shorter
473 update_lookup_entry( DEBUGSPEW_PARAM_COMMA( U) L, _ctx_base, _depth); // ... {_i} {bfc} k 473 update_lookup_entry( DEBUGSPEW_PARAM_COMMA( U) L, _ctx_base, _depth); // ... {_i} {bfc} k
474 } 474 }
475 else 475 else
476 { 476 {
477 lua_pop( L, 1); // ... {_i} {bfc} k 477 lua_pop( L, 1); // ... {_i} {bfc} k
478 } 478 }
479 STACK_MID( L, 2); 479 STACK_MID( L, 2);
480 } 480 }
481 // now process the tables we encountered at that depth 481 // now process the tables we encountered at that depth
482 ++ _depth; 482 ++ _depth;
483 lua_pushnil( L); // ... {_i} {bfc} nil 483 lua_pushnil( L); // ... {_i} {bfc} nil
484 while( lua_next( L, breadth_first_cache) != 0) // ... {_i} {bfc} k {} 484 while( lua_next( L, breadth_first_cache) != 0) // ... {_i} {bfc} k {}
485 { 485 {
486 DEBUGSPEW_CODE( char const* key = (lua_type( L, -2) == LUA_TSTRING) ? lua_tostring( L, -2) : "not a string"); 486 DEBUGSPEW_CODE( char const* key = (lua_type( L, -2) == LUA_TSTRING) ? lua_tostring( L, -2) : "not a string");
487 DEBUGSPEW_CODE( fprintf( stderr, INDENT_BEGIN "table '%s'\n" INDENT_END, key)); 487 DEBUGSPEW_CODE( fprintf( stderr, INDENT_BEGIN "table '%s'\n" INDENT_END, key));
488 DEBUGSPEW_CODE( ++ U->debugspew_indent_depth); 488 DEBUGSPEW_CODE( ++ U->debugspew_indent_depth);
489 // un-visit this table in case we do need to process it 489 // un-visit this table in case we do need to process it
490 lua_pushvalue( L, -1); // ... {_i} {bfc} k {} {} 490 lua_pushvalue( L, -1); // ... {_i} {bfc} k {} {}
491 lua_rawget( L, cache); // ... {_i} {bfc} k {} n 491 lua_rawget( L, cache); // ... {_i} {bfc} k {} n
492 ASSERT_L( lua_type( L, -1) == LUA_TNUMBER); 492 ASSERT_L( lua_type( L, -1) == LUA_TNUMBER);
493 visit_count = lua_tointeger( L, -1) - 1; 493 visit_count = lua_tointeger( L, -1) - 1;
494 lua_pop( L, 1); // ... {_i} {bfc} k {} 494 lua_pop( L, 1); // ... {_i} {bfc} k {}
495 lua_pushvalue( L, -1); // ... {_i} {bfc} k {} {} 495 lua_pushvalue( L, -1); // ... {_i} {bfc} k {} {}
496 if( visit_count > 0) 496 if( visit_count > 0)
497 { 497 {
498 lua_pushinteger( L, visit_count); // ... {_i} {bfc} k {} {} n 498 lua_pushinteger( L, visit_count); // ... {_i} {bfc} k {} {} n
499 } 499 }
500 else 500 else
501 { 501 {
502 lua_pushnil( L); // ... {_i} {bfc} k {} {} nil 502 lua_pushnil( L); // ... {_i} {bfc} k {} {} nil
503 } 503 }
504 lua_rawset( L, cache); // ... {_i} {bfc} k {} 504 lua_rawset( L, cache); // ... {_i} {bfc} k {}
505 // push table name in fqn stack (note that concatenation will crash if name is a not string!) 505 // push table name in fqn stack (note that concatenation will crash if name is a not string!)
506 lua_pushvalue( L, -2); // ... {_i} {bfc} k {} k 506 lua_pushvalue( L, -2); // ... {_i} {bfc} k {} k
507 lua_rawseti( L, fqn, _depth); // ... {_i} {bfc} k {} 507 lua_rawseti( L, fqn, _depth); // ... {_i} {bfc} k {}
508 populate_func_lookup_table_recur( DEBUGSPEW_PARAM_COMMA( U) L, _ctx_base, lua_gettop( L), _depth); 508 populate_func_lookup_table_recur( DEBUGSPEW_PARAM_COMMA( U) L, _ctx_base, lua_gettop( L), _depth);
509 lua_pop( L, 1); // ... {_i} {bfc} k 509 lua_pop( L, 1); // ... {_i} {bfc} k
510 STACK_MID( L, 2); 510 STACK_MID( L, 2);
511 DEBUGSPEW_CODE( -- U->debugspew_indent_depth); 511 DEBUGSPEW_CODE( -- U->debugspew_indent_depth);
512 } 512 }
513 // remove table name from fqn stack 513 // remove table name from fqn stack
514 lua_pushnil( L); // ... {_i} {bfc} nil 514 lua_pushnil( L); // ... {_i} {bfc} nil
515 lua_rawseti( L, fqn, _depth); // ... {_i} {bfc} 515 lua_rawseti( L, fqn, _depth); // ... {_i} {bfc}
516 -- _depth; 516 -- _depth;
517 // we are done with our cache 517 // we are done with our cache
518 lua_pop( L, 1); // ... {_i} 518 lua_pop( L, 1); // ... {_i}
519 STACK_END( L, 0); 519 STACK_END( L, 0);
520 // we are done // ... {_i} {bfc} 520 // we are done // ... {_i} {bfc}
521 DEBUGSPEW_CODE( -- U->debugspew_indent_depth); 521 DEBUGSPEW_CODE( -- U->debugspew_indent_depth);
522} 522}
523 523
524/* 524/*
@@ -526,63 +526,63 @@ static void populate_func_lookup_table_recur( DEBUGSPEW_PARAM_COMMA( Universe* U
526 */ 526 */
527void populate_func_lookup_table( lua_State* L, int _i, char const* name_) 527void populate_func_lookup_table( lua_State* L, int _i, char const* name_)
528{ 528{
529 int const ctx_base = lua_gettop( L) + 1; 529 int const ctx_base = lua_gettop( L) + 1;
530 int const in_base = lua_absindex( L, _i); 530 int const in_base = lua_absindex( L, _i);
531 int start_depth = 0; 531 int start_depth = 0;
532 DEBUGSPEW_CODE( Universe* U = universe_get( L)); 532 DEBUGSPEW_CODE( Universe* U = universe_get( L));
533 DEBUGSPEW_CODE( fprintf( stderr, INDENT_BEGIN "%p: populate_func_lookup_table('%s')\n" INDENT_END, L, name_ ? name_ : "NULL")); 533 DEBUGSPEW_CODE( fprintf( stderr, INDENT_BEGIN "%p: populate_func_lookup_table('%s')\n" INDENT_END, L, name_ ? name_ : "NULL"));
534 DEBUGSPEW_CODE( ++ U->debugspew_indent_depth); 534 DEBUGSPEW_CODE( ++ U->debugspew_indent_depth);
535 STACK_GROW( L, 3); 535 STACK_GROW( L, 3);
536 STACK_CHECK( L, 0); 536 STACK_CHECK( L, 0);
537 REGISTRY_GET( L, LOOKUP_REGKEY); // {} 537 REGISTRY_GET( L, LOOKUP_REGKEY); // {}
538 STACK_MID( L, 1); 538 STACK_MID( L, 1);
539 ASSERT_L( lua_istable( L, -1)); 539 ASSERT_L( lua_istable( L, -1));
540 if( lua_type( L, in_base) == LUA_TFUNCTION) // for example when a module is a simple function 540 if( lua_type( L, in_base) == LUA_TFUNCTION) // for example when a module is a simple function
541 { 541 {
542 name_ = name_ ? name_ : "NULL"; 542 name_ = name_ ? name_ : "NULL";
543 lua_pushvalue( L, in_base); // {} f 543 lua_pushvalue( L, in_base); // {} f
544 lua_pushstring( L, name_); // {} f _name 544 lua_pushstring( L, name_); // {} f _name
545 lua_rawset( L, -3); // {} 545 lua_rawset( L, -3); // {}
546 lua_pushstring( L, name_); // {} _name 546 lua_pushstring( L, name_); // {} _name
547 lua_pushvalue( L, in_base); // {} _name f 547 lua_pushvalue( L, in_base); // {} _name f
548 lua_rawset( L, -3); // {} 548 lua_rawset( L, -3); // {}
549 lua_pop( L, 1); // 549 lua_pop( L, 1); //
550 } 550 }
551 else if( lua_type( L, in_base) == LUA_TTABLE) 551 else if( lua_type( L, in_base) == LUA_TTABLE)
552 { 552 {
553 lua_newtable( L); // {} {fqn} 553 lua_newtable( L); // {} {fqn}
554 if( name_) 554 if( name_)
555 { 555 {
556 STACK_MID( L, 2); 556 STACK_MID( L, 2);
557 lua_pushstring( L, name_); // {} {fqn} "name" 557 lua_pushstring( L, name_); // {} {fqn} "name"
558 // generate a name, and if we already had one name, keep whichever is the shorter 558 // generate a name, and if we already had one name, keep whichever is the shorter
559 lua_pushvalue( L, in_base); // {} {fqn} "name" t 559 lua_pushvalue( L, in_base); // {} {fqn} "name" t
560 update_lookup_entry( DEBUGSPEW_PARAM_COMMA( U) L, ctx_base, start_depth); // {} {fqn} "name" 560 update_lookup_entry( DEBUGSPEW_PARAM_COMMA( U) L, ctx_base, start_depth); // {} {fqn} "name"
561 // don't forget to store the name at the bottom of the fqn stack 561 // don't forget to store the name at the bottom of the fqn stack
562 ++ start_depth; 562 ++ start_depth;
563 lua_rawseti( L, -2, start_depth); // {} {fqn} 563 lua_rawseti( L, -2, start_depth); // {} {fqn}
564 STACK_MID( L, 2); 564 STACK_MID( L, 2);
565 } 565 }
566 // retrieve the cache, create it if we haven't done it yet 566 // retrieve the cache, create it if we haven't done it yet
567 REGISTRY_GET( L, LOOKUPCACHE_REGKEY); // {} {fqn} {cache}? 567 REGISTRY_GET( L, LOOKUPCACHE_REGKEY); // {} {fqn} {cache}?
568 if( lua_isnil( L, -1)) 568 if( lua_isnil( L, -1))
569 { 569 {
570 lua_pop( L, 1); // {} {fqn} 570 lua_pop( L, 1); // {} {fqn}
571 lua_newtable( L); // {} {fqn} {cache} 571 lua_newtable( L); // {} {fqn} {cache}
572 REGISTRY_SET( L, LOOKUPCACHE_REGKEY, lua_pushvalue( L, -2)); 572 REGISTRY_SET( L, LOOKUPCACHE_REGKEY, lua_pushvalue( L, -2));
573 STACK_MID( L, 3); 573 STACK_MID( L, 3);
574 } 574 }
575 // process everything we find in that table, filling in lookup data for all functions and tables we see there 575 // process everything we find in that table, filling in lookup data for all functions and tables we see there
576 populate_func_lookup_table_recur( DEBUGSPEW_PARAM_COMMA( U) L, ctx_base, in_base, start_depth); 576 populate_func_lookup_table_recur( DEBUGSPEW_PARAM_COMMA( U) L, ctx_base, in_base, start_depth);
577 lua_pop( L, 3); 577 lua_pop( L, 3);
578 } 578 }
579 else 579 else
580 { 580 {
581 lua_pop( L, 1); // 581 lua_pop( L, 1); //
582 (void) luaL_error( L, "unsupported module type %s", lua_typename( L, lua_type( L, in_base))); 582 (void) luaL_error( L, "unsupported module type %s", lua_typename( L, lua_type( L, in_base)));
583 } 583 }
584 STACK_END( L, 0); 584 STACK_END( L, 0);
585 DEBUGSPEW_CODE( -- U->debugspew_indent_depth); 585 DEBUGSPEW_CODE( -- U->debugspew_indent_depth);
586} 586}
587 587
588/*---=== Inter-state copying ===---*/ 588/*---=== Inter-state copying ===---*/
@@ -595,61 +595,61 @@ static DECLARE_CONST_UNIQUE_KEY( REG_MTID, 0x2e68f9b4751584dc);
595*/ 595*/
596static lua_Integer get_mt_id( Universe* U, lua_State* L, int i) 596static lua_Integer get_mt_id( Universe* U, lua_State* L, int i)
597{ 597{
598 lua_Integer id; 598 lua_Integer id;
599 599
600 i = lua_absindex( L, i); 600 i = lua_absindex( L, i);
601 601
602 STACK_GROW( L, 3); 602 STACK_GROW( L, 3);
603 603
604 STACK_CHECK( L, 0); 604 STACK_CHECK( L, 0);
605 push_registry_subtable( L, REG_MTID); // ... _R[REG_MTID] 605 push_registry_subtable( L, REG_MTID); // ... _R[REG_MTID]
606 lua_pushvalue( L, i); // ... _R[REG_MTID] {mt} 606 lua_pushvalue( L, i); // ... _R[REG_MTID] {mt}
607 lua_rawget( L, -2); // ... _R[REG_MTID] mtk? 607 lua_rawget( L, -2); // ... _R[REG_MTID] mtk?
608 608
609 id = lua_tointeger( L, -1); // 0 for nil 609 id = lua_tointeger( L, -1); // 0 for nil
610 lua_pop( L, 1); // ... _R[REG_MTID] 610 lua_pop( L, 1); // ... _R[REG_MTID]
611 STACK_MID( L, 1); 611 STACK_MID( L, 1);
612
613 if( id == 0)
614 {
615 MUTEX_LOCK( &U->mtid_lock);
616 id = ++ U->last_mt_id;
617 MUTEX_UNLOCK( &U->mtid_lock);
618
619 /* Create two-way references: id_uint <-> table
620 */
621 lua_pushvalue( L, i); // ... _R[REG_MTID] {mt}
622 lua_pushinteger( L, id); // ... _R[REG_MTID] {mt} id
623 lua_rawset( L, -3); // ... _R[REG_MTID]
624 612
625 lua_pushinteger( L, id); // ... _R[REG_MTID] id 613 if( id == 0)
626 lua_pushvalue( L, i); // ... _R[REG_MTID] id {mt} 614 {
627 lua_rawset( L, -3); // ... _R[REG_MTID] 615 MUTEX_LOCK( &U->mtid_lock);
628 } 616 id = ++ U->last_mt_id;
629 lua_pop( L, 1); // ... 617 MUTEX_UNLOCK( &U->mtid_lock);
618
619 /* Create two-way references: id_uint <-> table
620 */
621 lua_pushvalue( L, i); // ... _R[REG_MTID] {mt}
622 lua_pushinteger( L, id); // ... _R[REG_MTID] {mt} id
623 lua_rawset( L, -3); // ... _R[REG_MTID]
624
625 lua_pushinteger( L, id); // ... _R[REG_MTID] id
626 lua_pushvalue( L, i); // ... _R[REG_MTID] id {mt}
627 lua_rawset( L, -3); // ... _R[REG_MTID]
628 }
629 lua_pop( L, 1); // ...
630 630
631 STACK_END( L, 0); 631 STACK_END( L, 0);
632 632
633 return id; 633 return id;
634} 634}
635 635
636// function sentinel used to transfer native functions from/to keeper states 636// function sentinel used to transfer native functions from/to keeper states
637static int func_lookup_sentinel( lua_State* L) 637static int func_lookup_sentinel( lua_State* L)
638{ 638{
639 return luaL_error( L, "function lookup sentinel for %s, should never be called", lua_tostring( L, lua_upvalueindex( 1))); 639 return luaL_error( L, "function lookup sentinel for %s, should never be called", lua_tostring( L, lua_upvalueindex( 1)));
640} 640}
641 641
642 642
643// function sentinel used to transfer native table from/to keeper states 643// function sentinel used to transfer native table from/to keeper states
644static int table_lookup_sentinel( lua_State* L) 644static int table_lookup_sentinel( lua_State* L)
645{ 645{
646 return luaL_error( L, "table lookup sentinel for %s, should never be called", lua_tostring( L, lua_upvalueindex( 1))); 646 return luaL_error( L, "table lookup sentinel for %s, should never be called", lua_tostring( L, lua_upvalueindex( 1)));
647} 647}
648 648
649// function sentinel used to transfer cloned full userdata from/to keeper states 649// function sentinel used to transfer cloned full userdata from/to keeper states
650static int userdata_clone_sentinel( lua_State* L) 650static int userdata_clone_sentinel( lua_State* L)
651{ 651{
652 return luaL_error( L, "userdata clone sentinel for %s, should never be called", lua_tostring( L, lua_upvalueindex( 1))); 652 return luaL_error( L, "userdata clone sentinel for %s, should never be called", lua_tostring( L, lua_upvalueindex( 1)));
653} 653}
654 654
655/* 655/*
@@ -657,70 +657,70 @@ static int userdata_clone_sentinel( lua_State* L)
657 */ 657 */
658static char const* find_lookup_name( lua_State* L, uint_t i, LookupMode mode_, char const* upName_, size_t* len_) 658static char const* find_lookup_name( lua_State* L, uint_t i, LookupMode mode_, char const* upName_, size_t* len_)
659{ 659{
660 DEBUGSPEW_CODE( Universe* const U = universe_get( L)); 660 DEBUGSPEW_CODE( Universe* const U = universe_get( L));
661 char const* fqn; 661 char const* fqn;
662 ASSERT_L( lua_isfunction( L, i) || lua_istable( L, i)); // ... v ... 662 ASSERT_L( lua_isfunction( L, i) || lua_istable( L, i)); // ... v ...
663 STACK_CHECK( L, 0); 663 STACK_CHECK( L, 0);
664 STACK_GROW( L, 3); // up to 3 slots are necessary on error 664 STACK_GROW( L, 3); // up to 3 slots are necessary on error
665 if( mode_ == eLM_FromKeeper) 665 if( mode_ == eLM_FromKeeper)
666 { 666 {
667 lua_CFunction f = lua_tocfunction( L, i); // should *always* be func_lookup_sentinel or table_lookup_sentinel! 667 lua_CFunction f = lua_tocfunction( L, i); // should *always* be func_lookup_sentinel or table_lookup_sentinel!
668 if( f == func_lookup_sentinel || f == table_lookup_sentinel || f == userdata_clone_sentinel) 668 if( f == func_lookup_sentinel || f == table_lookup_sentinel || f == userdata_clone_sentinel)
669 { 669 {
670 lua_getupvalue( L, i, 1); // ... v ... "f.q.n" 670 lua_getupvalue( L, i, 1); // ... v ... "f.q.n"
671 } 671 }
672 else 672 else
673 { 673 {
674 // if this is not a sentinel, this is some user-created table we wanted to lookup 674 // if this is not a sentinel, this is some user-created table we wanted to lookup
675 ASSERT_L( NULL == f && lua_istable( L, i)); 675 ASSERT_L( NULL == f && lua_istable( L, i));
676 // push anything that will convert to NULL string 676 // push anything that will convert to NULL string
677 lua_pushnil( L); // ... v ... nil 677 lua_pushnil( L); // ... v ... nil
678 } 678 }
679 } 679 }
680 else 680 else
681 { 681 {
682 // fetch the name from the source state's lookup table 682 // fetch the name from the source state's lookup table
683 REGISTRY_GET( L, LOOKUP_REGKEY); // ... v ... {} 683 REGISTRY_GET( L, LOOKUP_REGKEY); // ... v ... {}
684 STACK_MID( L, 1); 684 STACK_MID( L, 1);
685 ASSERT_L( lua_istable( L, -1)); 685 ASSERT_L( lua_istable( L, -1));
686 lua_pushvalue( L, i); // ... v ... {} v 686 lua_pushvalue( L, i); // ... v ... {} v
687 lua_rawget( L, -2); // ... v ... {} "f.q.n" 687 lua_rawget( L, -2); // ... v ... {} "f.q.n"
688 } 688 }
689 fqn = lua_tolstring( L, -1, len_); 689 fqn = lua_tolstring( L, -1, len_);
690 DEBUGSPEW_CODE( fprintf( stderr, INDENT_BEGIN "function [C] %s \n" INDENT_END, fqn)); 690 DEBUGSPEW_CODE( fprintf( stderr, INDENT_BEGIN "function [C] %s \n" INDENT_END, fqn));
691 // popping doesn't invalidate the pointer since this is an interned string gotten from the lookup database 691 // popping doesn't invalidate the pointer since this is an interned string gotten from the lookup database
692 lua_pop( L, (mode_ == eLM_FromKeeper) ? 1 : 2); // ... v ... 692 lua_pop( L, (mode_ == eLM_FromKeeper) ? 1 : 2); // ... v ...
693 STACK_MID( L, 0); 693 STACK_MID( L, 0);
694 if( NULL == fqn && !lua_istable( L, i)) // raise an error if we try to send an unknown function (but not for tables) 694 if( NULL == fqn && !lua_istable( L, i)) // raise an error if we try to send an unknown function (but not for tables)
695 { 695 {
696 char const *from, *typewhat, *what, *gotchaA, *gotchaB; 696 char const *from, *typewhat, *what, *gotchaA, *gotchaB;
697 // try to discover the name of the function we want to send 697 // try to discover the name of the function we want to send
698 lua_getglobal( L, "decoda_name"); // ... v ... decoda_name 698 lua_getglobal( L, "decoda_name"); // ... v ... decoda_name
699 from = lua_tostring( L, -1); 699 from = lua_tostring( L, -1);
700 lua_pushcfunction( L, luaG_nameof); // ... v ... decoda_name luaG_nameof 700 lua_pushcfunction( L, luaG_nameof); // ... v ... decoda_name luaG_nameof
701 lua_pushvalue( L, i); // ... v ... decoda_name luaG_nameof t 701 lua_pushvalue( L, i); // ... v ... decoda_name luaG_nameof t
702 lua_call( L, 1, 2); // ... v ... decoda_name "type" "name"|nil 702 lua_call( L, 1, 2); // ... v ... decoda_name "type" "name"|nil
703 typewhat = (lua_type( L, -2) == LUA_TSTRING) ? lua_tostring( L, -2) : luaL_typename( L, -2); 703 typewhat = (lua_type( L, -2) == LUA_TSTRING) ? lua_tostring( L, -2) : luaL_typename( L, -2);
704 // second return value can be nil if the table was not found 704 // second return value can be nil if the table was not found
705 // probable reason: the function was removed from the source Lua state before Lanes was required. 705 // probable reason: the function was removed from the source Lua state before Lanes was required.
706 if( lua_isnil( L, -1)) 706 if( lua_isnil( L, -1))
707 { 707 {
708 gotchaA = " referenced by"; 708 gotchaA = " referenced by";
709 gotchaB = "\n(did you remove it from the source Lua state before requiring Lanes?)"; 709 gotchaB = "\n(did you remove it from the source Lua state before requiring Lanes?)";
710 what = upName_; 710 what = upName_;
711 } 711 }
712 else 712 else
713 { 713 {
714 gotchaA = ""; 714 gotchaA = "";
715 gotchaB = ""; 715 gotchaB = "";
716 what = (lua_type( L, -1) == LUA_TSTRING) ? lua_tostring( L, -1) : luaL_typename( L, -1); 716 what = (lua_type( L, -1) == LUA_TSTRING) ? lua_tostring( L, -1) : luaL_typename( L, -1);
717 } 717 }
718 (void) luaL_error( L, "%s%s '%s' not found in %s origin transfer database.%s", typewhat, gotchaA, what, from ? from : "main", gotchaB); 718 (void) luaL_error( L, "%s%s '%s' not found in %s origin transfer database.%s", typewhat, gotchaA, what, from ? from : "main", gotchaB);
719 *len_ = 0; 719 *len_ = 0;
720 return NULL; 720 return NULL;
721 } 721 }
722 STACK_END( L, 0); 722 STACK_END( L, 0);
723 return fqn; 723 return fqn;
724} 724}
725 725
726 726
@@ -729,67 +729,67 @@ static char const* find_lookup_name( lua_State* L, uint_t i, LookupMode mode_, c
729 */ 729 */
730static bool_t lookup_table( lua_State* L2, lua_State* L, uint_t i, LookupMode mode_, char const* upName_) 730static bool_t lookup_table( lua_State* L2, lua_State* L, uint_t i, LookupMode mode_, char const* upName_)
731{ 731{
732 // get the name of the table we want to send 732 // get the name of the table we want to send
733 size_t len; 733 size_t len;
734 char const* fqn = find_lookup_name( L, i, mode_, upName_, &len); 734 char const* fqn = find_lookup_name( L, i, mode_, upName_, &len);
735 if( NULL == fqn) // name not found, it is some user-created table 735 if( NULL == fqn) // name not found, it is some user-created table
736 { 736 {
737 return FALSE; 737 return FALSE;
738 } 738 }
739 // push the equivalent table in the destination's stack, retrieved from the lookup table 739 // push the equivalent table in the destination's stack, retrieved from the lookup table
740 STACK_CHECK( L2, 0); // L // L2 740 STACK_CHECK( L2, 0); // L // L2
741 STACK_GROW( L2, 3); // up to 3 slots are necessary on error 741 STACK_GROW( L2, 3); // up to 3 slots are necessary on error
742 switch( mode_) 742 switch( mode_)
743 { 743 {
744 default: // shouldn't happen, in theory... 744 default: // shouldn't happen, in theory...
745 (void) luaL_error( L, "internal error: unknown lookup mode"); 745 (void) luaL_error( L, "internal error: unknown lookup mode");
746 return FALSE; 746 return FALSE;
747 747
748 case eLM_ToKeeper: 748 case eLM_ToKeeper:
749 // push a sentinel closure that holds the lookup name as upvalue 749 // push a sentinel closure that holds the lookup name as upvalue
750 lua_pushlstring( L2, fqn, len); // "f.q.n" 750 lua_pushlstring( L2, fqn, len); // "f.q.n"
751 lua_pushcclosure( L2, table_lookup_sentinel, 1); // f 751 lua_pushcclosure( L2, table_lookup_sentinel, 1); // f
752 break; 752 break;
753 753
754 case eLM_LaneBody: 754 case eLM_LaneBody:
755 case eLM_FromKeeper: 755 case eLM_FromKeeper:
756 REGISTRY_GET( L2, LOOKUP_REGKEY); // {} 756 REGISTRY_GET( L2, LOOKUP_REGKEY); // {}
757 STACK_MID( L2, 1); 757 STACK_MID( L2, 1);
758 ASSERT_L( lua_istable( L2, -1)); 758 ASSERT_L( lua_istable( L2, -1));
759 lua_pushlstring( L2, fqn, len); // {} "f.q.n" 759 lua_pushlstring( L2, fqn, len); // {} "f.q.n"
760 lua_rawget( L2, -2); // {} t 760 lua_rawget( L2, -2); // {} t
761 // we accept destination lookup failures in the case of transfering the Lanes body function (this will result in the source table being cloned instead) 761 // we accept destination lookup failures in the case of transfering the Lanes body function (this will result in the source table being cloned instead)
762 // but not when we extract something out of a keeper, as there is nothing to clone! 762 // but not when we extract something out of a keeper, as there is nothing to clone!
763 if( lua_isnil( L2, -1) && mode_ == eLM_LaneBody) 763 if( lua_isnil( L2, -1) && mode_ == eLM_LaneBody)
764 { 764 {
765 lua_pop( L2, 2); // 765 lua_pop( L2, 2); //
766 STACK_MID( L2, 0); 766 STACK_MID( L2, 0);
767 return FALSE; 767 return FALSE;
768 } 768 }
769 else if( !lua_istable( L2, -1)) 769 else if( !lua_istable( L2, -1))
770 { 770 {
771 char const* from, *to; 771 char const* from, *to;
772 lua_getglobal( L, "decoda_name"); // ... t ... decoda_name 772 lua_getglobal( L, "decoda_name"); // ... t ... decoda_name
773 from = lua_tostring( L, -1); 773 from = lua_tostring( L, -1);
774 lua_pop( L, 1); // ... t ... 774 lua_pop( L, 1); // ... t ...
775 lua_getglobal( L2, "decoda_name"); // {} t decoda_name 775 lua_getglobal( L2, "decoda_name"); // {} t decoda_name
776 to = lua_tostring( L2, -1); 776 to = lua_tostring( L2, -1);
777 lua_pop( L2, 1); // {} t 777 lua_pop( L2, 1); // {} t
778 // when mode_ == eLM_FromKeeper, L is a keeper state and L2 is not, therefore L2 is the state where we want to raise the error 778 // when mode_ == eLM_FromKeeper, L is a keeper state and L2 is not, therefore L2 is the state where we want to raise the error
779 (void) luaL_error( 779 (void) luaL_error(
780 (mode_ == eLM_FromKeeper) ? L2 : L 780 (mode_ == eLM_FromKeeper) ? L2 : L
781 , "INTERNAL ERROR IN %s: table '%s' not found in %s destination transfer database." 781 , "INTERNAL ERROR IN %s: table '%s' not found in %s destination transfer database."
782 , from ? from : "main" 782 , from ? from : "main"
783 , fqn 783 , fqn
784 , to ? to : "main" 784 , to ? to : "main"
785 ); 785 );
786 return FALSE; 786 return FALSE;
787 } 787 }
788 lua_remove( L2, -2); // t 788 lua_remove( L2, -2); // t
789 break; 789 break;
790 } 790 }
791 STACK_END( L2, 1); 791 STACK_END( L2, 1);
792 return TRUE; 792 return TRUE;
793} 793}
794 794
795 795
@@ -805,33 +805,33 @@ static bool_t lookup_table( lua_State* L2, lua_State* L, uint_t i, LookupMode mo
805 */ 805 */
806static bool_t push_cached_table( lua_State* L2, uint_t L2_cache_i, lua_State* L, uint_t i) 806static bool_t push_cached_table( lua_State* L2, uint_t L2_cache_i, lua_State* L, uint_t i)
807{ 807{
808 bool_t not_found_in_cache; // L2 808 bool_t not_found_in_cache; // L2
809 DECLARE_CONST_UNIQUE_KEY( p, lua_topointer( L, i)); 809 DECLARE_CONST_UNIQUE_KEY( p, lua_topointer( L, i));
810 810
811 ASSERT_L( L2_cache_i != 0); 811 ASSERT_L( L2_cache_i != 0);
812 STACK_GROW( L2, 3); 812 STACK_GROW( L2, 3);
813 STACK_CHECK( L2, 0); 813 STACK_CHECK( L2, 0);
814 814
815 // We don't need to use the from state ('L') in ID since the life span 815 // We don't need to use the from state ('L') in ID since the life span
816 // is only for the duration of a copy (both states are locked). 816 // is only for the duration of a copy (both states are locked).
817 // push a light userdata uniquely representing the table 817 // push a light userdata uniquely representing the table
818 push_unique_key( L2, p); // ... p 818 push_unique_key( L2, p); // ... p
819 819
820 //fprintf( stderr, "<< ID: %s >>\n", lua_tostring( L2, -1)); 820 //fprintf( stderr, "<< ID: %s >>\n", lua_tostring( L2, -1));
821 821
822 lua_rawget( L2, L2_cache_i); // ... {cached|nil} 822 lua_rawget( L2, L2_cache_i); // ... {cached|nil}
823 not_found_in_cache = lua_isnil( L2, -1); 823 not_found_in_cache = lua_isnil( L2, -1);
824 if( not_found_in_cache) 824 if( not_found_in_cache)
825 { 825 {
826 lua_pop( L2, 1); // ... 826 lua_pop( L2, 1); // ...
827 lua_newtable( L2); // ... {} 827 lua_newtable( L2); // ... {}
828 push_unique_key( L2, p); // ... {} p 828 push_unique_key( L2, p); // ... {} p
829 lua_pushvalue( L2, -2); // ... {} p {} 829 lua_pushvalue( L2, -2); // ... {} p {}
830 lua_rawset( L2, L2_cache_i); // ... {} 830 lua_rawset( L2, L2_cache_i); // ... {}
831 } 831 }
832 STACK_END( L2, 1); 832 STACK_END( L2, 1);
833 ASSERT_L( lua_istable( L2, -1)); 833 ASSERT_L( lua_istable( L2, -1));
834 return !not_found_in_cache; 834 return !not_found_in_cache;
835} 835}
836 836
837 837
@@ -840,144 +840,144 @@ static bool_t push_cached_table( lua_State* L2, uint_t L2_cache_i, lua_State* L,
840 */ 840 */
841static int discover_object_name_recur( lua_State* L, int shortest_, int depth_) 841static int discover_object_name_recur( lua_State* L, int shortest_, int depth_)
842{ 842{
843 int const what = 1; // o "r" {c} {fqn} ... {?} 843 int const what = 1; // o "r" {c} {fqn} ... {?}
844 int const result = 2; 844 int const result = 2;
845 int const cache = 3; 845 int const cache = 3;
846 int const fqn = 4; 846 int const fqn = 4;
847 // no need to scan this table if the name we will discover is longer than one we already know 847 // no need to scan this table if the name we will discover is longer than one we already know
848 if( shortest_ <= depth_ + 1) 848 if( shortest_ <= depth_ + 1)
849 { 849 {
850 return shortest_; 850 return shortest_;
851 } 851 }
852 STACK_GROW( L, 3); 852 STACK_GROW( L, 3);
853 STACK_CHECK( L, 0); 853 STACK_CHECK( L, 0);
854 // stack top contains the table to search in 854 // stack top contains the table to search in
855 lua_pushvalue( L, -1); // o "r" {c} {fqn} ... {?} {?} 855 lua_pushvalue( L, -1); // o "r" {c} {fqn} ... {?} {?}
856 lua_rawget( L, cache); // o "r" {c} {fqn} ... {?} nil/1 856 lua_rawget( L, cache); // o "r" {c} {fqn} ... {?} nil/1
857 // if table is already visited, we are done 857 // if table is already visited, we are done
858 if( !lua_isnil( L, -1)) 858 if( !lua_isnil( L, -1))
859 { 859 {
860 lua_pop( L, 1); // o "r" {c} {fqn} ... {?} 860 lua_pop( L, 1); // o "r" {c} {fqn} ... {?}
861 return shortest_; 861 return shortest_;
862 } 862 }
863 // examined table is not in the cache, add it now 863 // examined table is not in the cache, add it now
864 lua_pop( L, 1); // o "r" {c} {fqn} ... {?} 864 lua_pop( L, 1); // o "r" {c} {fqn} ... {?}
865 lua_pushvalue( L, -1); // o "r" {c} {fqn} ... {?} {?} 865 lua_pushvalue( L, -1); // o "r" {c} {fqn} ... {?} {?}
866 lua_pushinteger( L, 1); // o "r" {c} {fqn} ... {?} {?} 1 866 lua_pushinteger( L, 1); // o "r" {c} {fqn} ... {?} {?} 1
867 lua_rawset( L, cache); // o "r" {c} {fqn} ... {?} 867 lua_rawset( L, cache); // o "r" {c} {fqn} ... {?}
868 // scan table contents 868 // scan table contents
869 lua_pushnil( L); // o "r" {c} {fqn} ... {?} nil 869 lua_pushnil( L); // o "r" {c} {fqn} ... {?} nil
870 while( lua_next( L, -2)) // o "r" {c} {fqn} ... {?} k v 870 while( lua_next( L, -2)) // o "r" {c} {fqn} ... {?} k v
871 { 871 {
872 //char const *const strKey = (lua_type( L, -2) == LUA_TSTRING) ? lua_tostring( L, -2) : NULL; // only for debugging 872 //char const *const strKey = (lua_type( L, -2) == LUA_TSTRING) ? lua_tostring( L, -2) : NULL; // only for debugging
873 //lua_Number const numKey = (lua_type( L, -2) == LUA_TNUMBER) ? lua_tonumber( L, -2) : -6666; // only for debugging 873 //lua_Number const numKey = (lua_type( L, -2) == LUA_TNUMBER) ? lua_tonumber( L, -2) : -6666; // only for debugging
874 STACK_MID( L, 2); 874 STACK_MID( L, 2);
875 // append key name to fqn stack 875 // append key name to fqn stack
876 ++ depth_; 876 ++ depth_;
877 lua_pushvalue( L, -2); // o "r" {c} {fqn} ... {?} k v k 877 lua_pushvalue( L, -2); // o "r" {c} {fqn} ... {?} k v k
878 lua_rawseti( L, fqn, depth_); // o "r" {c} {fqn} ... {?} k v 878 lua_rawseti( L, fqn, depth_); // o "r" {c} {fqn} ... {?} k v
879 if( lua_rawequal( L, -1, what)) // is it what we are looking for? 879 if( lua_rawequal( L, -1, what)) // is it what we are looking for?
880 { 880 {
881 STACK_MID( L, 2); 881 STACK_MID( L, 2);
882 // update shortest name 882 // update shortest name
883 if( depth_ < shortest_) 883 if( depth_ < shortest_)
884 { 884 {
885 shortest_ = depth_; 885 shortest_ = depth_;
886 luaG_pushFQN( L, fqn, depth_, NULL); // o "r" {c} {fqn} ... {?} k v "fqn" 886 luaG_pushFQN( L, fqn, depth_, NULL); // o "r" {c} {fqn} ... {?} k v "fqn"
887 lua_replace( L, result); // o "r" {c} {fqn} ... {?} k v 887 lua_replace( L, result); // o "r" {c} {fqn} ... {?} k v
888 } 888 }
889 // no need to search further at this level 889 // no need to search further at this level
890 lua_pop( L, 2); // o "r" {c} {fqn} ... {?} 890 lua_pop( L, 2); // o "r" {c} {fqn} ... {?}
891 STACK_MID( L, 0); 891 STACK_MID( L, 0);
892 break; 892 break;
893 } 893 }
894 switch( lua_type( L, -1)) // o "r" {c} {fqn} ... {?} k v 894 switch( lua_type( L, -1)) // o "r" {c} {fqn} ... {?} k v
895 { 895 {
896 default: // nil, boolean, light userdata, number and string aren't identifiable 896 default: // nil, boolean, light userdata, number and string aren't identifiable
897 break; 897 break;
898 898
899 case LUA_TTABLE: // o "r" {c} {fqn} ... {?} k {} 899 case LUA_TTABLE: // o "r" {c} {fqn} ... {?} k {}
900 STACK_MID( L, 2); 900 STACK_MID( L, 2);
901 shortest_ = discover_object_name_recur( L, shortest_, depth_); 901 shortest_ = discover_object_name_recur( L, shortest_, depth_);
902 // search in the table's metatable too 902 // search in the table's metatable too
903 if( lua_getmetatable( L, -1)) // o "r" {c} {fqn} ... {?} k {} {mt} 903 if( lua_getmetatable( L, -1)) // o "r" {c} {fqn} ... {?} k {} {mt}
904 { 904 {
905 if( lua_istable( L, -1)) 905 if( lua_istable( L, -1))
906 { 906 {
907 ++ depth_; 907 ++ depth_;
908 lua_pushliteral( L, "__metatable"); // o "r" {c} {fqn} ... {?} k {} {mt} "__metatable" 908 lua_pushliteral( L, "__metatable"); // o "r" {c} {fqn} ... {?} k {} {mt} "__metatable"
909 lua_rawseti( L, fqn, depth_); // o "r" {c} {fqn} ... {?} k {} {mt} 909 lua_rawseti( L, fqn, depth_); // o "r" {c} {fqn} ... {?} k {} {mt}
910 shortest_ = discover_object_name_recur( L, shortest_, depth_); 910 shortest_ = discover_object_name_recur( L, shortest_, depth_);
911 lua_pushnil( L); // o "r" {c} {fqn} ... {?} k {} {mt} nil 911 lua_pushnil( L); // o "r" {c} {fqn} ... {?} k {} {mt} nil
912 lua_rawseti( L, fqn, depth_); // o "r" {c} {fqn} ... {?} k {} {mt} 912 lua_rawseti( L, fqn, depth_); // o "r" {c} {fqn} ... {?} k {} {mt}
913 -- depth_; 913 -- depth_;
914 } 914 }
915 lua_pop( L, 1); // o "r" {c} {fqn} ... {?} k {} 915 lua_pop( L, 1); // o "r" {c} {fqn} ... {?} k {}
916 } 916 }
917 STACK_MID( L, 2); 917 STACK_MID( L, 2);
918 break; 918 break;
919 919
920 case LUA_TTHREAD: // o "r" {c} {fqn} ... {?} k T 920 case LUA_TTHREAD: // o "r" {c} {fqn} ... {?} k T
921 // TODO: explore the thread's stack frame looking for our culprit? 921 // TODO: explore the thread's stack frame looking for our culprit?
922 break; 922 break;
923 923
924 case LUA_TUSERDATA: // o "r" {c} {fqn} ... {?} k U 924 case LUA_TUSERDATA: // o "r" {c} {fqn} ... {?} k U
925 STACK_MID( L, 2); 925 STACK_MID( L, 2);
926 // search in the object's metatable (some modules are built that way) 926 // search in the object's metatable (some modules are built that way)
927 if( lua_getmetatable( L, -1)) // o "r" {c} {fqn} ... {?} k U {mt} 927 if( lua_getmetatable( L, -1)) // o "r" {c} {fqn} ... {?} k U {mt}
928 { 928 {
929 if( lua_istable( L, -1)) 929 if( lua_istable( L, -1))
930 { 930 {
931 ++ depth_; 931 ++ depth_;
932 lua_pushliteral( L, "__metatable"); // o "r" {c} {fqn} ... {?} k U {mt} "__metatable" 932 lua_pushliteral( L, "__metatable"); // o "r" {c} {fqn} ... {?} k U {mt} "__metatable"
933 lua_rawseti( L, fqn, depth_); // o "r" {c} {fqn} ... {?} k U {mt} 933 lua_rawseti( L, fqn, depth_); // o "r" {c} {fqn} ... {?} k U {mt}
934 shortest_ = discover_object_name_recur( L, shortest_, depth_); 934 shortest_ = discover_object_name_recur( L, shortest_, depth_);
935 lua_pushnil( L); // o "r" {c} {fqn} ... {?} k U {mt} nil 935 lua_pushnil( L); // o "r" {c} {fqn} ... {?} k U {mt} nil
936 lua_rawseti( L, fqn, depth_); // o "r" {c} {fqn} ... {?} k U {mt} 936 lua_rawseti( L, fqn, depth_); // o "r" {c} {fqn} ... {?} k U {mt}
937 -- depth_; 937 -- depth_;
938 } 938 }
939 lua_pop( L, 1); // o "r" {c} {fqn} ... {?} k U 939 lua_pop( L, 1); // o "r" {c} {fqn} ... {?} k U
940 } 940 }
941 STACK_MID( L, 2); 941 STACK_MID( L, 2);
942 // search in the object's uservalues 942 // search in the object's uservalues
943 { 943 {
944 int uvi = 1; 944 int uvi = 1;
945 while( lua_getiuservalue( L, -1, uvi) != LUA_TNONE) // o "r" {c} {fqn} ... {?} k U {u} 945 while( lua_getiuservalue( L, -1, uvi) != LUA_TNONE) // o "r" {c} {fqn} ... {?} k U {u}
946 { 946 {
947 if( lua_istable( L, -1)) // if it is a table, look inside 947 if( lua_istable( L, -1)) // if it is a table, look inside
948 { 948 {
949 ++ depth_; 949 ++ depth_;
950 lua_pushliteral( L, "uservalue"); // o "r" {c} {fqn} ... {?} k v {u} "uservalue" 950 lua_pushliteral( L, "uservalue"); // o "r" {c} {fqn} ... {?} k v {u} "uservalue"
951 lua_rawseti( L, fqn, depth_); // o "r" {c} {fqn} ... {?} k v {u} 951 lua_rawseti( L, fqn, depth_); // o "r" {c} {fqn} ... {?} k v {u}
952 shortest_ = discover_object_name_recur( L, shortest_, depth_); 952 shortest_ = discover_object_name_recur( L, shortest_, depth_);
953 lua_pushnil( L); // o "r" {c} {fqn} ... {?} k v {u} nil 953 lua_pushnil( L); // o "r" {c} {fqn} ... {?} k v {u} nil
954 lua_rawseti( L, fqn, depth_); // o "r" {c} {fqn} ... {?} k v {u} 954 lua_rawseti( L, fqn, depth_); // o "r" {c} {fqn} ... {?} k v {u}
955 -- depth_; 955 -- depth_;
956 } 956 }
957 lua_pop( L, 1); // o "r" {c} {fqn} ... {?} k U 957 lua_pop( L, 1); // o "r" {c} {fqn} ... {?} k U
958 ++ uvi; 958 ++ uvi;
959 } 959 }
960 // when lua_getiuservalue() returned LUA_TNONE, it pushed a nil. pop it now 960 // when lua_getiuservalue() returned LUA_TNONE, it pushed a nil. pop it now
961 lua_pop( L, 1); // o "r" {c} {fqn} ... {?} k U 961 lua_pop( L, 1); // o "r" {c} {fqn} ... {?} k U
962 } 962 }
963 STACK_MID( L, 2); 963 STACK_MID( L, 2);
964 break; 964 break;
965 } 965 }
966 // make ready for next iteration 966 // make ready for next iteration
967 lua_pop( L, 1); // o "r" {c} {fqn} ... {?} k 967 lua_pop( L, 1); // o "r" {c} {fqn} ... {?} k
968 // remove name from fqn stack 968 // remove name from fqn stack
969 lua_pushnil( L); // o "r" {c} {fqn} ... {?} k nil 969 lua_pushnil( L); // o "r" {c} {fqn} ... {?} k nil
970 lua_rawseti( L, fqn, depth_); // o "r" {c} {fqn} ... {?} k 970 lua_rawseti( L, fqn, depth_); // o "r" {c} {fqn} ... {?} k
971 STACK_MID( L, 1); 971 STACK_MID( L, 1);
972 -- depth_; 972 -- depth_;
973 } // o "r" {c} {fqn} ... {?} 973 } // o "r" {c} {fqn} ... {?}
974 STACK_MID( L, 0); 974 STACK_MID( L, 0);
975 // remove the visited table from the cache, in case a shorter path to the searched object exists 975 // remove the visited table from the cache, in case a shorter path to the searched object exists
976 lua_pushvalue( L, -1); // o "r" {c} {fqn} ... {?} {?} 976 lua_pushvalue( L, -1); // o "r" {c} {fqn} ... {?} {?}
977 lua_pushnil( L); // o "r" {c} {fqn} ... {?} {?} nil 977 lua_pushnil( L); // o "r" {c} {fqn} ... {?} {?} nil
978 lua_rawset( L, cache); // o "r" {c} {fqn} ... {?} 978 lua_rawset( L, cache); // o "r" {c} {fqn} ... {?}
979 STACK_END( L, 0); 979 STACK_END( L, 0);
980 return shortest_; 980 return shortest_;
981} 981}
982 982
983 983
@@ -986,46 +986,46 @@ static int discover_object_name_recur( lua_State* L, int shortest_, int depth_)
986 */ 986 */
987int luaG_nameof( lua_State* L) 987int luaG_nameof( lua_State* L)
988{ 988{
989 int what = lua_gettop( L); 989 int what = lua_gettop( L);
990 if( what > 1) 990 if( what > 1)
991 { 991 {
992 luaL_argerror( L, what, "too many arguments."); 992 luaL_argerror( L, what, "too many arguments.");
993 } 993 }
994 994
995 // nil, boolean, light userdata, number and string aren't identifiable 995 // nil, boolean, light userdata, number and string aren't identifiable
996 if( lua_type( L, 1) < LUA_TTABLE) 996 if( lua_type( L, 1) < LUA_TTABLE)
997 { 997 {
998 lua_pushstring( L, luaL_typename( L, 1)); // o "type" 998 lua_pushstring( L, luaL_typename( L, 1)); // o "type"
999 lua_insert( L, -2); // "type" o 999 lua_insert( L, -2); // "type" o
1000 return 2; 1000 return 2;
1001 } 1001 }
1002 1002
1003 STACK_GROW( L, 4); 1003 STACK_GROW( L, 4);
1004 STACK_CHECK( L, 0); 1004 STACK_CHECK( L, 0);
1005 // this slot will contain the shortest name we found when we are done 1005 // this slot will contain the shortest name we found when we are done
1006 lua_pushnil( L); // o nil 1006 lua_pushnil( L); // o nil
1007 // push a cache that will contain all already visited tables 1007 // push a cache that will contain all already visited tables
1008 lua_newtable( L); // o nil {c} 1008 lua_newtable( L); // o nil {c}
1009 // push a table whose contents are strings that, when concatenated, produce unique name 1009 // push a table whose contents are strings that, when concatenated, produce unique name
1010 lua_newtable( L); // o nil {c} {fqn} 1010 lua_newtable( L); // o nil {c} {fqn}
1011 lua_pushliteral( L, "_G"); // o nil {c} {fqn} "_G" 1011 lua_pushliteral( L, "_G"); // o nil {c} {fqn} "_G"
1012 lua_rawseti( L, -2, 1); // o nil {c} {fqn} 1012 lua_rawseti( L, -2, 1); // o nil {c} {fqn}
1013 // this is where we start the search 1013 // this is where we start the search
1014 lua_pushglobaltable( L); // o nil {c} {fqn} _G 1014 lua_pushglobaltable( L); // o nil {c} {fqn} _G
1015 (void) discover_object_name_recur( L, 6666, 1); 1015 (void) discover_object_name_recur( L, 6666, 1);
1016 if( lua_isnil( L, 2)) // try again with registry, just in case... 1016 if( lua_isnil( L, 2)) // try again with registry, just in case...
1017 { 1017 {
1018 lua_pop( L, 1); // o nil {c} {fqn} 1018 lua_pop( L, 1); // o nil {c} {fqn}
1019 lua_pushliteral( L, "_R"); // o nil {c} {fqn} "_R" 1019 lua_pushliteral( L, "_R"); // o nil {c} {fqn} "_R"
1020 lua_rawseti( L, -2, 1); // o nil {c} {fqn} 1020 lua_rawseti( L, -2, 1); // o nil {c} {fqn}
1021 lua_pushvalue( L, LUA_REGISTRYINDEX); // o nil {c} {fqn} _R 1021 lua_pushvalue( L, LUA_REGISTRYINDEX); // o nil {c} {fqn} _R
1022 (void) discover_object_name_recur( L, 6666, 1); 1022 (void) discover_object_name_recur( L, 6666, 1);
1023 } 1023 }
1024 lua_pop( L, 3); // o "result" 1024 lua_pop( L, 3); // o "result"
1025 STACK_END( L, 1); 1025 STACK_END( L, 1);
1026 lua_pushstring( L, luaL_typename( L, 1)); // o "result" "type" 1026 lua_pushstring( L, luaL_typename( L, 1)); // o "result" "type"
1027 lua_replace( L, -3); // "type" "result" 1027 lua_replace( L, -3); // "type" "result"
1028 return 2; 1028 return 2;
1029} 1029}
1030 1030
1031 1031
@@ -1034,73 +1034,73 @@ int luaG_nameof( lua_State* L)
1034 */ 1034 */
1035static void lookup_native_func( lua_State* L2, lua_State* L, uint_t i, LookupMode mode_, char const* upName_) 1035static void lookup_native_func( lua_State* L2, lua_State* L, uint_t i, LookupMode mode_, char const* upName_)
1036{ 1036{
1037 // get the name of the function we want to send 1037 // get the name of the function we want to send
1038 size_t len; 1038 size_t len;
1039 char const* fqn = find_lookup_name( L, i, mode_, upName_, &len); 1039 char const* fqn = find_lookup_name( L, i, mode_, upName_, &len);
1040 // push the equivalent function in the destination's stack, retrieved from the lookup table 1040 // push the equivalent function in the destination's stack, retrieved from the lookup table
1041 STACK_CHECK( L2, 0); // L // L2 1041 STACK_CHECK( L2, 0); // L // L2
1042 STACK_GROW( L2, 3); // up to 3 slots are necessary on error 1042 STACK_GROW( L2, 3); // up to 3 slots are necessary on error
1043 switch( mode_) 1043 switch( mode_)
1044 { 1044 {
1045 default: // shouldn't happen, in theory... 1045 default: // shouldn't happen, in theory...
1046 (void) luaL_error( L, "internal error: unknown lookup mode"); 1046 (void) luaL_error( L, "internal error: unknown lookup mode");
1047 return; 1047 return;
1048 1048
1049 case eLM_ToKeeper: 1049 case eLM_ToKeeper:
1050 // push a sentinel closure that holds the lookup name as upvalue 1050 // push a sentinel closure that holds the lookup name as upvalue
1051 lua_pushlstring( L2, fqn, len); // "f.q.n" 1051 lua_pushlstring( L2, fqn, len); // "f.q.n"
1052 lua_pushcclosure( L2, func_lookup_sentinel, 1); // f 1052 lua_pushcclosure( L2, func_lookup_sentinel, 1); // f
1053 break; 1053 break;
1054 1054
1055 case eLM_LaneBody: 1055 case eLM_LaneBody:
1056 case eLM_FromKeeper: 1056 case eLM_FromKeeper:
1057 REGISTRY_GET( L2, LOOKUP_REGKEY); // {} 1057 REGISTRY_GET( L2, LOOKUP_REGKEY); // {}
1058 STACK_MID( L2, 1); 1058 STACK_MID( L2, 1);
1059 ASSERT_L( lua_istable( L2, -1)); 1059 ASSERT_L( lua_istable( L2, -1));
1060 lua_pushlstring( L2, fqn, len); // {} "f.q.n" 1060 lua_pushlstring( L2, fqn, len); // {} "f.q.n"
1061 lua_rawget( L2, -2); // {} f 1061 lua_rawget( L2, -2); // {} f
1062 // nil means we don't know how to transfer stuff: user should do something 1062 // nil means we don't know how to transfer stuff: user should do something
1063 // anything other than function or table should not happen! 1063 // anything other than function or table should not happen!
1064 if( !lua_isfunction( L2, -1) && !lua_istable( L2, -1)) 1064 if( !lua_isfunction( L2, -1) && !lua_istable( L2, -1))
1065 { 1065 {
1066 char const* from, * to; 1066 char const* from, * to;
1067 lua_getglobal( L, "decoda_name"); // ... f ... decoda_name 1067 lua_getglobal( L, "decoda_name"); // ... f ... decoda_name
1068 from = lua_tostring( L, -1); 1068 from = lua_tostring( L, -1);
1069 lua_pop( L, 1); // ... f ... 1069 lua_pop( L, 1); // ... f ...
1070 lua_getglobal( L2, "decoda_name"); // {} f decoda_name 1070 lua_getglobal( L2, "decoda_name"); // {} f decoda_name
1071 to = lua_tostring( L2, -1); 1071 to = lua_tostring( L2, -1);
1072 lua_pop( L2, 1); // {} f 1072 lua_pop( L2, 1); // {} f
1073 // when mode_ == eLM_FromKeeper, L is a keeper state and L2 is not, therefore L2 is the state where we want to raise the error 1073 // when mode_ == eLM_FromKeeper, L is a keeper state and L2 is not, therefore L2 is the state where we want to raise the error
1074 (void) luaL_error( 1074 (void) luaL_error(
1075 (mode_ == eLM_FromKeeper) ? L2 : L 1075 (mode_ == eLM_FromKeeper) ? L2 : L
1076 , "%s%s: function '%s' not found in %s destination transfer database." 1076 , "%s%s: function '%s' not found in %s destination transfer database."
1077 , lua_isnil( L2, -1) ? "" : "INTERNAL ERROR IN " 1077 , lua_isnil( L2, -1) ? "" : "INTERNAL ERROR IN "
1078 , from ? from : "main" 1078 , from ? from : "main"
1079 , fqn 1079 , fqn
1080 , to ? to : "main" 1080 , to ? to : "main"
1081 ); 1081 );
1082 return; 1082 return;
1083 } 1083 }
1084 lua_remove( L2, -2); // f 1084 lua_remove( L2, -2); // f
1085 break; 1085 break;
1086 1086
1087 /* keep it in case I need it someday, who knows... 1087 /* keep it in case I need it someday, who knows...
1088 case eLM_RawFunctions: 1088 case eLM_RawFunctions:
1089 { 1089 {
1090 int n; 1090 int n;
1091 char const* upname; 1091 char const* upname;
1092 lua_CFunction f = lua_tocfunction( L, i); 1092 lua_CFunction f = lua_tocfunction( L, i);
1093 // copy upvalues 1093 // copy upvalues
1094 for( n = 0; (upname = lua_getupvalue( L, i, 1 + n)) != NULL; ++ n) 1094 for( n = 0; (upname = lua_getupvalue( L, i, 1 + n)) != NULL; ++ n)
1095 { 1095 {
1096 luaG_inter_move( U, L, L2, 1, mode_); // [up[,up ...]] 1096 luaG_inter_move( U, L, L2, 1, mode_); // [up[,up ...]]
1097 } 1097 }
1098 lua_pushcclosure( L2, f, n); // 1098 lua_pushcclosure( L2, f, n); //
1099 } 1099 }
1100 break; 1100 break;
1101 */ 1101 */
1102 } 1102 }
1103 STACK_END( L2, 1); 1103 STACK_END( L2, 1);
1104} 1104}
1105 1105
1106 1106
@@ -1112,23 +1112,23 @@ static void lookup_native_func( lua_State* L2, lua_State* L, uint_t i, LookupMod
1112#if USE_DEBUG_SPEW 1112#if USE_DEBUG_SPEW
1113static char const* lua_type_names[] = 1113static char const* lua_type_names[] =
1114{ 1114{
1115 "LUA_TNIL" 1115 "LUA_TNIL"
1116 , "LUA_TBOOLEAN" 1116 , "LUA_TBOOLEAN"
1117 , "LUA_TLIGHTUSERDATA" 1117 , "LUA_TLIGHTUSERDATA"
1118 , "LUA_TNUMBER" 1118 , "LUA_TNUMBER"
1119 , "LUA_TSTRING" 1119 , "LUA_TSTRING"
1120 , "LUA_TTABLE" 1120 , "LUA_TTABLE"
1121 , "LUA_TFUNCTION" 1121 , "LUA_TFUNCTION"
1122 , "LUA_TUSERDATA" 1122 , "LUA_TUSERDATA"
1123 , "LUA_TTHREAD" 1123 , "LUA_TTHREAD"
1124 , "<LUA_NUMTAGS>" // not really a type 1124 , "<LUA_NUMTAGS>" // not really a type
1125 , "LUA_TJITCDATA" // LuaJIT specific 1125 , "LUA_TJITCDATA" // LuaJIT specific
1126}; 1126};
1127static char const* vt_names[] = 1127static char const* vt_names[] =
1128{ 1128{
1129 "VT_NORMAL" 1129 "VT_NORMAL"
1130 , "VT_KEY" 1130 , "VT_KEY"
1131 , "VT_METATABLE" 1131 , "VT_METATABLE"
1132}; 1132};
1133#endif // USE_DEBUG_SPEW 1133#endif // USE_DEBUG_SPEW
1134 1134
@@ -1149,148 +1149,148 @@ static int buf_writer( lua_State* L, void const* b, size_t size, void* ud)
1149 1149
1150static void copy_func( Universe* U, lua_State* L2, uint_t L2_cache_i, lua_State* L, uint_t i, LookupMode mode_, char const* upName_) 1150static void copy_func( Universe* U, lua_State* L2, uint_t L2_cache_i, lua_State* L, uint_t i, LookupMode mode_, char const* upName_)
1151{ 1151{
1152 int n, needToPush; 1152 int n, needToPush;
1153 luaL_Buffer B; 1153 luaL_Buffer B;
1154 B.L = NULL; 1154 B.L = NULL;
1155 1155
1156 ASSERT_L( L2_cache_i != 0); // ... {cache} ... p 1156 ASSERT_L( L2_cache_i != 0); // ... {cache} ... p
1157 STACK_GROW( L, 2); 1157 STACK_GROW( L, 2);
1158 STACK_CHECK( L, 0); 1158 STACK_CHECK( L, 0);
1159 1159
1160 1160
1161 // 'lua_dump()' needs the function at top of stack 1161 // 'lua_dump()' needs the function at top of stack
1162 // if already on top of the stack, no need to push again 1162 // if already on top of the stack, no need to push again
1163 needToPush = (i != (uint_t)lua_gettop( L)); 1163 needToPush = (i != (uint_t)lua_gettop( L));
1164 if( needToPush) 1164 if( needToPush)
1165 { 1165 {
1166 lua_pushvalue( L, i); // ... f 1166 lua_pushvalue( L, i); // ... f
1167 } 1167 }
1168 1168
1169 // 1169 //
1170 // "value returned is the error code returned by the last call 1170 // "value returned is the error code returned by the last call
1171 // to the writer" (and we only return 0) 1171 // to the writer" (and we only return 0)
1172 // not sure this could ever fail but for memory shortage reasons 1172 // not sure this could ever fail but for memory shortage reasons
1173 // last parameter is Lua 5.4-specific (no stripping) 1173 // last parameter is Lua 5.4-specific (no stripping)
1174 if( lua504_dump( L, buf_writer, &B, 0) != 0) 1174 if( lua504_dump( L, buf_writer, &B, 0) != 0)
1175 { 1175 {
1176 luaL_error( L, "internal error: function dump failed."); 1176 luaL_error( L, "internal error: function dump failed.");
1177 } 1177 }
1178 1178
1179 // pushes dumped string on 'L' 1179 // pushes dumped string on 'L'
1180 luaL_pushresult( &B); // ... f b 1180 luaL_pushresult( &B); // ... f b
1181 1181
1182 // if not pushed, no need to pop 1182 // if not pushed, no need to pop
1183 if( needToPush) 1183 if( needToPush)
1184 { 1184 {
1185 lua_remove( L, -2); // ... b 1185 lua_remove( L, -2); // ... b
1186 } 1186 }
1187 1187
1188 // transfer the bytecode, then the upvalues, to create a similar closure 1188 // transfer the bytecode, then the upvalues, to create a similar closure
1189 { 1189 {
1190 char const* name = NULL; 1190 char const* name = NULL;
1191 1191
1192 #if LOG_FUNC_INFO 1192 #if LOG_FUNC_INFO
1193 // "To get information about a function you push it onto the 1193 // "To get information about a function you push it onto the
1194 // stack and start the what string with the character '>'." 1194 // stack and start the what string with the character '>'."
1195 // 1195 //
1196 { 1196 {
1197 lua_Debug ar; 1197 lua_Debug ar;
1198 lua_pushvalue( L, i); // ... b f 1198 lua_pushvalue( L, i); // ... b f
1199 // fills 'name' 'namewhat' and 'linedefined', pops function 1199 // fills 'name' 'namewhat' and 'linedefined', pops function
1200 lua_getinfo( L, ">nS", &ar); // ... b 1200 lua_getinfo( L, ">nS", &ar); // ... b
1201 name = ar.namewhat; 1201 name = ar.namewhat;
1202 fprintf( stderr, INDENT_BEGIN "FNAME: %s @ %d\n", i, s_indent, ar.short_src, ar.linedefined); // just gives NULL 1202 fprintf( stderr, INDENT_BEGIN "FNAME: %s @ %d\n", i, s_indent, ar.short_src, ar.linedefined); // just gives NULL
1203 } 1203 }
1204 #endif // LOG_FUNC_INFO 1204 #endif // LOG_FUNC_INFO
1205 { 1205 {
1206 size_t sz; 1206 size_t sz;
1207 char const* s = lua_tolstring( L, -1, &sz); // ... b 1207 char const* s = lua_tolstring( L, -1, &sz); // ... b
1208 ASSERT_L( s && sz); 1208 ASSERT_L( s && sz);
1209 STACK_GROW( L2, 2); 1209 STACK_GROW( L2, 2);
1210 // Note: Line numbers seem to be taken precisely from the 1210 // Note: Line numbers seem to be taken precisely from the
1211 // original function. 'name' is not used since the chunk 1211 // original function. 'name' is not used since the chunk
1212 // is precompiled (it seems...). 1212 // is precompiled (it seems...).
1213 // 1213 //
1214 // TBD: Can we get the function's original name through, as well? 1214 // TBD: Can we get the function's original name through, as well?
1215 // 1215 //
1216 if( luaL_loadbuffer( L2, s, sz, name) != 0) // ... {cache} ... p function 1216 if( luaL_loadbuffer( L2, s, sz, name) != 0) // ... {cache} ... p function
1217 { 1217 {
1218 // chunk is precompiled so only LUA_ERRMEM can happen 1218 // chunk is precompiled so only LUA_ERRMEM can happen
1219 // "Otherwise, it pushes an error message" 1219 // "Otherwise, it pushes an error message"
1220 // 1220 //
1221 STACK_GROW( L, 1); 1221 STACK_GROW( L, 1);
1222 luaL_error( L, "%s: %s", upName_, lua_tostring( L2, -1)); 1222 luaL_error( L, "%s: %s", upName_, lua_tostring( L2, -1));
1223 } 1223 }
1224 // remove the dumped string 1224 // remove the dumped string
1225 lua_pop( L, 1); // ... 1225 lua_pop( L, 1); // ...
1226 // now set the cache as soon as we can. 1226 // now set the cache as soon as we can.
1227 // this is necessary if one of the function's upvalues references it indirectly 1227 // this is necessary if one of the function's upvalues references it indirectly
1228 // we need to find it in the cache even if it isn't fully transfered yet 1228 // we need to find it in the cache even if it isn't fully transfered yet
1229 lua_insert( L2, -2); // ... {cache} ... function p 1229 lua_insert( L2, -2); // ... {cache} ... function p
1230 lua_pushvalue( L2, -2); // ... {cache} ... function p function 1230 lua_pushvalue( L2, -2); // ... {cache} ... function p function
1231 // cache[p] = function 1231 // cache[p] = function
1232 lua_rawset( L2, L2_cache_i); // ... {cache} ... function 1232 lua_rawset( L2, L2_cache_i); // ... {cache} ... function
1233 } 1233 }
1234 STACK_MID( L, 0); 1234 STACK_MID( L, 0);
1235 1235
1236 /* push over any upvalues; references to this function will come from 1236 /* push over any upvalues; references to this function will come from
1237 * cache so we don't end up in eternal loop. 1237 * cache so we don't end up in eternal loop.
1238 * Lua5.2 and Lua5.3: one of the upvalues is _ENV, which we don't want to copy! 1238 * Lua5.2 and Lua5.3: one of the upvalues is _ENV, which we don't want to copy!
1239 * instead, the function shall have LUA_RIDX_GLOBALS taken in the destination state! 1239 * instead, the function shall have LUA_RIDX_GLOBALS taken in the destination state!
1240 */ 1240 */
1241 { 1241 {
1242 char const* upname; 1242 char const* upname;
1243#if LUA_VERSION_NUM >= 502 1243#if LUA_VERSION_NUM >= 502
1244 // Starting with Lua 5.2, each Lua function gets its environment as one of its upvalues (named LUA_ENV, aka "_ENV" by default) 1244 // Starting with Lua 5.2, each Lua function gets its environment as one of its upvalues (named LUA_ENV, aka "_ENV" by default)
1245 // Generally this is LUA_RIDX_GLOBALS, which we don't want to copy from the source to the destination state... 1245 // Generally this is LUA_RIDX_GLOBALS, which we don't want to copy from the source to the destination state...
1246 // -> if we encounter an upvalue equal to the global table in the source, bind it to the destination's global table 1246 // -> if we encounter an upvalue equal to the global table in the source, bind it to the destination's global table
1247 lua_pushglobaltable( L); // ... _G 1247 lua_pushglobaltable( L); // ... _G
1248#endif // LUA_VERSION_NUM 1248#endif // LUA_VERSION_NUM
1249 for( n = 0; (upname = lua_getupvalue( L, i, 1 + n)) != NULL; ++ n) 1249 for( n = 0; (upname = lua_getupvalue( L, i, 1 + n)) != NULL; ++ n)
1250 { // ... _G up[n] 1250 { // ... _G up[n]
1251 DEBUGSPEW_CODE( fprintf( stderr, INDENT_BEGIN "UPNAME[%d]: %s -> " INDENT_END, n, upname)); 1251 DEBUGSPEW_CODE( fprintf( stderr, INDENT_BEGIN "UPNAME[%d]: %s -> " INDENT_END, n, upname));
1252#if LUA_VERSION_NUM >= 502 1252#if LUA_VERSION_NUM >= 502
1253 if( lua_rawequal( L, -1, -2)) // is the upvalue equal to the global table? 1253 if( lua_rawequal( L, -1, -2)) // is the upvalue equal to the global table?
1254 { 1254 {
1255 DEBUGSPEW_CODE( fprintf( stderr, "pushing destination global scope\n")); 1255 DEBUGSPEW_CODE( fprintf( stderr, "pushing destination global scope\n"));
1256 lua_pushglobaltable( L2); // ... {cache} ... function <upvalues> 1256 lua_pushglobaltable( L2); // ... {cache} ... function <upvalues>
1257 } 1257 }
1258 else 1258 else
1259#endif // LUA_VERSION_NUM 1259#endif // LUA_VERSION_NUM
1260 { 1260 {
1261 DEBUGSPEW_CODE( fprintf( stderr, "copying value\n")); 1261 DEBUGSPEW_CODE( fprintf( stderr, "copying value\n"));
1262 if( !inter_copy_one( U, L2, L2_cache_i, L, lua_gettop( L), VT_NORMAL, mode_, upname)) // ... {cache} ... function <upvalues> 1262 if( !inter_copy_one( U, L2, L2_cache_i, L, lua_gettop( L), VT_NORMAL, mode_, upname)) // ... {cache} ... function <upvalues>
1263 { 1263 {
1264 luaL_error( L, "Cannot copy upvalue type '%s'", luaL_typename( L, -1)); 1264 luaL_error( L, "Cannot copy upvalue type '%s'", luaL_typename( L, -1));
1265 } 1265 }
1266 } 1266 }
1267 lua_pop( L, 1); // ... _G 1267 lua_pop( L, 1); // ... _G
1268 } 1268 }
1269#if LUA_VERSION_NUM >= 502 1269#if LUA_VERSION_NUM >= 502
1270 lua_pop( L, 1); // ... 1270 lua_pop( L, 1); // ...
1271#endif // LUA_VERSION_NUM 1271#endif // LUA_VERSION_NUM
1272 } 1272 }
1273 // L2: function + 'n' upvalues (>=0) 1273 // L2: function + 'n' upvalues (>=0)
1274 1274
1275 STACK_MID( L, 0); 1275 STACK_MID( L, 0);
1276 1276
1277 // Set upvalues (originally set to 'nil' by 'lua_load') 1277 // Set upvalues (originally set to 'nil' by 'lua_load')
1278 { 1278 {
1279 int func_index = lua_gettop( L2) - n; 1279 int func_index = lua_gettop( L2) - n;
1280 for( ; n > 0; -- n) 1280 for( ; n > 0; -- n)
1281 { 1281 {
1282 char const* rc = lua_setupvalue( L2, func_index, n); // ... {cache} ... function 1282 char const* rc = lua_setupvalue( L2, func_index, n); // ... {cache} ... function
1283 // 1283 //
1284 // "assigns the value at the top of the stack to the upvalue and returns its name. 1284 // "assigns the value at the top of the stack to the upvalue and returns its name.
1285 // It also pops the value from the stack." 1285 // It also pops the value from the stack."
1286 1286
1287 ASSERT_L( rc); // not having enough slots? 1287 ASSERT_L( rc); // not having enough slots?
1288 } 1288 }
1289 // once all upvalues have been set we are left 1289 // once all upvalues have been set we are left
1290 // with the function at the top of the stack // ... {cache} ... function 1290 // with the function at the top of the stack // ... {cache} ... function
1291 } 1291 }
1292 } 1292 }
1293 STACK_END( L, 0); 1293 STACK_END( L, 0);
1294} 1294}
1295 1295
1296/* 1296/*
@@ -1301,168 +1301,168 @@ static void copy_func( Universe* U, lua_State* L2, uint_t L2_cache_i, lua_State*
1301 */ 1301 */
1302static void copy_cached_func( Universe* U, lua_State* L2, uint_t L2_cache_i, lua_State* L, uint_t i, LookupMode mode_, char const* upName_) 1302static void copy_cached_func( Universe* U, lua_State* L2, uint_t L2_cache_i, lua_State* L, uint_t i, LookupMode mode_, char const* upName_)
1303{ 1303{
1304 FuncSubType funcSubType; 1304 FuncSubType funcSubType;
1305 /*lua_CFunction cfunc =*/ luaG_tocfunction( L, i, &funcSubType); // NULL for LuaJIT-fast && bytecode functions 1305 /*lua_CFunction cfunc =*/ luaG_tocfunction( L, i, &funcSubType); // NULL for LuaJIT-fast && bytecode functions
1306 if( funcSubType == FST_Bytecode) 1306 if( funcSubType == FST_Bytecode)
1307 { 1307 {
1308 void* const aspointer = (void*)lua_topointer( L, i); 1308 void* const aspointer = (void*)lua_topointer( L, i);
1309 // TBD: Merge this and same code for tables 1309 // TBD: Merge this and same code for tables
1310 ASSERT_L( L2_cache_i != 0); 1310 ASSERT_L( L2_cache_i != 0);
1311 1311
1312 STACK_GROW( L2, 2); 1312 STACK_GROW( L2, 2);
1313 1313
1314 // L2_cache[id_str]= function 1314 // L2_cache[id_str]= function
1315 // 1315 //
1316 STACK_CHECK( L2, 0); 1316 STACK_CHECK( L2, 0);
1317 1317
1318 // We don't need to use the from state ('L') in ID since the life span 1318 // We don't need to use the from state ('L') in ID since the life span
1319 // is only for the duration of a copy (both states are locked). 1319 // is only for the duration of a copy (both states are locked).
1320 // 1320 //
1321 1321
1322 // push a light userdata uniquely representing the function 1322 // push a light userdata uniquely representing the function
1323 lua_pushlightuserdata( L2, aspointer); // ... {cache} ... p 1323 lua_pushlightuserdata( L2, aspointer); // ... {cache} ... p
1324 1324
1325 //fprintf( stderr, "<< ID: %s >>\n", lua_tostring( L2, -1)); 1325 //fprintf( stderr, "<< ID: %s >>\n", lua_tostring( L2, -1));
1326 1326
1327 lua_pushvalue( L2, -1); // ... {cache} ... p p 1327 lua_pushvalue( L2, -1); // ... {cache} ... p p
1328 lua_rawget( L2, L2_cache_i); // ... {cache} ... p function|nil|true 1328 lua_rawget( L2, L2_cache_i); // ... {cache} ... p function|nil|true
1329 1329
1330 if( lua_isnil( L2, -1)) // function is unknown 1330 if( lua_isnil( L2, -1)) // function is unknown
1331 { 1331 {
1332 lua_pop( L2, 1); // ... {cache} ... p 1332 lua_pop( L2, 1); // ... {cache} ... p
1333 1333
1334 // Set to 'true' for the duration of creation; need to find self-references 1334 // Set to 'true' for the duration of creation; need to find self-references
1335 // via upvalues 1335 // via upvalues
1336 // 1336 //
1337 // pushes a copy of the func, stores a reference in the cache 1337 // pushes a copy of the func, stores a reference in the cache
1338 copy_func( U, L2, L2_cache_i, L, i, mode_, upName_); // ... {cache} ... function 1338 copy_func( U, L2, L2_cache_i, L, i, mode_, upName_); // ... {cache} ... function
1339 } 1339 }
1340 else // found function in the cache 1340 else // found function in the cache
1341 { 1341 {
1342 lua_remove( L2, -2); // ... {cache} ... function 1342 lua_remove( L2, -2); // ... {cache} ... function
1343 } 1343 }
1344 STACK_END( L2, 1); 1344 STACK_END( L2, 1);
1345 ASSERT_L( lua_isfunction( L2, -1)); 1345 ASSERT_L( lua_isfunction( L2, -1));
1346 } 1346 }
1347 else // function is native/LuaJIT: no need to cache 1347 else // function is native/LuaJIT: no need to cache
1348 { 1348 {
1349 lookup_native_func( L2, L, i, mode_, upName_); // ... {cache} ... function 1349 lookup_native_func( L2, L, i, mode_, upName_); // ... {cache} ... function
1350 // if the function was in fact a lookup sentinel, we can either get a function or a table here 1350 // if the function was in fact a lookup sentinel, we can either get a function or a table here
1351 ASSERT_L( lua_isfunction( L2, -1) || lua_istable( L2, -1)); 1351 ASSERT_L( lua_isfunction( L2, -1) || lua_istable( L2, -1));
1352 } 1352 }
1353} 1353}
1354 1354
1355static bool_t push_cached_metatable( Universe* U, lua_State* L2, uint_t L2_cache_i, lua_State* L, uint_t i, enum eLookupMode mode_, char const* upName_) 1355static bool_t push_cached_metatable( Universe* U, lua_State* L2, uint_t L2_cache_i, lua_State* L, uint_t i, enum eLookupMode mode_, char const* upName_)
1356{ 1356{
1357 STACK_CHECK( L, 0); 1357 STACK_CHECK( L, 0);
1358 if( lua_getmetatable( L, i)) // ... mt 1358 if( lua_getmetatable( L, i)) // ... mt
1359 { 1359 {
1360 lua_Integer const mt_id = get_mt_id( U, L, -1); // Unique id for the metatable 1360 lua_Integer const mt_id = get_mt_id( U, L, -1); // Unique id for the metatable
1361 1361
1362 STACK_CHECK( L2, 0); 1362 STACK_CHECK( L2, 0);
1363 STACK_GROW( L2, 4); 1363 STACK_GROW( L2, 4);
1364 // do we already know this metatable? 1364 // do we already know this metatable?
1365 push_registry_subtable( L2, REG_MTID); // _R[REG_MTID] 1365 push_registry_subtable( L2, REG_MTID); // _R[REG_MTID]
1366 lua_pushinteger( L2, mt_id); // _R[REG_MTID] id 1366 lua_pushinteger( L2, mt_id); // _R[REG_MTID] id
1367 lua_rawget( L2, -2); // _R[REG_MTID] mt? 1367 lua_rawget( L2, -2); // _R[REG_MTID] mt?
1368 1368
1369 STACK_MID( L2, 2); 1369 STACK_MID( L2, 2);
1370 1370
1371 if( lua_isnil( L2, -1)) 1371 if( lua_isnil( L2, -1))
1372 { // L2 did not know the metatable 1372 { // L2 did not know the metatable
1373 lua_pop( L2, 1); // _R[REG_MTID] 1373 lua_pop( L2, 1); // _R[REG_MTID]
1374 if( inter_copy_one( U, L2, L2_cache_i, L, lua_gettop( L), VT_METATABLE, mode_, upName_)) // _R[REG_MTID] mt 1374 if( inter_copy_one( U, L2, L2_cache_i, L, lua_gettop( L), VT_METATABLE, mode_, upName_)) // _R[REG_MTID] mt
1375 { 1375 {
1376 STACK_MID( L2, 2); 1376 STACK_MID( L2, 2);
1377 // mt_id -> metatable 1377 // mt_id -> metatable
1378 lua_pushinteger( L2, mt_id); // _R[REG_MTID] mt id 1378 lua_pushinteger( L2, mt_id); // _R[REG_MTID] mt id
1379 lua_pushvalue( L2, -2); // _R[REG_MTID] mt id mt 1379 lua_pushvalue( L2, -2); // _R[REG_MTID] mt id mt
1380 lua_rawset( L2, -4); // _R[REG_MTID] mt 1380 lua_rawset( L2, -4); // _R[REG_MTID] mt
1381 1381
1382 // metatable -> mt_id 1382 // metatable -> mt_id
1383 lua_pushvalue( L2, -1); // _R[REG_MTID] mt mt 1383 lua_pushvalue( L2, -1); // _R[REG_MTID] mt mt
1384 lua_pushinteger( L2, mt_id); // _R[REG_MTID] mt mt id 1384 lua_pushinteger( L2, mt_id); // _R[REG_MTID] mt mt id
1385 lua_rawset( L2, -4); // _R[REG_MTID] mt 1385 lua_rawset( L2, -4); // _R[REG_MTID] mt
1386 } 1386 }
1387 else 1387 else
1388 { 1388 {
1389 (void) luaL_error( L, "Error copying a metatable"); 1389 (void) luaL_error( L, "Error copying a metatable");
1390 } 1390 }
1391 STACK_MID( L2, 2); 1391 STACK_MID( L2, 2);
1392 } 1392 }
1393 lua_remove( L2, -2); // mt 1393 lua_remove( L2, -2); // mt
1394 1394
1395 lua_pop( L, 1); // ... 1395 lua_pop( L, 1); // ...
1396 STACK_END( L2, 1); 1396 STACK_END( L2, 1);
1397 STACK_MID( L, 0); 1397 STACK_MID( L, 0);
1398 return TRUE; 1398 return TRUE;
1399 } 1399 }
1400 STACK_END( L, 0); 1400 STACK_END( L, 0);
1401 return FALSE; 1401 return FALSE;
1402} 1402}
1403 1403
1404static void inter_copy_keyvaluepair( Universe* U, lua_State* L2, uint_t L2_cache_i, lua_State* L, enum e_vt vt, LookupMode mode_, char const* upName_) 1404static void inter_copy_keyvaluepair( Universe* U, lua_State* L2, uint_t L2_cache_i, lua_State* L, enum e_vt vt, LookupMode mode_, char const* upName_)
1405{ 1405{
1406 uint_t val_i = lua_gettop( L); 1406 uint_t val_i = lua_gettop( L);
1407 uint_t key_i = val_i - 1; 1407 uint_t key_i = val_i - 1;
1408 1408
1409 // Only basic key types are copied over; others ignored 1409 // Only basic key types are copied over; others ignored
1410 if( inter_copy_one( U, L2, 0 /*key*/, L, key_i, VT_KEY, mode_, upName_)) 1410 if( inter_copy_one( U, L2, 0 /*key*/, L, key_i, VT_KEY, mode_, upName_))
1411 { 1411 {
1412 char* valPath = (char*) upName_; 1412 char* valPath = (char*) upName_;
1413 if( U->verboseErrors) 1413 if( U->verboseErrors)
1414 { 1414 {
1415 // for debug purposes, let's try to build a useful name 1415 // for debug purposes, let's try to build a useful name
1416 if( lua_type( L, key_i) == LUA_TSTRING) 1416 if( lua_type( L, key_i) == LUA_TSTRING)
1417 { 1417 {
1418 char const* key = lua_tostring( L, key_i); 1418 char const* key = lua_tostring( L, key_i);
1419 size_t const keyRawLen = lua_rawlen( L, key_i); 1419 size_t const keyRawLen = lua_rawlen( L, key_i);
1420 size_t const bufLen = strlen( upName_) + keyRawLen + 2; 1420 size_t const bufLen = strlen( upName_) + keyRawLen + 2;
1421 valPath = (char*) alloca( bufLen); 1421 valPath = (char*) alloca( bufLen);
1422 sprintf( valPath, "%s.%*s", upName_, (int) keyRawLen, key); 1422 sprintf( valPath, "%s.%*s", upName_, (int) keyRawLen, key);
1423 key = NULL; 1423 key = NULL;
1424 } 1424 }
1425#if defined LUA_LNUM || LUA_VERSION_NUM >= 503 1425#if defined LUA_LNUM || LUA_VERSION_NUM >= 503
1426 else if( lua_isinteger( L, key_i)) 1426 else if( lua_isinteger( L, key_i))
1427 { 1427 {
1428 lua_Integer key = lua_tointeger( L, key_i); 1428 lua_Integer key = lua_tointeger( L, key_i);
1429 valPath = (char*) alloca( strlen( upName_) + 32 + 3); 1429 valPath = (char*) alloca( strlen( upName_) + 32 + 3);
1430 sprintf( valPath, "%s[" LUA_INTEGER_FMT "]", upName_, key); 1430 sprintf( valPath, "%s[" LUA_INTEGER_FMT "]", upName_, key);
1431 } 1431 }
1432#endif // defined LUA_LNUM || LUA_VERSION_NUM >= 503 1432#endif // defined LUA_LNUM || LUA_VERSION_NUM >= 503
1433 else if( lua_type( L, key_i) == LUA_TNUMBER) 1433 else if( lua_type( L, key_i) == LUA_TNUMBER)
1434 { 1434 {
1435 lua_Number key = lua_tonumber( L, key_i); 1435 lua_Number key = lua_tonumber( L, key_i);
1436 valPath = (char*) alloca( strlen( upName_) + 32 + 3); 1436 valPath = (char*) alloca( strlen( upName_) + 32 + 3);
1437 sprintf( valPath, "%s[" LUA_NUMBER_FMT "]", upName_, key); 1437 sprintf( valPath, "%s[" LUA_NUMBER_FMT "]", upName_, key);
1438 } 1438 }
1439 else if( lua_type( L, key_i) == LUA_TLIGHTUSERDATA) 1439 else if( lua_type( L, key_i) == LUA_TLIGHTUSERDATA)
1440 { 1440 {
1441 void* key = lua_touserdata( L, key_i); 1441 void* key = lua_touserdata( L, key_i);
1442 valPath = (char*) alloca( strlen( upName_) + 16 + 5); 1442 valPath = (char*) alloca( strlen( upName_) + 16 + 5);
1443 sprintf( valPath, "%s[U:%p]", upName_, key); 1443 sprintf( valPath, "%s[U:%p]", upName_, key);
1444 } 1444 }
1445 else if( lua_type( L, key_i) == LUA_TBOOLEAN) 1445 else if( lua_type( L, key_i) == LUA_TBOOLEAN)
1446 { 1446 {
1447 int key = lua_toboolean( L, key_i); 1447 int key = lua_toboolean( L, key_i);
1448 valPath = (char*) alloca( strlen( upName_) + 8); 1448 valPath = (char*) alloca( strlen( upName_) + 8);
1449 sprintf( valPath, "%s[%s]", upName_, key ? "true" : "false"); 1449 sprintf( valPath, "%s[%s]", upName_, key ? "true" : "false");
1450 } 1450 }
1451 } 1451 }
1452 /* 1452 /*
1453 * Contents of metatables are copied with cache checking; 1453 * Contents of metatables are copied with cache checking;
1454 * important to detect loops. 1454 * important to detect loops.
1455 */ 1455 */
1456 if( inter_copy_one( U, L2, L2_cache_i, L, val_i, VT_NORMAL, mode_, valPath)) 1456 if( inter_copy_one( U, L2, L2_cache_i, L, val_i, VT_NORMAL, mode_, valPath))
1457 { 1457 {
1458 ASSERT_L( lua_istable( L2, -3)); 1458 ASSERT_L( lua_istable( L2, -3));
1459 lua_rawset( L2, -3); // add to table (pops key & val) 1459 lua_rawset( L2, -3); // add to table (pops key & val)
1460 } 1460 }
1461 else 1461 else
1462 { 1462 {
1463 luaL_error( L, "Unable to copy %s entry '%s' because of value is of type '%s'", (vt == VT_NORMAL) ? "table" : "metatable", valPath, luaL_typename( L, val_i)); 1463 luaL_error( L, "Unable to copy %s entry '%s' because of value is of type '%s'", (vt == VT_NORMAL) ? "table" : "metatable", valPath, luaL_typename( L, val_i));
1464 } 1464 }
1465 } 1465 }
1466} 1466}
1467 1467
1468/* 1468/*
@@ -1473,330 +1473,330 @@ static DECLARE_CONST_UNIQUE_KEY( CLONABLES_CACHE_KEY, 0xD04EE018B3DEE8F5);
1473 1473
1474static bool_t copyclone( Universe* U, lua_State* L2, uint_t L2_cache_i, lua_State* L, uint_t i, LookupMode mode_, char const* upName_) 1474static bool_t copyclone( Universe* U, lua_State* L2, uint_t L2_cache_i, lua_State* L, uint_t i, LookupMode mode_, char const* upName_)
1475{ 1475{
1476 void* const source = lua_touserdata( L, i); 1476 void* const source = lua_touserdata( L, i);
1477 1477
1478 STACK_CHECK( L, 0); 1478 STACK_CHECK( L, 0);
1479 STACK_CHECK( L2, 0); 1479 STACK_CHECK( L2, 0);
1480 1480
1481 // Check if the source was already cloned during this copy 1481 // Check if the source was already cloned during this copy
1482 lua_pushlightuserdata( L2, source); // ... source 1482 lua_pushlightuserdata( L2, source); // ... source
1483 lua_rawget( L2, L2_cache_i); // ... clone? 1483 lua_rawget( L2, L2_cache_i); // ... clone?
1484 if ( !lua_isnil( L2, -1)) 1484 if ( !lua_isnil( L2, -1))
1485 { 1485 {
1486 STACK_MID( L2, 1); 1486 STACK_MID( L2, 1);
1487 return TRUE; 1487 return TRUE;
1488 } 1488 }
1489 else 1489 else
1490 { 1490 {
1491 lua_pop( L2, 1); // ... 1491 lua_pop( L2, 1); // ...
1492 } 1492 }
1493 STACK_MID( L2, 0); 1493 STACK_MID( L2, 0);
1494 1494
1495 // no metatable? -> not clonable 1495 // no metatable? -> not clonable
1496 if( !lua_getmetatable( L, i)) // ... mt? 1496 if( !lua_getmetatable( L, i)) // ... mt?
1497 { 1497 {
1498 STACK_MID( L, 0); 1498 STACK_MID( L, 0);
1499 return FALSE; 1499 return FALSE;
1500 } 1500 }
1501 1501
1502 // no __lanesclone? -> not clonable 1502 // no __lanesclone? -> not clonable
1503 lua_getfield( L, -1, "__lanesclone"); // ... mt __lanesclone? 1503 lua_getfield( L, -1, "__lanesclone"); // ... mt __lanesclone?
1504 if( lua_isnil( L, -1)) 1504 if( lua_isnil( L, -1))
1505 { 1505 {
1506 lua_pop( L, 2); // ... 1506 lua_pop( L, 2); // ...
1507 STACK_MID( L, 0); 1507 STACK_MID( L, 0);
1508 return FALSE; 1508 return FALSE;
1509 } 1509 }
1510 1510
1511 { 1511 {
1512 int const mt = lua_absindex( L, -2); 1512 int const mt = lua_absindex( L, -2);
1513 size_t userdata_size = 0; 1513 size_t userdata_size = 0;
1514 void* clone = NULL; 1514 void* clone = NULL;
1515 lua_pushvalue( L, -1); // ... mt __lanesclone __lanesclone 1515 lua_pushvalue( L, -1); // ... mt __lanesclone __lanesclone
1516 // call the cloning function with 1 argument, should return the number of bytes to allocate for the clone 1516 // call the cloning function with 1 argument, should return the number of bytes to allocate for the clone
1517 lua_pushlightuserdata( L, source); // ... mt __lanesclone __lanesclone source 1517 lua_pushlightuserdata( L, source); // ... mt __lanesclone __lanesclone source
1518 lua_call( L, 1, 1); // ... mt __lanesclone size 1518 lua_call( L, 1, 1); // ... mt __lanesclone size
1519 STACK_MID( L, 3); 1519 STACK_MID( L, 3);
1520 userdata_size = (size_t) lua_tointeger( L, -1); // ... mt __lanesclone size 1520 userdata_size = (size_t) lua_tointeger( L, -1); // ... mt __lanesclone size
1521 lua_pop( L, 1); // ... mt __lanesclone 1521 lua_pop( L, 1); // ... mt __lanesclone
1522 // we need to copy over the uservalues of the userdata as well 1522 // we need to copy over the uservalues of the userdata as well
1523 { 1523 {
1524 // extract all the uservalues, but don't transfer them yet 1524 // extract all the uservalues, but don't transfer them yet
1525 int uvi = 0; 1525 int uvi = 0;
1526 while( lua_getiuservalue( L, i, uvi + 1) != LUA_TNONE) // ... mt __lanesclone [uv]+ nil 1526 while( lua_getiuservalue( L, i, uvi + 1) != LUA_TNONE) // ... mt __lanesclone [uv]+ nil
1527 { 1527 {
1528 ++ uvi; 1528 ++ uvi;
1529 } 1529 }
1530 // when lua_getiuservalue() returned LUA_TNONE, it pushed a nil. pop it now 1530 // when lua_getiuservalue() returned LUA_TNONE, it pushed a nil. pop it now
1531 lua_pop( L, 1); // ... mt __lanesclone [uv]+ 1531 lua_pop( L, 1); // ... mt __lanesclone [uv]+
1532 // create the clone userdata with the required number of uservalue slots 1532 // create the clone userdata with the required number of uservalue slots
1533 clone = lua_newuserdatauv( L2, userdata_size, uvi); // ... u 1533 clone = lua_newuserdatauv( L2, userdata_size, uvi); // ... u
1534 // copy the metatable in the target state, and give it to the clone we put there 1534 // copy the metatable in the target state, and give it to the clone we put there
1535 if( inter_copy_one( U, L2, L2_cache_i, L, mt, VT_NORMAL, mode_, upName_)) // ... u mt|sentinel 1535 if( inter_copy_one( U, L2, L2_cache_i, L, mt, VT_NORMAL, mode_, upName_)) // ... u mt|sentinel
1536 { 1536 {
1537 if( eLM_ToKeeper == mode_) // ... u sentinel 1537 if( eLM_ToKeeper == mode_) // ... u sentinel
1538 { 1538 {
1539 ASSERT_L( lua_tocfunction( L2, -1) == table_lookup_sentinel); 1539 ASSERT_L( lua_tocfunction( L2, -1) == table_lookup_sentinel);
1540 // we want to create a new closure with a 'clone sentinel' function, where the upvalues are the userdata and the metatable fqn 1540 // we want to create a new closure with a 'clone sentinel' function, where the upvalues are the userdata and the metatable fqn
1541 lua_getupvalue( L2, -1, 1); // ... u sentinel fqn 1541 lua_getupvalue( L2, -1, 1); // ... u sentinel fqn
1542 lua_remove( L2, -2); // ... u fqn 1542 lua_remove( L2, -2); // ... u fqn
1543 lua_insert( L2, -2); // ... fqn u 1543 lua_insert( L2, -2); // ... fqn u
1544 lua_pushcclosure( L2, userdata_clone_sentinel, 2); // ... userdata_clone_sentinel 1544 lua_pushcclosure( L2, userdata_clone_sentinel, 2); // ... userdata_clone_sentinel
1545 } 1545 }
1546 else // from keeper or direct // ... u mt 1546 else // from keeper or direct // ... u mt
1547 { 1547 {
1548 ASSERT_L( lua_istable( L2, -1)); 1548 ASSERT_L( lua_istable( L2, -1));
1549 lua_setmetatable( L2, -2); // ... u 1549 lua_setmetatable( L2, -2); // ... u
1550 } 1550 }
1551 STACK_MID( L2, 1); 1551 STACK_MID( L2, 1);
1552 } 1552 }
1553 else 1553 else
1554 { 1554 {
1555 (void) luaL_error( L, "Error copying a metatable"); 1555 (void) luaL_error( L, "Error copying a metatable");
1556 } 1556 }
1557 // first, add the entry in the cache (at this point it is either the actual userdata or the keeper sentinel 1557 // first, add the entry in the cache (at this point it is either the actual userdata or the keeper sentinel
1558 lua_pushlightuserdata( L2, source); // ... u source 1558 lua_pushlightuserdata( L2, source); // ... u source
1559 lua_pushvalue( L2, -2); // ... u source u 1559 lua_pushvalue( L2, -2); // ... u source u
1560 lua_rawset( L2, L2_cache_i); // ... u 1560 lua_rawset( L2, L2_cache_i); // ... u
1561 // make sure we have the userdata now 1561 // make sure we have the userdata now
1562 if( eLM_ToKeeper == mode_) // ... userdata_clone_sentinel 1562 if( eLM_ToKeeper == mode_) // ... userdata_clone_sentinel
1563 { 1563 {
1564 lua_getupvalue( L2, -1, 2); // ... userdata_clone_sentinel u 1564 lua_getupvalue( L2, -1, 2); // ... userdata_clone_sentinel u
1565 } 1565 }
1566 // assign uservalues 1566 // assign uservalues
1567 while( uvi > 0) 1567 while( uvi > 0)
1568 { 1568 {
1569 inter_copy_one( U, L2, L2_cache_i, L, lua_absindex( L, -1), VT_NORMAL, mode_, upName_); // ... u uv 1569 inter_copy_one( U, L2, L2_cache_i, L, lua_absindex( L, -1), VT_NORMAL, mode_, upName_); // ... u uv
1570 lua_pop( L, 1); // ... mt __lanesclone [uv]* 1570 lua_pop( L, 1); // ... mt __lanesclone [uv]*
1571 // this pops the value from the stack 1571 // this pops the value from the stack
1572 lua_setiuservalue( L2, -2, uvi); // ... u 1572 lua_setiuservalue( L2, -2, uvi); // ... u
1573 -- uvi; 1573 -- uvi;
1574 } 1574 }
1575 // when we are done, all uservalues are popped from the source stack, and we want only the single transferred value in the destination 1575 // when we are done, all uservalues are popped from the source stack, and we want only the single transferred value in the destination
1576 if( eLM_ToKeeper == mode_) // ... userdata_clone_sentinel u 1576 if( eLM_ToKeeper == mode_) // ... userdata_clone_sentinel u
1577 { 1577 {
1578 lua_pop( L2, 1); // ... userdata_clone_sentinel 1578 lua_pop( L2, 1); // ... userdata_clone_sentinel
1579 } 1579 }
1580 STACK_MID( L2, 1); 1580 STACK_MID( L2, 1);
1581 STACK_MID( L, 2); 1581 STACK_MID( L, 2);
1582 // call cloning function in source state to perform the actual memory cloning 1582 // call cloning function in source state to perform the actual memory cloning
1583 lua_pushlightuserdata( L, clone); // ... mt __lanesclone clone 1583 lua_pushlightuserdata( L, clone); // ... mt __lanesclone clone
1584 lua_pushlightuserdata( L, source); // ... mt __lanesclone clone source 1584 lua_pushlightuserdata( L, source); // ... mt __lanesclone clone source
1585 lua_call( L, 2, 0); // ... mt 1585 lua_call( L, 2, 0); // ... mt
1586 STACK_MID( L, 1); 1586 STACK_MID( L, 1);
1587 } 1587 }
1588 } 1588 }
1589 1589
1590 STACK_END( L2, 1); 1590 STACK_END( L2, 1);
1591 lua_pop( L, 1); // ... 1591 lua_pop( L, 1); // ...
1592 STACK_END( L, 0); 1592 STACK_END( L, 0);
1593 return TRUE; 1593 return TRUE;
1594} 1594}
1595 1595
1596static bool_t inter_copy_userdata( Universe* U, lua_State* L2, uint_t L2_cache_i, lua_State* L, uint_t i, enum e_vt vt, LookupMode mode_, char const* upName_) 1596static bool_t inter_copy_userdata( Universe* U, lua_State* L2, uint_t L2_cache_i, lua_State* L, uint_t i, enum e_vt vt, LookupMode mode_, char const* upName_)
1597{ 1597{
1598 STACK_CHECK( L, 0); 1598 STACK_CHECK( L, 0);
1599 STACK_CHECK( L2, 0); 1599 STACK_CHECK( L2, 0);
1600 if( vt == VT_KEY) 1600 if( vt == VT_KEY)
1601 { 1601 {
1602 return FALSE; 1602 return FALSE;
1603 } 1603 }
1604 1604
1605 // try clonable userdata first 1605 // try clonable userdata first
1606 if( copyclone( U, L2, L2_cache_i, L, i, mode_, upName_)) 1606 if( copyclone( U, L2, L2_cache_i, L, i, mode_, upName_))
1607 { 1607 {
1608 STACK_MID( L, 0); 1608 STACK_MID( L, 0);
1609 STACK_MID( L2, 1); 1609 STACK_MID( L2, 1);
1610 return TRUE; 1610 return TRUE;
1611 } 1611 }
1612 1612
1613 STACK_MID( L, 0); 1613 STACK_MID( L, 0);
1614 STACK_MID( L2, 0); 1614 STACK_MID( L2, 0);
1615 1615
1616 // Allow only deep userdata entities to be copied across 1616 // Allow only deep userdata entities to be copied across
1617 DEBUGSPEW_CODE( fprintf( stderr, "USERDATA\n")); 1617 DEBUGSPEW_CODE( fprintf( stderr, "USERDATA\n"));
1618 if( copydeep( U, L2, L2_cache_i, L, i, mode_, upName_)) 1618 if( copydeep( U, L2, L2_cache_i, L, i, mode_, upName_))
1619 { 1619 {
1620 STACK_MID( L, 0); 1620 STACK_MID( L, 0);
1621 STACK_MID( L2, 1); 1621 STACK_MID( L2, 1);
1622 return TRUE; 1622 return TRUE;
1623 } 1623 }
1624 1624
1625 STACK_MID( L, 0); 1625 STACK_MID( L, 0);
1626 STACK_MID( L2, 0); 1626 STACK_MID( L2, 0);
1627 1627
1628 // Not a deep or clonable full userdata 1628 // Not a deep or clonable full userdata
1629 if( U->demoteFullUserdata) // attempt demotion to light userdata 1629 if( U->demoteFullUserdata) // attempt demotion to light userdata
1630 { 1630 {
1631 void* lud = lua_touserdata( L, i); 1631 void* lud = lua_touserdata( L, i);
1632 lua_pushlightuserdata( L2, lud); 1632 lua_pushlightuserdata( L2, lud);
1633 } 1633 }
1634 else // raise an error 1634 else // raise an error
1635 { 1635 {
1636 (void) luaL_error( L, "can't copy non-deep full userdata across lanes"); 1636 (void) luaL_error( L, "can't copy non-deep full userdata across lanes");
1637 } 1637 }
1638 1638
1639 STACK_END( L2, 1); 1639 STACK_END( L2, 1);
1640 STACK_END( L, 0); 1640 STACK_END( L, 0);
1641 return TRUE; 1641 return TRUE;
1642} 1642}
1643 1643
1644static bool_t inter_copy_function( Universe* U, lua_State* L2, uint_t L2_cache_i, lua_State* L, uint_t i, enum e_vt vt, LookupMode mode_, char const* upName_) 1644static bool_t inter_copy_function( Universe* U, lua_State* L2, uint_t L2_cache_i, lua_State* L, uint_t i, enum e_vt vt, LookupMode mode_, char const* upName_)
1645{ 1645{
1646 if( vt == VT_KEY) 1646 if( vt == VT_KEY)
1647 { 1647 {
1648 return FALSE; 1648 return FALSE;
1649 } 1649 }
1650 1650
1651 STACK_CHECK( L, 0); 1651 STACK_CHECK( L, 0);
1652 STACK_CHECK( L2, 0); 1652 STACK_CHECK( L2, 0);
1653 DEBUGSPEW_CODE( fprintf( stderr, "FUNCTION %s\n", upName_)); 1653 DEBUGSPEW_CODE( fprintf( stderr, "FUNCTION %s\n", upName_));
1654 1654
1655 if( lua_tocfunction( L, i) == userdata_clone_sentinel) // we are actually copying a clonable full userdata from a keeper 1655 if( lua_tocfunction( L, i) == userdata_clone_sentinel) // we are actually copying a clonable full userdata from a keeper
1656 { 1656 {
1657 // clone the full userdata again 1657 // clone the full userdata again
1658 size_t userdata_size = 0; 1658 size_t userdata_size = 0;
1659 void* source; 1659 void* source;
1660 void* clone; 1660 void* clone;
1661 1661
1662 // let's see if we already restored this userdata 1662 // let's see if we already restored this userdata
1663 lua_getupvalue( L, i, 2); // ... u 1663 lua_getupvalue( L, i, 2); // ... u
1664 source = lua_touserdata( L, -1); 1664 source = lua_touserdata( L, -1);
1665 lua_pushlightuserdata( L2, source); // ... source 1665 lua_pushlightuserdata( L2, source); // ... source
1666 lua_rawget( L2, L2_cache_i); // ... u? 1666 lua_rawget( L2, L2_cache_i); // ... u?
1667 if( !lua_isnil( L2, -1)) 1667 if( !lua_isnil( L2, -1))
1668 { 1668 {
1669 lua_pop( L, 1); // ... 1669 lua_pop( L, 1); // ...
1670 STACK_MID( L, 0); 1670 STACK_MID( L, 0);
1671 STACK_MID( L2, 1); 1671 STACK_MID( L2, 1);
1672 return TRUE; 1672 return TRUE;
1673 } 1673 }
1674 lua_pop( L2, 1); // ... 1674 lua_pop( L2, 1); // ...
1675 1675
1676 // this function has 2 upvalues: the fqn of its metatable, and the userdata itself 1676 // this function has 2 upvalues: the fqn of its metatable, and the userdata itself
1677 lookup_table( L2, L, i, mode_, upName_); // ... mt 1677 lookup_table( L2, L, i, mode_, upName_); // ... mt
1678 // __lanesclone should always exist because we wouldn't be restoring data from a userdata_clone_sentinel closure to begin with 1678 // __lanesclone should always exist because we wouldn't be restoring data from a userdata_clone_sentinel closure to begin with
1679 lua_getfield( L2, -1, "__lanesclone"); // ... mt __lanesclone 1679 lua_getfield( L2, -1, "__lanesclone"); // ... mt __lanesclone
1680 lua_pushvalue( L2, -1); // ... mt __lanesclone __lanesclone 1680 lua_pushvalue( L2, -1); // ... mt __lanesclone __lanesclone
1681 // 'i' slot is the closure, but from now on it is the actual userdata 1681 // 'i' slot is the closure, but from now on it is the actual userdata
1682 i = lua_gettop( L); 1682 i = lua_gettop( L);
1683 source = lua_touserdata( L, -1); 1683 source = lua_touserdata( L, -1);
1684 // call the cloning function with 1 argument, should return the number of bytes to allocate for the clone 1684 // call the cloning function with 1 argument, should return the number of bytes to allocate for the clone
1685 lua_pushlightuserdata( L2, source); // ... mt __lanesclone __lanesclone source 1685 lua_pushlightuserdata( L2, source); // ... mt __lanesclone __lanesclone source
1686 lua_call( L2, 1, 1); // ... mt __lanesclone size 1686 lua_call( L2, 1, 1); // ... mt __lanesclone size
1687 userdata_size = (size_t) lua_tointeger( L2, -1); // ... mt __lanesclone size 1687 userdata_size = (size_t) lua_tointeger( L2, -1); // ... mt __lanesclone size
1688 lua_pop( L2, 1); // ... mt __lanesclone 1688 lua_pop( L2, 1); // ... mt __lanesclone
1689 { 1689 {
1690 // extract uservalues (don't transfer them yet) 1690 // extract uservalues (don't transfer them yet)
1691 int uvi = 0; 1691 int uvi = 0;
1692 while( lua_getiuservalue( L, i, uvi + 1) != LUA_TNONE) // ... u uv 1692 while( lua_getiuservalue( L, i, uvi + 1) != LUA_TNONE) // ... u uv
1693 { 1693 {
1694 ++ uvi; 1694 ++ uvi;
1695 } 1695 }
1696 // when lua_getiuservalue() returned LUA_TNONE, it pushed a nil. pop it now 1696 // when lua_getiuservalue() returned LUA_TNONE, it pushed a nil. pop it now
1697 lua_pop( L, 1); // ... u [uv]* 1697 lua_pop( L, 1); // ... u [uv]*
1698 STACK_MID( L, uvi + 1); 1698 STACK_MID( L, uvi + 1);
1699 // create the clone userdata with the required number of uservalue slots 1699 // create the clone userdata with the required number of uservalue slots
1700 clone = lua_newuserdatauv( L2, userdata_size, uvi); // ... mt __lanesclone u 1700 clone = lua_newuserdatauv( L2, userdata_size, uvi); // ... mt __lanesclone u
1701 // add it in the cache 1701 // add it in the cache
1702 lua_pushlightuserdata( L2, source); // ... mt __lanesclone u source 1702 lua_pushlightuserdata( L2, source); // ... mt __lanesclone u source
1703 lua_pushvalue( L2, -2); // ... mt __lanesclone u source u 1703 lua_pushvalue( L2, -2); // ... mt __lanesclone u source u
1704 lua_rawset( L2, L2_cache_i); // ... mt __lanesclone u 1704 lua_rawset( L2, L2_cache_i); // ... mt __lanesclone u
1705 // set metatable 1705 // set metatable
1706 lua_pushvalue( L2, -3); // ... mt __lanesclone u mt 1706 lua_pushvalue( L2, -3); // ... mt __lanesclone u mt
1707 lua_setmetatable( L2, -2); // ... mt __lanesclone u 1707 lua_setmetatable( L2, -2); // ... mt __lanesclone u
1708 // transfer and assign uservalues 1708 // transfer and assign uservalues
1709 while( uvi > 0) 1709 while( uvi > 0)
1710 { 1710 {
1711 inter_copy_one( U, L2, L2_cache_i, L, lua_absindex( L, -1), vt, mode_, upName_); // ... mt __lanesclone u uv 1711 inter_copy_one( U, L2, L2_cache_i, L, lua_absindex( L, -1), vt, mode_, upName_); // ... mt __lanesclone u uv
1712 lua_pop( L, 1); // ... u [uv]* 1712 lua_pop( L, 1); // ... u [uv]*
1713 // this pops the value from the stack 1713 // this pops the value from the stack
1714 lua_setiuservalue( L2, -2, uvi); // ... mt __lanesclone u 1714 lua_setiuservalue( L2, -2, uvi); // ... mt __lanesclone u
1715 -- uvi; 1715 -- uvi;
1716 } 1716 }
1717 // when we are done, all uservalues are popped from the stack 1717 // when we are done, all uservalues are popped from the stack
1718 lua_pop( L, 1); // ... 1718 lua_pop( L, 1); // ...
1719 STACK_MID( L, 0); 1719 STACK_MID( L, 0);
1720 STACK_MID( L2, 3); // ... mt __lanesclone u 1720 STACK_MID( L2, 3); // ... mt __lanesclone u
1721 } 1721 }
1722 // perform the custom cloning part 1722 // perform the custom cloning part
1723 lua_replace( L2, -3); // ... u __lanesclone 1723 lua_replace( L2, -3); // ... u __lanesclone
1724 lua_pushlightuserdata( L2, clone); // ... u __lanesclone clone 1724 lua_pushlightuserdata( L2, clone); // ... u __lanesclone clone
1725 lua_pushlightuserdata( L2, source); // ... u __lanesclone clone source 1725 lua_pushlightuserdata( L2, source); // ... u __lanesclone clone source
1726 lua_call( L2, 2, 0); // ... u 1726 lua_call( L2, 2, 0); // ... u
1727 } 1727 }
1728 else 1728 else
1729 { 1729 {
1730 DEBUGSPEW_CODE( fprintf( stderr, "FUNCTION %s\n", upName_)); 1730 DEBUGSPEW_CODE( fprintf( stderr, "FUNCTION %s\n", upName_));
1731 DEBUGSPEW_CODE( ++ U->debugspew_indent_depth); 1731 DEBUGSPEW_CODE( ++ U->debugspew_indent_depth);
1732 STACK_CHECK( L2, 0); 1732 STACK_CHECK( L2, 0);
1733 copy_cached_func( U, L2, L2_cache_i, L, i, mode_, upName_); 1733 copy_cached_func( U, L2, L2_cache_i, L, i, mode_, upName_);
1734 STACK_END( L2, 1); 1734 STACK_END( L2, 1);
1735 DEBUGSPEW_CODE( -- U->debugspew_indent_depth); 1735 DEBUGSPEW_CODE( -- U->debugspew_indent_depth);
1736 } 1736 }
1737 STACK_END( L2, 1); 1737 STACK_END( L2, 1);
1738 STACK_END( L, 0); 1738 STACK_END( L, 0);
1739 return TRUE; 1739 return TRUE;
1740} 1740}
1741 1741
1742static bool_t inter_copy_table( Universe* U, lua_State* L2, uint_t L2_cache_i, lua_State* L, uint_t i, enum e_vt vt, LookupMode mode_, char const* upName_) 1742static bool_t inter_copy_table( Universe* U, lua_State* L2, uint_t L2_cache_i, lua_State* L, uint_t i, enum e_vt vt, LookupMode mode_, char const* upName_)
1743{ 1743{
1744 if( vt == VT_KEY) 1744 if( vt == VT_KEY)
1745 { 1745 {
1746 return FALSE; 1746 return FALSE;
1747 } 1747 }
1748 1748
1749 STACK_CHECK( L, 0); 1749 STACK_CHECK( L, 0);
1750 STACK_CHECK( L2, 0); 1750 STACK_CHECK( L2, 0);
1751 DEBUGSPEW_CODE( fprintf( stderr, "TABLE %s\n", upName_)); 1751 DEBUGSPEW_CODE( fprintf( stderr, "TABLE %s\n", upName_));
1752 1752
1753 /* 1753 /*
1754 * First, let's try to see if this table is special (aka is it some table that we registered in our lookup databases during module registration?) 1754 * First, let's try to see if this table is special (aka is it some table that we registered in our lookup databases during module registration?)
1755 * Note that this table CAN be a module table, but we just didn't register it, in which case we'll send it through the table cloning mechanism 1755 * Note that this table CAN be a module table, but we just didn't register it, in which case we'll send it through the table cloning mechanism
1756 */ 1756 */
1757 if( lookup_table( L2, L, i, mode_, upName_)) 1757 if( lookup_table( L2, L, i, mode_, upName_))
1758 { 1758 {
1759 ASSERT_L( lua_istable( L2, -1) || (lua_tocfunction( L2, -1) == table_lookup_sentinel)); // from lookup datables // can also be table_lookup_sentinel if this is a table we know 1759 ASSERT_L( lua_istable( L2, -1) || (lua_tocfunction( L2, -1) == table_lookup_sentinel)); // from lookup datables // can also be table_lookup_sentinel if this is a table we know
1760 return TRUE; 1760 return TRUE;
1761 } 1761 }
1762 1762
1763 /* Check if we've already copied the same table from 'L' (during this transmission), and 1763 /* Check if we've already copied the same table from 'L' (during this transmission), and
1764 * reuse the old copy. This allows table upvalues shared by multiple 1764 * reuse the old copy. This allows table upvalues shared by multiple
1765 * local functions to point to the same table, also in the target. 1765 * local functions to point to the same table, also in the target.
1766 * Also, this takes care of cyclic tables and multiple references 1766 * Also, this takes care of cyclic tables and multiple references
1767 * to the same subtable. 1767 * to the same subtable.
1768 * 1768 *
1769 * Note: Even metatables need to go through this test; to detect 1769 * Note: Even metatables need to go through this test; to detect
1770 * loops such as those in required module tables (getmetatable(lanes).lanes == lanes) 1770 * loops such as those in required module tables (getmetatable(lanes).lanes == lanes)
1771 */ 1771 */
1772 if( push_cached_table( L2, L2_cache_i, L, i)) 1772 if( push_cached_table( L2, L2_cache_i, L, i))
1773 { 1773 {
1774 ASSERT_L( lua_istable( L2, -1)); // from cache 1774 ASSERT_L( lua_istable( L2, -1)); // from cache
1775 return TRUE; 1775 return TRUE;
1776 } 1776 }
1777 ASSERT_L( lua_istable( L2, -1)); 1777 ASSERT_L( lua_istable( L2, -1));
1778 1778
1779 STACK_GROW( L, 2); 1779 STACK_GROW( L, 2);
1780 STACK_GROW( L2, 2); 1780 STACK_GROW( L2, 2);
1781 1781
1782 lua_pushnil( L); // start iteration 1782 lua_pushnil( L); // start iteration
1783 while( lua_next( L, i)) 1783 while( lua_next( L, i))
1784 { 1784 {
1785 // need a function to prevent overflowing the stack with verboseErrors-induced alloca() 1785 // need a function to prevent overflowing the stack with verboseErrors-induced alloca()
1786 inter_copy_keyvaluepair( U, L2, L2_cache_i, L, vt, mode_, upName_); 1786 inter_copy_keyvaluepair( U, L2, L2_cache_i, L, vt, mode_, upName_);
1787 lua_pop( L, 1); // pop value (next round) 1787 lua_pop( L, 1); // pop value (next round)
1788 } 1788 }
1789 STACK_MID( L, 0); 1789 STACK_MID( L, 0);
1790 STACK_MID( L2, 1); 1790 STACK_MID( L2, 1);
1791 1791
1792 // Metatables are expected to be immutable, and copied only once. 1792 // Metatables are expected to be immutable, and copied only once.
1793 if( push_cached_metatable( U, L2, L2_cache_i, L, i, mode_, upName_)) // ... t mt? 1793 if( push_cached_metatable( U, L2, L2_cache_i, L, i, mode_, upName_)) // ... t mt?
1794 { 1794 {
1795 lua_setmetatable( L2, -2); // ... t 1795 lua_setmetatable( L2, -2); // ... t
1796 } 1796 }
1797 STACK_END( L2, 1); 1797 STACK_END( L2, 1);
1798 STACK_END( L, 0); 1798 STACK_END( L, 0);
1799 return TRUE; 1799 return TRUE;
1800} 1800}
1801 1801
1802/* 1802/*
@@ -1811,118 +1811,118 @@ static bool_t inter_copy_table( Universe* U, lua_State* L2, uint_t L2_cache_i, l
1811*/ 1811*/
1812bool_t inter_copy_one( Universe* U, lua_State* L2, uint_t L2_cache_i, lua_State* L, uint_t i, enum e_vt vt, LookupMode mode_, char const* upName_) 1812bool_t inter_copy_one( Universe* U, lua_State* L2, uint_t L2_cache_i, lua_State* L, uint_t i, enum e_vt vt, LookupMode mode_, char const* upName_)
1813{ 1813{
1814 bool_t ret = TRUE; 1814 bool_t ret = TRUE;
1815 int val_type = lua_type( L, i); 1815 int val_type = lua_type( L, i);
1816 static int const pod_mask = (1 << LUA_TNIL) | (1 << LUA_TBOOLEAN) | (1 << LUA_TLIGHTUSERDATA) | (1 << LUA_TNUMBER) | (1 << LUA_TSTRING); 1816 static int const pod_mask = (1 << LUA_TNIL) | (1 << LUA_TBOOLEAN) | (1 << LUA_TLIGHTUSERDATA) | (1 << LUA_TNUMBER) | (1 << LUA_TSTRING);
1817 STACK_GROW( L2, 1); 1817 STACK_GROW( L2, 1);
1818 STACK_CHECK( L, 0); // L // L2 1818 STACK_CHECK( L, 0); // L // L2
1819 STACK_CHECK( L2, 0); // L // L2 1819 STACK_CHECK( L2, 0); // L // L2
1820 1820
1821 DEBUGSPEW_CODE( fprintf( stderr, INDENT_BEGIN "inter_copy_one()\n" INDENT_END)); 1821 DEBUGSPEW_CODE( fprintf( stderr, INDENT_BEGIN "inter_copy_one()\n" INDENT_END));
1822 DEBUGSPEW_CODE( ++ U->debugspew_indent_depth); 1822 DEBUGSPEW_CODE( ++ U->debugspew_indent_depth);
1823 DEBUGSPEW_CODE( fprintf( stderr, INDENT_BEGIN "%s %s: " INDENT_END, lua_type_names[val_type], vt_names[vt])); 1823 DEBUGSPEW_CODE( fprintf( stderr, INDENT_BEGIN "%s %s: " INDENT_END, lua_type_names[val_type], vt_names[vt]));
1824 1824
1825 // Non-POD can be skipped if its metatable contains { __lanesignore = true } 1825 // Non-POD can be skipped if its metatable contains { __lanesignore = true }
1826 if( ((1 << val_type) & pod_mask) == 0) 1826 if( ((1 << val_type) & pod_mask) == 0)
1827 { 1827 {
1828 if( lua_getmetatable( L, i)) // ... mt 1828 if( lua_getmetatable( L, i)) // ... mt
1829 { 1829 {
1830 lua_getfield( L, -1, "__lanesignore"); // ... mt ignore? 1830 lua_getfield( L, -1, "__lanesignore"); // ... mt ignore?
1831 if( lua_isboolean( L, -1) && lua_toboolean( L, -1)) 1831 if( lua_isboolean( L, -1) && lua_toboolean( L, -1))
1832 { 1832 {
1833 DEBUGSPEW_CODE( fprintf( stderr, INDENT_BEGIN "__lanesignore -> LUA_TNIL\n" INDENT_END)); 1833 DEBUGSPEW_CODE( fprintf( stderr, INDENT_BEGIN "__lanesignore -> LUA_TNIL\n" INDENT_END));
1834 val_type = LUA_TNIL; 1834 val_type = LUA_TNIL;
1835 } 1835 }
1836 lua_pop( L, 2); // ... 1836 lua_pop( L, 2); // ...
1837 } 1837 }
1838 } 1838 }
1839 STACK_MID( L, 0); 1839 STACK_MID( L, 0);
1840 1840
1841 /* Lets push nil to L2 if the object should be ignored */ 1841 /* Lets push nil to L2 if the object should be ignored */
1842 switch( val_type) 1842 switch( val_type)
1843 { 1843 {
1844 /* Basic types allowed both as values, and as table keys */ 1844 /* Basic types allowed both as values, and as table keys */
1845 1845
1846 case LUA_TBOOLEAN: 1846 case LUA_TBOOLEAN:
1847 { 1847 {
1848 bool_t v = lua_toboolean( L, i); 1848 bool_t v = lua_toboolean( L, i);
1849 DEBUGSPEW_CODE( fprintf( stderr, "%s\n", v ? "true" : "false")); 1849 DEBUGSPEW_CODE( fprintf( stderr, "%s\n", v ? "true" : "false"));
1850 lua_pushboolean( L2, v); 1850 lua_pushboolean( L2, v);
1851 } 1851 }
1852 break; 1852 break;
1853 1853
1854 case LUA_TNUMBER: 1854 case LUA_TNUMBER:
1855 /* LNUM patch support (keeping integer accuracy) */ 1855 /* LNUM patch support (keeping integer accuracy) */
1856#if defined LUA_LNUM || LUA_VERSION_NUM >= 503 1856#if defined LUA_LNUM || LUA_VERSION_NUM >= 503
1857 if( lua_isinteger( L, i)) 1857 if( lua_isinteger( L, i))
1858 { 1858 {
1859 lua_Integer v = lua_tointeger( L, i); 1859 lua_Integer v = lua_tointeger( L, i);
1860 DEBUGSPEW_CODE( fprintf( stderr, LUA_INTEGER_FMT "\n", v)); 1860 DEBUGSPEW_CODE( fprintf( stderr, LUA_INTEGER_FMT "\n", v));
1861 lua_pushinteger( L2, v); 1861 lua_pushinteger( L2, v);
1862 break; 1862 break;
1863 } 1863 }
1864 else 1864 else
1865#endif // defined LUA_LNUM || LUA_VERSION_NUM >= 503 1865#endif // defined LUA_LNUM || LUA_VERSION_NUM >= 503
1866 { 1866 {
1867 lua_Number v = lua_tonumber( L, i); 1867 lua_Number v = lua_tonumber( L, i);
1868 DEBUGSPEW_CODE( fprintf( stderr, LUA_NUMBER_FMT "\n", v)); 1868 DEBUGSPEW_CODE( fprintf( stderr, LUA_NUMBER_FMT "\n", v));
1869 lua_pushnumber( L2, v); 1869 lua_pushnumber( L2, v);
1870 } 1870 }
1871 break; 1871 break;
1872 1872
1873 case LUA_TSTRING: 1873 case LUA_TSTRING:
1874 { 1874 {
1875 size_t len; 1875 size_t len;
1876 char const* s = lua_tolstring( L, i, &len); 1876 char const* s = lua_tolstring( L, i, &len);
1877 DEBUGSPEW_CODE( fprintf( stderr, "'%s'\n", s)); 1877 DEBUGSPEW_CODE( fprintf( stderr, "'%s'\n", s));
1878 lua_pushlstring( L2, s, len); 1878 lua_pushlstring( L2, s, len);
1879 } 1879 }
1880 break; 1880 break;
1881 1881
1882 case LUA_TLIGHTUSERDATA: 1882 case LUA_TLIGHTUSERDATA:
1883 { 1883 {
1884 void* p = lua_touserdata( L, i); 1884 void* p = lua_touserdata( L, i);
1885 DEBUGSPEW_CODE( fprintf( stderr, "%p\n", p)); 1885 DEBUGSPEW_CODE( fprintf( stderr, "%p\n", p));
1886 lua_pushlightuserdata( L2, p); 1886 lua_pushlightuserdata( L2, p);
1887 } 1887 }
1888 break; 1888 break;
1889 1889
1890 /* The following types are not allowed as table keys */ 1890 /* The following types are not allowed as table keys */
1891 1891
1892 case LUA_TUSERDATA: 1892 case LUA_TUSERDATA:
1893 ret = inter_copy_userdata( U, L2, L2_cache_i, L, i, vt, mode_, upName_); 1893 ret = inter_copy_userdata( U, L2, L2_cache_i, L, i, vt, mode_, upName_);
1894 break; 1894 break;
1895 1895
1896 case LUA_TNIL: 1896 case LUA_TNIL:
1897 if( vt == VT_KEY) 1897 if( vt == VT_KEY)
1898 { 1898 {
1899 ret = FALSE; 1899 ret = FALSE;
1900 break; 1900 break;
1901 } 1901 }
1902 lua_pushnil( L2); 1902 lua_pushnil( L2);
1903 break; 1903 break;
1904 1904
1905 case LUA_TFUNCTION: 1905 case LUA_TFUNCTION:
1906 ret = inter_copy_function( U, L2, L2_cache_i, L, i, vt, mode_, upName_); 1906 ret = inter_copy_function( U, L2, L2_cache_i, L, i, vt, mode_, upName_);
1907 break; 1907 break;
1908 1908
1909 case LUA_TTABLE: 1909 case LUA_TTABLE:
1910 ret = inter_copy_table( U, L2, L2_cache_i, L, i, vt, mode_, upName_); 1910 ret = inter_copy_table( U, L2, L2_cache_i, L, i, vt, mode_, upName_);
1911 break; 1911 break;
1912 1912
1913 /* The following types cannot be copied */ 1913 /* The following types cannot be copied */
1914 1914
1915 case 10: // LuaJIT CDATA 1915 case 10: // LuaJIT CDATA
1916 case LUA_TTHREAD: 1916 case LUA_TTHREAD:
1917 ret = FALSE; 1917 ret = FALSE;
1918 break; 1918 break;
1919 } 1919 }
1920 1920
1921 DEBUGSPEW_CODE( -- U->debugspew_indent_depth); 1921 DEBUGSPEW_CODE( -- U->debugspew_indent_depth);
1922 1922
1923 STACK_END( L2, ret ? 1 : 0); 1923 STACK_END( L2, ret ? 1 : 0);
1924 STACK_END( L, 0); 1924 STACK_END( L, 0);
1925 return ret; 1925 return ret;
1926} 1926}
1927 1927
1928/* 1928/*
@@ -1934,122 +1934,122 @@ bool_t inter_copy_one( Universe* U, lua_State* L2, uint_t L2_cache_i, lua_State*
1934*/ 1934*/
1935int luaG_inter_copy( Universe* U, lua_State* L, lua_State* L2, uint_t n, LookupMode mode_) 1935int luaG_inter_copy( Universe* U, lua_State* L, lua_State* L2, uint_t n, LookupMode mode_)
1936{ 1936{
1937 uint_t top_L = lua_gettop( L); // ... {}n 1937 uint_t top_L = lua_gettop( L); // ... {}n
1938 uint_t top_L2 = lua_gettop( L2); // ... 1938 uint_t top_L2 = lua_gettop( L2); // ...
1939 uint_t i, j; 1939 uint_t i, j;
1940 char tmpBuf[16]; 1940 char tmpBuf[16];
1941 char const* pBuf = U->verboseErrors ? tmpBuf : "?"; 1941 char const* pBuf = U->verboseErrors ? tmpBuf : "?";
1942 bool_t copyok = TRUE; 1942 bool_t copyok = TRUE;
1943 1943
1944 DEBUGSPEW_CODE( fprintf( stderr, INDENT_BEGIN "luaG_inter_copy()\n" INDENT_END)); 1944 DEBUGSPEW_CODE( fprintf( stderr, INDENT_BEGIN "luaG_inter_copy()\n" INDENT_END));
1945 DEBUGSPEW_CODE( ++ U->debugspew_indent_depth); 1945 DEBUGSPEW_CODE( ++ U->debugspew_indent_depth);
1946 1946
1947 if( n > top_L) 1947 if( n > top_L)
1948 { 1948 {
1949 // requesting to copy more than is available? 1949 // requesting to copy more than is available?
1950 DEBUGSPEW_CODE( fprintf( stderr, INDENT_BEGIN "nothing to copy()\n" INDENT_END)); 1950 DEBUGSPEW_CODE( fprintf( stderr, INDENT_BEGIN "nothing to copy()\n" INDENT_END));
1951 DEBUGSPEW_CODE( -- U->debugspew_indent_depth); 1951 DEBUGSPEW_CODE( -- U->debugspew_indent_depth);
1952 return -1; 1952 return -1;
1953 } 1953 }
1954 1954
1955 STACK_CHECK( L2, 0); 1955 STACK_CHECK( L2, 0);
1956 STACK_GROW( L2, n + 1); 1956 STACK_GROW( L2, n + 1);
1957 1957
1958 /* 1958 /*
1959 * Make a cache table for the duration of this copy. Collects tables and 1959 * Make a cache table for the duration of this copy. Collects tables and
1960 * function entries, avoiding the same entries to be passed on as multiple 1960 * function entries, avoiding the same entries to be passed on as multiple
1961 * copies. ESSENTIAL i.e. for handling upvalue tables in the right manner! 1961 * copies. ESSENTIAL i.e. for handling upvalue tables in the right manner!
1962 */ 1962 */
1963 lua_newtable( L2); // ... cache 1963 lua_newtable( L2); // ... cache
1964 1964
1965 STACK_CHECK( L, 0); 1965 STACK_CHECK( L, 0);
1966 for( i = top_L - n + 1, j = 1; i <= top_L; ++ i, ++ j) 1966 for( i = top_L - n + 1, j = 1; i <= top_L; ++ i, ++ j)
1967 { 1967 {
1968 if( U->verboseErrors) 1968 if( U->verboseErrors)
1969 { 1969 {
1970 sprintf( tmpBuf, "arg_%d", j); 1970 sprintf( tmpBuf, "arg_%d", j);
1971 } 1971 }
1972 copyok = inter_copy_one( U, L2, top_L2 + 1, L, i, VT_NORMAL, mode_, pBuf); // ... cache {}n 1972 copyok = inter_copy_one( U, L2, top_L2 + 1, L, i, VT_NORMAL, mode_, pBuf); // ... cache {}n
1973 if( !copyok) 1973 if( !copyok)
1974 { 1974 {
1975 break; 1975 break;
1976 } 1976 }
1977 } 1977 }
1978 STACK_END( L, 0); 1978 STACK_END( L, 0);
1979 1979
1980 DEBUGSPEW_CODE( -- U->debugspew_indent_depth); 1980 DEBUGSPEW_CODE( -- U->debugspew_indent_depth);
1981 1981
1982 if( copyok) 1982 if( copyok)
1983 { 1983 {
1984 STACK_MID( L2, n + 1); 1984 STACK_MID( L2, n + 1);
1985 // Remove the cache table. Persistent caching would cause i.e. multiple 1985 // Remove the cache table. Persistent caching would cause i.e. multiple
1986 // messages passed in the same table to use the same table also in receiving end. 1986 // messages passed in the same table to use the same table also in receiving end.
1987 lua_remove( L2, top_L2 + 1); 1987 lua_remove( L2, top_L2 + 1);
1988 return 0; 1988 return 0;
1989 } 1989 }
1990 1990
1991 // error -> pop everything from the target state stack 1991 // error -> pop everything from the target state stack
1992 lua_settop( L2, top_L2); 1992 lua_settop( L2, top_L2);
1993 STACK_END( L2, 0); 1993 STACK_END( L2, 0);
1994 return -2; 1994 return -2;
1995} 1995}
1996 1996
1997 1997
1998int luaG_inter_move( Universe* U, lua_State* L, lua_State* L2, uint_t n, LookupMode mode_) 1998int luaG_inter_move( Universe* U, lua_State* L, lua_State* L2, uint_t n, LookupMode mode_)
1999{ 1999{
2000 int ret = luaG_inter_copy( U, L, L2, n, mode_); 2000 int ret = luaG_inter_copy( U, L, L2, n, mode_);
2001 lua_pop( L, (int) n); 2001 lua_pop( L, (int) n);
2002 return ret; 2002 return ret;
2003} 2003}
2004 2004
2005int luaG_inter_copy_package( Universe* U, lua_State* L, lua_State* L2, int package_idx_, LookupMode mode_) 2005int luaG_inter_copy_package( Universe* U, lua_State* L, lua_State* L2, int package_idx_, LookupMode mode_)
2006{ 2006{
2007 DEBUGSPEW_CODE( fprintf( stderr, INDENT_BEGIN "luaG_inter_copy_package()\n" INDENT_END)); 2007 DEBUGSPEW_CODE( fprintf( stderr, INDENT_BEGIN "luaG_inter_copy_package()\n" INDENT_END));
2008 DEBUGSPEW_CODE( ++ U->debugspew_indent_depth); 2008 DEBUGSPEW_CODE( ++ U->debugspew_indent_depth);
2009 // package 2009 // package
2010 STACK_CHECK( L, 0); 2010 STACK_CHECK( L, 0);
2011 STACK_CHECK( L2, 0); 2011 STACK_CHECK( L2, 0);
2012 package_idx_ = lua_absindex( L, package_idx_); 2012 package_idx_ = lua_absindex( L, package_idx_);
2013 if( lua_type( L, package_idx_) != LUA_TTABLE) 2013 if( lua_type( L, package_idx_) != LUA_TTABLE)
2014 { 2014 {
2015 lua_pushfstring( L, "expected package as table, got %s", luaL_typename( L, package_idx_)); 2015 lua_pushfstring( L, "expected package as table, got %s", luaL_typename( L, package_idx_));
2016 STACK_MID( L, 1); 2016 STACK_MID( L, 1);
2017 // raise the error when copying from lane to lane, else just leave it on the stack to be raised later 2017 // raise the error when copying from lane to lane, else just leave it on the stack to be raised later
2018 return ( mode_ == eLM_LaneBody) ? lua_error( L) : 1; 2018 return ( mode_ == eLM_LaneBody) ? lua_error( L) : 1;
2019 } 2019 }
2020 lua_getglobal( L2, "package"); 2020 lua_getglobal( L2, "package");
2021 if( !lua_isnil( L2, -1)) // package library not loaded: do nothing 2021 if( !lua_isnil( L2, -1)) // package library not loaded: do nothing
2022 { 2022 {
2023 int i; 2023 int i;
2024 // package.loaders is renamed package.searchers in Lua 5.2 2024 // package.loaders is renamed package.searchers in Lua 5.2
2025 // but don't copy it anyway, as the function names change depending on the slot index! 2025 // but don't copy it anyway, as the function names change depending on the slot index!
2026 // users should provide an on_state_create function to setup custom loaders instead 2026 // users should provide an on_state_create function to setup custom loaders instead
2027 // don't copy package.preload in keeper states (they don't know how to translate functions) 2027 // don't copy package.preload in keeper states (they don't know how to translate functions)
2028 char const* entries[] = { "path", "cpath", (mode_ == eLM_LaneBody) ? "preload" : NULL/*, (LUA_VERSION_NUM == 501) ? "loaders" : "searchers"*/, NULL}; 2028 char const* entries[] = { "path", "cpath", (mode_ == eLM_LaneBody) ? "preload" : NULL/*, (LUA_VERSION_NUM == 501) ? "loaders" : "searchers"*/, NULL};
2029 for( i = 0; entries[i]; ++ i) 2029 for( i = 0; entries[i]; ++ i)
2030 { 2030 {
2031 DEBUGSPEW_CODE( fprintf( stderr, INDENT_BEGIN "package.%s\n" INDENT_END, entries[i])); 2031 DEBUGSPEW_CODE( fprintf( stderr, INDENT_BEGIN "package.%s\n" INDENT_END, entries[i]));
2032 lua_getfield( L, package_idx_, entries[i]); 2032 lua_getfield( L, package_idx_, entries[i]);
2033 if( lua_isnil( L, -1)) 2033 if( lua_isnil( L, -1))
2034 { 2034 {
2035 lua_pop( L, 1); 2035 lua_pop( L, 1);
2036 } 2036 }
2037 else 2037 else
2038 { 2038 {
2039 DEBUGSPEW_CODE( ++ U->debugspew_indent_depth); 2039 DEBUGSPEW_CODE( ++ U->debugspew_indent_depth);
2040 luaG_inter_move( U, L, L2, 1, mode_); // moves the entry to L2 2040 luaG_inter_move( U, L, L2, 1, mode_); // moves the entry to L2
2041 DEBUGSPEW_CODE( -- U->debugspew_indent_depth); 2041 DEBUGSPEW_CODE( -- U->debugspew_indent_depth);
2042 lua_setfield( L2, -2, entries[i]); // set package[entries[i]] 2042 lua_setfield( L2, -2, entries[i]); // set package[entries[i]]
2043 } 2043 }
2044 } 2044 }
2045 } 2045 }
2046 else 2046 else
2047 { 2047 {
2048 DEBUGSPEW_CODE( fprintf( stderr, INDENT_BEGIN "'package' not loaded, nothing to do\n" INDENT_END)); 2048 DEBUGSPEW_CODE( fprintf( stderr, INDENT_BEGIN "'package' not loaded, nothing to do\n" INDENT_END));
2049 } 2049 }
2050 lua_pop( L2, 1); 2050 lua_pop( L2, 1);
2051 STACK_END( L2, 0); 2051 STACK_END( L2, 0);
2052 STACK_END( L, 0); 2052 STACK_END( L, 0);
2053 DEBUGSPEW_CODE( -- U->debugspew_indent_depth); 2053 DEBUGSPEW_CODE( -- U->debugspew_indent_depth);
2054 return 0; 2054 return 0;
2055} 2055}
diff --git a/src/tools.h b/src/tools.h
index 3bf5a02..a0893e4 100644
--- a/src/tools.h
+++ b/src/tools.h
@@ -27,9 +27,9 @@ void push_registry_subtable( lua_State* L, UniqueKey key_);
27 27
28enum e_vt 28enum e_vt
29{ 29{
30 VT_NORMAL, 30 VT_NORMAL,
31 VT_KEY, 31 VT_KEY,
32 VT_METATABLE 32 VT_METATABLE
33}; 33};
34bool_t inter_copy_one( Universe* U, lua_State* L2, uint_t L2_cache_i, lua_State* L, uint_t i, enum e_vt vt, LookupMode mode_, char const* upName_); 34bool_t inter_copy_one( Universe* U, lua_State* L2, uint_t L2_cache_i, lua_State* L, uint_t i, enum e_vt vt, LookupMode mode_, char const* upName_);
35 35
diff --git a/src/uniquekey.h b/src/uniquekey.h
index 0cef3a1..ff3d45d 100644
--- a/src/uniquekey.h
+++ b/src/uniquekey.h
@@ -6,7 +6,7 @@
6// Lua light userdata can hold a pointer. 6// Lua light userdata can hold a pointer.
7struct s_UniqueKey 7struct s_UniqueKey
8{ 8{
9 void* value; 9 void* value;
10}; 10};
11typedef struct s_UniqueKey UniqueKey; 11typedef struct s_UniqueKey UniqueKey;
12 12
diff --git a/src/universe.c b/src/universe.c
index e1cd38f..9f84baf 100644
--- a/src/universe.c
+++ b/src/universe.c
@@ -43,33 +43,33 @@ static DECLARE_CONST_UNIQUE_KEY( UNIVERSE_REGKEY, 0x9f877b2cf078f17f);
43 43
44Universe* universe_create( lua_State* L) 44Universe* universe_create( lua_State* L)
45{ 45{
46 Universe* U = (Universe*) lua_newuserdatauv( L, sizeof(Universe), 0); // universe 46 Universe* U = (Universe*) lua_newuserdatauv( L, sizeof(Universe), 0); // universe
47 memset( U, 0, sizeof( Universe)); 47 memset( U, 0, sizeof( Universe));
48 STACK_CHECK( L, 1); 48 STACK_CHECK( L, 1);
49 REGISTRY_SET( L, UNIVERSE_REGKEY, lua_pushvalue(L, -2)); // universe 49 REGISTRY_SET( L, UNIVERSE_REGKEY, lua_pushvalue(L, -2)); // universe
50 STACK_END( L, 1); 50 STACK_END( L, 1);
51 return U; 51 return U;
52} 52}
53 53
54// ################################################################################################ 54// ################################################################################################
55 55
56void universe_store( lua_State* L, Universe* U) 56void universe_store( lua_State* L, Universe* U)
57{ 57{
58 STACK_CHECK( L, 0); 58 STACK_CHECK( L, 0);
59 REGISTRY_SET( L, UNIVERSE_REGKEY, (NULL != U) ? lua_pushlightuserdata( L, U) : lua_pushnil( L)); 59 REGISTRY_SET( L, UNIVERSE_REGKEY, (NULL != U) ? lua_pushlightuserdata( L, U) : lua_pushnil( L));
60 STACK_END( L, 0); 60 STACK_END( L, 0);
61} 61}
62 62
63// ################################################################################################ 63// ################################################################################################
64 64
65Universe* universe_get( lua_State* L) 65Universe* universe_get( lua_State* L)
66{ 66{
67 Universe* universe; 67 Universe* universe;
68 STACK_GROW( L, 2); 68 STACK_GROW( L, 2);
69 STACK_CHECK( L, 0); 69 STACK_CHECK( L, 0);
70 REGISTRY_GET( L, UNIVERSE_REGKEY); 70 REGISTRY_GET( L, UNIVERSE_REGKEY);
71 universe = lua_touserdata( L, -1); // NULL if nil 71 universe = lua_touserdata( L, -1); // NULL if nil
72 lua_pop( L, 1); 72 lua_pop( L, 1);
73 STACK_END( L, 0); 73 STACK_END( L, 0);
74 return universe; 74 return universe;
75} 75}
diff --git a/src/universe.h b/src/universe.h
index 0ef5a93..248a117 100644
--- a/src/universe.h
+++ b/src/universe.h
@@ -27,16 +27,16 @@ typedef struct s_Lane Lane;
27// everything we need to provide to lua_newstate() 27// everything we need to provide to lua_newstate()
28struct AllocatorDefinition_s 28struct AllocatorDefinition_s
29{ 29{
30 lua_Alloc allocF; 30 lua_Alloc allocF;
31 void* allocUD; 31 void* allocUD;
32}; 32};
33typedef struct AllocatorDefinition_s AllocatorDefinition; 33typedef struct AllocatorDefinition_s AllocatorDefinition;
34 34
35// mutex-protected allocator for use with Lua states that share a non-threadsafe allocator 35// mutex-protected allocator for use with Lua states that share a non-threadsafe allocator
36struct ProtectedAllocator_s 36struct ProtectedAllocator_s
37{ 37{
38 AllocatorDefinition definition; 38 AllocatorDefinition definition;
39 MUTEX_T lock; 39 MUTEX_T lock;
40}; 40};
41typedef struct ProtectedAllocator_s ProtectedAllocator; 41typedef struct ProtectedAllocator_s ProtectedAllocator;
42 42
@@ -47,51 +47,51 @@ typedef struct ProtectedAllocator_s ProtectedAllocator;
47// don't forget to initialize all members in LG_configure() 47// don't forget to initialize all members in LG_configure()
48struct s_Universe 48struct s_Universe
49{ 49{
50 // for verbose errors 50 // for verbose errors
51 bool_t verboseErrors; 51 bool_t verboseErrors;
52 52
53 bool_t demoteFullUserdata; 53 bool_t demoteFullUserdata;
54 54
55 // before a state is created, this function will be called to obtain the allocator 55 // before a state is created, this function will be called to obtain the allocator
56 lua_CFunction provide_allocator; 56 lua_CFunction provide_allocator;
57 57
58 // after a state is created, this function will be called right after the bases libraries are loaded 58 // after a state is created, this function will be called right after the bases libraries are loaded
59 lua_CFunction on_state_create_func; 59 lua_CFunction on_state_create_func;
60 60
61 // Initialized and used only if allocator="protected" is found in the configuration settings 61 // Initialized and used only if allocator="protected" is found in the configuration settings
62 // contains a mutex and the original allocator definition 62 // contains a mutex and the original allocator definition
63 ProtectedAllocator protected_allocator; 63 ProtectedAllocator protected_allocator;
64 64
65 Keepers* keepers; 65 Keepers* keepers;
66 66
67 // Initialized by 'init_once_LOCKED()': the deep userdata Linda object 67 // Initialized by 'init_once_LOCKED()': the deep userdata Linda object
68 // used for timers (each lane will get a proxy to this) 68 // used for timers (each lane will get a proxy to this)
69 volatile DeepPrelude* timer_deep; // = NULL 69 volatile DeepPrelude* timer_deep; // = NULL
70 70
71#if HAVE_LANE_TRACKING 71#if HAVE_LANE_TRACKING
72 MUTEX_T tracking_cs; 72 MUTEX_T tracking_cs;
73 Lane* volatile tracking_first; // will change to TRACKING_END if we want to activate tracking 73 Lane* volatile tracking_first; // will change to TRACKING_END if we want to activate tracking
74#endif // HAVE_LANE_TRACKING 74#endif // HAVE_LANE_TRACKING
75 75
76 MUTEX_T selfdestruct_cs; 76 MUTEX_T selfdestruct_cs;
77 77
78 // require() serialization 78 // require() serialization
79 MUTEX_T require_cs; 79 MUTEX_T require_cs;
80 80
81 // Lock for reference counter inc/dec locks (to be initialized by outside code) TODO: get rid of this and use atomics instead! 81 // Lock for reference counter inc/dec locks (to be initialized by outside code) TODO: get rid of this and use atomics instead!
82 MUTEX_T deep_lock; 82 MUTEX_T deep_lock;
83 MUTEX_T mtid_lock; 83 MUTEX_T mtid_lock;
84 84
85 lua_Integer last_mt_id; 85 lua_Integer last_mt_id;
86 86
87#if USE_DEBUG_SPEW 87#if USE_DEBUG_SPEW
88 int debugspew_indent_depth; 88 int debugspew_indent_depth;
89#endif // USE_DEBUG_SPEW 89#endif // USE_DEBUG_SPEW
90 90
91 Lane* volatile selfdestruct_first; 91 Lane* volatile selfdestruct_first;
92 // After a lane has removed itself from the chain, it still performs some processing. 92 // After a lane has removed itself from the chain, it still performs some processing.
93 // The terminal desinit sequence should wait for all such processing to terminate before force-killing threads 93 // The terminal desinit sequence should wait for all such processing to terminate before force-killing threads
94 int volatile selfdestructing_count; 94 int volatile selfdestructing_count;
95}; 95};
96typedef struct s_Universe Universe; 96typedef struct s_Universe Universe;
97 97