diff options
author | Benoit Germain <bnt.germain@gmail.com> | 2022-02-07 08:56:39 +0100 |
---|---|---|
committer | Benoit Germain <bnt.germain@gmail.com> | 2022-02-07 08:56:39 +0100 |
commit | 621fb024b5f887ef9e81e2f28bf087386f5300e1 (patch) | |
tree | 3ed681e2db07a0516904b2cda4e7144c714d19e0 | |
parent | 00c84df3adc0b295ef20bc35bc8df9632e3b93e7 (diff) | |
download | lanes-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.c | 334 | ||||
-rw-r--r-- | src/cancel.h | 32 | ||||
-rw-r--r-- | src/compat.c | 102 | ||||
-rw-r--r-- | src/deep.c | 654 | ||||
-rw-r--r-- | src/deep.h | 24 | ||||
-rw-r--r-- | src/keeper.c | 1208 | ||||
-rw-r--r-- | src/keeper.h | 10 | ||||
-rw-r--r-- | src/lanes.c | 2886 | ||||
-rw-r--r-- | src/lanes.lua | 1380 | ||||
-rw-r--r-- | src/lanes_private.h | 110 | ||||
-rw-r--r-- | src/linda.c | 1380 | ||||
-rw-r--r-- | src/macros_and_utils.h | 66 | ||||
-rw-r--r-- | src/state.c | 554 | ||||
-rw-r--r-- | src/threading.c | 910 | ||||
-rw-r--r-- | src/threading.h | 67 | ||||
-rw-r--r-- | src/tools.c | 3308 | ||||
-rw-r--r-- | src/tools.h | 6 | ||||
-rw-r--r-- | src/uniquekey.h | 2 | ||||
-rw-r--r-- | src/universe.c | 34 | ||||
-rw-r--r-- | src/universe.h | 64 |
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 | */ |
56 | static inline enum e_cancel_request cancel_test( lua_State* L) | 56 | static 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 | // |
71 | LUAG_FUNC( cancel_test) | 71 | LUAG_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 | ||
81 | static void cancel_hook( lua_State* L, lua_Debug* ar) | 81 | static 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 | ||
115 | static cancel_result thread_cancel_soft( Lane* s, double secs_, bool_t wake_lindas_) | 115 | static 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 | ||
133 | static cancel_result thread_cancel_hard( lua_State* L, Lane* s, double secs_, bool_t force_, double waitkill_timeout_) | 133 | static 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 | ||
178 | cancel_result thread_cancel( lua_State* L, Lane* s, CancelOp op_, double secs_, bool_t force_, double waitkill_timeout_) | 178 | cancel_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 |
209 | static CancelOp which_op( lua_State* L, int idx_) | 209 | static 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]]) |
251 | LUAG_FUNC( thread_cancel) | 251 | LUAG_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 | */ |
18 | enum e_cancel_request | 18 | enum 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 | ||
25 | typedef enum | 25 | typedef 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 | ||
32 | typedef enum | 32 | typedef 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 | ||
51 | static inline int cancel_error( lua_State* L) | 51 | static 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 |
13 | static int luaL_getsubtable (lua_State *L, int idx, const char *fname) | 13 | static 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 | ||
29 | void luaL_requiref (lua_State *L, const char *modname, lua_CFunction openf, int glb) | 29 | void 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 | ||
48 | void* lua_newuserdatauv( lua_State* L, size_t sz, int nuvalue) | 48 | void* 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 | ||
54 | int lua_getiuservalue( lua_State* L, int idx, int n) | 54 | int 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 | ||
79 | int lua_setiuservalue( lua_State* L, int idx, int n) | 79 | int 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 |
@@ -73,17 +73,17 @@ static DECLARE_CONST_UNIQUE_KEY( DEEP_PROXY_CACHE_KEY, 0x05773d6fc26be106); | |||
73 | */ | 73 | */ |
74 | static void set_deep_lookup( lua_State* L) | 74 | static 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 | */ |
93 | static void get_deep_lookup( lua_State* L) | 93 | static 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 | */ |
111 | static inline luaG_IdFunction get_idfunc( lua_State* L, int index, LookupMode mode_) | 111 | static 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 | ||
145 | void free_deep_prelude( lua_State* L, DeepPrelude* prelude_) | 145 | void 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 | */ |
160 | static int deep_userdata_gc( lua_State* L) | 160 | static 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 | */ |
206 | char const* push_deep_proxy( Universe* U, lua_State* L, DeepPrelude* prelude, int nuv_, LookupMode mode_) | 206 | char 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 | */ |
381 | int luaG_newdeepuserdata( lua_State* L, luaG_IdFunction idfunc, int nuv_) | 381 | int 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 | */ |
428 | void* luaG_todeep( lua_State* L, luaG_IdFunction idfunc, int index) | 428 | void* 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 | */ |
453 | bool_t copydeep( Universe* U, lua_State* L2, uint_t L2_cache_i, lua_State* L, uint_t i, LookupMode mode_, char const* upName_) | 453 | bool_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 |
@@ -24,18 +24,18 @@ typedef struct s_Universe Universe; | |||
24 | 24 | ||
25 | enum eLookupMode | 25 | enum 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 | }; |
31 | typedef enum eLookupMode LookupMode; | 31 | typedef enum eLookupMode LookupMode; |
32 | 32 | ||
33 | enum eDeepOp | 33 | enum 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 | }; |
40 | typedef enum eDeepOp DeepOp; | 40 | typedef 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 |
50 | struct s_DeepPrelude | 50 | struct 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 | }; |
58 | typedef struct s_DeepPrelude DeepPrelude; | 58 | typedef 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 | ||
62 | typedef struct | 62 | typedef 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 | ||
69 | static int const CONTENTS_TABLE = 1; | 69 | static 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 |
72 | static keeper_fifo* prepare_fifo_access( lua_State* L, int idx_) | 72 | static 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} |
88 | static void fifo_new( lua_State* L) | 88 | static 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 |
103 | static void fifo_push( lua_State* L, keeper_fifo* fifo_, lua_Integer count_) | 103 | static 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 |
122 | static void fifo_peek( lua_State* L, keeper_fifo* fifo_, lua_Integer count_) | 122 | static 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) |
134 | static void fifo_pop( lua_State* L, keeper_fifo* fifo_, lua_Integer count_) | 134 | static 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_) | |||
169 | static DECLARE_CONST_UNIQUE_KEY( FIFOS_KEY, 0xdce50bbc351cd465); | 169 | static DECLARE_CONST_UNIQUE_KEY( FIFOS_KEY, 0xdce50bbc351cd465); |
170 | static void push_table( lua_State* L, int idx_) | 170 | static 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 | ||
192 | int keeper_push_linda_storage( Universe* U, lua_State* L, void* ptr_, ptrdiff_t magic_) | 192 | int 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 |
242 | int keepercall_clear( lua_State* L) | 242 | int 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 |
258 | int keepercall_send( lua_State* L) | 258 | int 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 |
294 | int keepercall_receive( lua_State* L) | 294 | int 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] |
329 | int keepercall_receive_batched( lua_State* L) | 329 | int 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 |
362 | int keepercall_limit( lua_State* L) | 362 | int 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 |
398 | int keepercall_set( lua_State* L) | 398 | int 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 |
471 | int keepercall_get( lua_State* L) | 471 | int 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 [, ...]] |
497 | int keepercall_count( lua_State* L) | 497 | int 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 |
583 | void close_keepers( Universe* U, lua_State* L) | 583 | void 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 | */ |
633 | void init_keepers( Universe* U, lua_State* L) | 633 | void 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) |
724 | Keeper* which_keeper(Keepers* keepers_, ptrdiff_t magic_) | 724 | Keeper* 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 | ||
731 | Keeper* keeper_acquire( Keepers* keepers_, ptrdiff_t magic_) | 731 | Keeper* 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 | ||
757 | void keeper_release( Keeper* K) | 757 | void 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 | ||
763 | void keeper_toggle_nil_sentinels( lua_State* L, int val_i_, LookupMode const mode_) | 763 | void 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 | */ |
796 | int keeper_call( Universe* U, lua_State* K, keeper_api_t func_, lua_State* L, void* linda, uint_t starting_index) | 796 | int 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 | ||
14 | struct s_Keeper | 14 | struct 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 | }; |
20 | typedef struct s_Keeper Keeper; | 20 | typedef struct s_Keeper Keeper; |
21 | 21 | ||
22 | struct s_Keepers | 22 | struct s_Keepers |
23 | { | 23 | { |
24 | int nb_keepers; | 24 | int nb_keepers; |
25 | Keeper keeper_array[1]; | 25 | Keeper keeper_array[1]; |
26 | }; | 26 | }; |
27 | typedef struct s_Keepers Keepers; | 27 | typedef 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 |
114 | static void securize_debug_threadname( lua_State* L, Lane* s) | 114 | static 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 | */ |
155 | static bool_t push_registry_table( lua_State* L, UniqueKey key, bool_t create) | 155 | static 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) | |||
187 | static void tracking_add( Lane* s) | 187 | static 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 | */ |
203 | static bool_t tracking_remove( Lane* s) | 203 | static 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 | ||
239 | static void lane_cleanup( Lane* s) | 239 | static 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 | // |
273 | LUAG_FUNC( set_finalizer) | 273 | LUAG_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 | ||
303 | static int run_finalizers( lua_State* L, int lua_rc) | 303 | static 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 | */ |
393 | static void selfdestruct_add( Lane* s) | 393 | static 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 | */ |
406 | static bool_t selfdestruct_remove( Lane* s) | 406 | static 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 | */ |
442 | static int selfdestruct_gc( lua_State* L) | 442 | static 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 | // |
607 | LUAG_FUNC( set_singlethreaded) | 607 | LUAG_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 | ||
651 | LUAG_FUNC( set_error_reporting) | 651 | LUAG_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 | } |
669 | done: | 669 | done: |
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 | ||
674 | static int lane_error( lua_State* L) | 674 | static 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 | ||
751 | static void push_stack_trace( lua_State* L, int rc_, int stk_base_) | 751 | static 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 | ||
786 | LUAG_FUNC( set_debug_threadname) | 786 | LUAG_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 | ||
806 | LUAG_FUNC( get_debug_threadname) | 806 | LUAG_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 | ||
814 | LUAG_FUNC( set_thread_priority) | 814 | LUAG_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 | ||
828 | LUAG_FUNC( set_thread_affinity) | 828 | LUAG_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 |
842 | struct errcode_name | 842 | struct errcode_name |
843 | { | 843 | { |
844 | int code; | 844 | int code; |
845 | char const* name; | 845 | char const* name; |
846 | }; | 846 | }; |
847 | 847 | ||
848 | static struct errcode_name s_errcodes[] = | 848 | static 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 | }; |
858 | static char const* get_errcode_name( int _code) | 858 | static 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 |
873 | static void thread_cleanup_handler( void* opaque) | 873 | static 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 | ||
883 | static THREAD_RETURN_T THREAD_CALLCONV lane_main( void* vs) | 883 | static 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 |
985 | LUAG_FUNC( require) | 985 | LUAG_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) |
1007 | LUAG_FUNC( register) | 1007 | LUAG_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 | // |
1040 | LUAG_FUNC( lane_new) | 1040 | LUAG_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 | // |
1298 | LUAG_FUNC( thread_gc) | 1298 | LUAG_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 | // |
1385 | static char const * thread_status_string( Lane* s) | 1385 | static 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 | ||
1399 | int push_thread_status( lua_State* L, Lane* s) | 1399 | int 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 | // |
1417 | LUAG_FUNC( thread_join) | 1417 | LUAG_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 |
1501 | LUAG_FUNC( thread_index) | 1501 | LUAG_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 [] = { | |||
1771 | static void init_once_LOCKED( void) | 1771 | static 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 |
1813 | LUAG_FUNC( configure) | 1813 | LUAG_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 | ||
2025 | void signal_handler( int signal) | 2025 | void 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; | |||
2037 | static volatile int s_ecoc_go_ahead = 0; | 2037 | static volatile int s_ecoc_go_ahead = 0; |
2038 | static void EnableCrashingOnCrashes( void) | 2038 | static 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 | ||
2070 | int LANES_API luaopen_lanes_core( lua_State* L) | 2070 | int 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 | ||
2103 | static int default_luaopen_lanes( lua_State* L) | 2103 | static 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 |
2114 | void LANES_API luaopen_lanes_embedded( lua_State* L, lua_CFunction _luaopen_lanes) | 2114 | void 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 |
47 | lanes.configure = function( settings_) | 47 | lanes.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 |
733 | end -- lanes.configure | 733 | end -- lanes.configure |
734 | 734 | ||
735 | lanesMeta.__index = function( t, k) | 735 | lanesMeta.__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] |
740 | end | 740 | end |
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) |
743 | if core.settings then | 743 | if core.settings then |
744 | return lanes.configure() | 744 | return lanes.configure() |
745 | else | 745 | else |
746 | return lanes | 746 | return lanes |
747 | end | 747 | end |
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 | // |
10 | struct s_Lane | 10 | struct 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 | }; |
72 | typedef struct s_Lane Lane; | 72 | typedef struct s_Lane Lane; |
73 | 73 | ||
@@ -79,14 +79,14 @@ typedef struct s_Lane Lane; | |||
79 | 79 | ||
80 | static inline Lane* get_lane_from_registry( lua_State* L) | 80 | static 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 | ||
92 | int push_thread_status( lua_State* L, Lane* s); | 92 | int 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 | */ |
49 | struct s_Linda | 49 | struct 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 | ||
63 | static inline struct s_Linda* lua_toLinda( lua_State* L, int idx_) | 63 | static 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 | ||
70 | static void check_key_types( lua_State* L, int start_, int end_) | 70 | static 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 | ||
84 | LUAG_FUNC( linda_protected_call) | 84 | LUAG_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 | */ |
121 | LUAG_FUNC( linda_send) | 121 | LUAG_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" |
274 | LUAG_FUNC( linda_receive) | 274 | LUAG_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 | */ |
424 | LUAG_FUNC( linda_set) | 424 | LUAG_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 | */ |
479 | LUAG_FUNC( linda_count) | 479 | LUAG_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 | */ |
504 | LUAG_FUNC( linda_get) | 504 | LUAG_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 | */ |
548 | LUAG_FUNC( linda_limit) | 548 | LUAG_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 | */ |
590 | LUAG_FUNC( linda_cancel) | 590 | LUAG_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 | */ |
634 | LUAG_FUNC( linda_deep) | 634 | LUAG_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 | ||
650 | static int linda_tostring( lua_State* L, int idx_, bool_t opt_) | 650 | static 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 | ||
671 | LUAG_FUNC( linda_tostring) | 671 | LUAG_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 | */ |
684 | LUAG_FUNC( linda_concat) | 684 | LUAG_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 | */ |
710 | LUAG_FUNC( linda_dump) | 710 | LUAG_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 | */ |
721 | LUAG_FUNC( linda_towatch) | 721 | LUAG_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 | */ |
759 | static void* linda_id( lua_State* L, DeepOp op_) | 759 | static 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 | */ |
922 | LUAG_FUNC( linda) | 922 | LUAG_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 | // |
60 | static int luaG_new_require( lua_State* L) | 60 | static 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 | */ |
93 | void serialize_require( DEBUGSPEW_PARAM_COMMA( Universe* U) lua_State* L) | 93 | void 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 | ||
121 | static int require_lanes_core( lua_State* L) | 121 | static 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 | ||
129 | static const luaL_Reg libs[] = | 129 | static 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 | ||
165 | static void open1lib( DEBUGSPEW_PARAM_COMMA( Universe* U) lua_State* L, char const* name_, size_t len_) | 165 | static 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) |
196 | static void copy_one_time_settings( Universe* U, lua_State* L, lua_State* L2) | 196 | static 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 | ||
218 | void initialize_on_state_create( Universe* U, lua_State* L) | 218 | void 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 | ||
249 | lua_State* create_state( Universe* U, lua_State* from_) | 249 | lua_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 | ||
274 | void call_on_state_create( Universe* U, lua_State* L, lua_State* from_, LookupMode mode_) | 274 | void 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 | */ |
321 | lua_State* luaG_newstate( Universe* U, lua_State* from_, char const* libs_) | 321 | lua_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. | |||
104 | static void FAIL( char const* funcname, int rc) | 104 | static 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 | ||
295 | static int const gs_prio_remap[] = | 295 | static 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 |
311 | void THREAD_CREATE( THREAD_T* ref, THREAD_RETURN_T (__stdcall *func)( void*), void* data, int prio /* -3..+3 */) | 311 | void 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 | ||
335 | void THREAD_SET_PRIORITY( int prio) | 335 | void 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 | ||
344 | void THREAD_SET_AFFINITY( unsigned int aff) | 344 | void 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 | ||
352 | bool_t THREAD_WAIT_IMPL( THREAD_T *ref, double secs) | 352 | bool_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 |
647 | static int const gs_prio_remap[] = | 647 | static 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 | ||
767 | void THREAD_CREATE( THREAD_T* ref, THREAD_RETURN_T (*func)( void*), void* data, int prio /* -3..+3 */) | 767 | void 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 | ||
879 | void THREAD_SET_PRIORITY( int prio) | 879 | void 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 | ||
892 | void THREAD_SET_AFFINITY( unsigned int aff) | 892 | void 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 | ||
155 | void SIGNAL_INIT( SIGNAL_T *ref ); | 152 | void 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 |
107 | void luaG_dump( lua_State* L) | 107 | void 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 | ||
158 | static void* protected_lua_Alloc( void *ud, void *ptr, size_t osize, size_t nsize) | 158 | static 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 | ||
168 | static int luaG_provide_protected_allocator( lua_State* L) | 168 | static 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? |
178 | void initialize_allocator_function( Universe* U, lua_State* L) | 178 | void 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 | ||
215 | void cleanup_allocator_function( Universe* U, lua_State* L) | 215 | void 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 | ||
229 | static int dummy_writer( lua_State* L, void const* p, size_t sz, void* ud) | 229 | static 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 | ||
251 | typedef enum | 251 | typedef 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 | ||
258 | FuncSubType luaG_getfuncsubtype( lua_State *L, int _i) | 258 | FuncSubType 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 | ||
284 | static lua_CFunction luaG_tocfunction( lua_State *L, int _i, FuncSubType *_out) | 284 | static 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 |
295 | static char const* luaG_pushFQN( lua_State* L, int t, int last, size_t* length) | 295 | static 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 | */ |
327 | static void update_lookup_entry( DEBUGSPEW_PARAM_COMMA( Universe* U) lua_State* L, int _ctx_base, int _depth) | 327 | static 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 | ||
401 | static void populate_func_lookup_table_recur( DEBUGSPEW_PARAM_COMMA( Universe* U) lua_State* L, int _ctx_base, int _i, int _depth) | 401 | static 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 | */ |
527 | void populate_func_lookup_table( lua_State* L, int _i, char const* name_) | 527 | void 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 | */ |
596 | static lua_Integer get_mt_id( Universe* U, lua_State* L, int i) | 596 | static 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 |
637 | static int func_lookup_sentinel( lua_State* L) | 637 | static 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 |
644 | static int table_lookup_sentinel( lua_State* L) | 644 | static 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 |
650 | static int userdata_clone_sentinel( lua_State* L) | 650 | static 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 | */ |
658 | static char const* find_lookup_name( lua_State* L, uint_t i, LookupMode mode_, char const* upName_, size_t* len_) | 658 | static 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 | */ |
730 | static bool_t lookup_table( lua_State* L2, lua_State* L, uint_t i, LookupMode mode_, char const* upName_) | 730 | static 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 | */ |
806 | static bool_t push_cached_table( lua_State* L2, uint_t L2_cache_i, lua_State* L, uint_t i) | 806 | static 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 | */ |
841 | static int discover_object_name_recur( lua_State* L, int shortest_, int depth_) | 841 | static 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 | */ |
987 | int luaG_nameof( lua_State* L) | 987 | int 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 | */ |
1035 | static void lookup_native_func( lua_State* L2, lua_State* L, uint_t i, LookupMode mode_, char const* upName_) | 1035 | static 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 |
1113 | static char const* lua_type_names[] = | 1113 | static 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 | }; |
1127 | static char const* vt_names[] = | 1127 | static 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 | ||
1150 | static void copy_func( Universe* U, lua_State* L2, uint_t L2_cache_i, lua_State* L, uint_t i, LookupMode mode_, char const* upName_) | 1150 | static 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 | */ |
1302 | static 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_) | 1302 | static 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 | ||
1355 | static 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_) | 1355 | static 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 | ||
1404 | static 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_) | 1404 | static 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 | ||
1474 | static bool_t copyclone( Universe* U, lua_State* L2, uint_t L2_cache_i, lua_State* L, uint_t i, LookupMode mode_, char const* upName_) | 1474 | static 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 | ||
1596 | static 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_) | 1596 | static 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 | ||
1644 | static 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_) | 1644 | static 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 | ||
1742 | static 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_) | 1742 | static 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 | */ |
1812 | bool_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_) | 1812 | bool_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 | */ |
1935 | int luaG_inter_copy( Universe* U, lua_State* L, lua_State* L2, uint_t n, LookupMode mode_) | 1935 | int 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 | ||
1998 | int luaG_inter_move( Universe* U, lua_State* L, lua_State* L2, uint_t n, LookupMode mode_) | 1998 | int 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 | ||
2005 | int luaG_inter_copy_package( Universe* U, lua_State* L, lua_State* L2, int package_idx_, LookupMode mode_) | 2005 | int 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 | ||
28 | enum e_vt | 28 | enum e_vt |
29 | { | 29 | { |
30 | VT_NORMAL, | 30 | VT_NORMAL, |
31 | VT_KEY, | 31 | VT_KEY, |
32 | VT_METATABLE | 32 | VT_METATABLE |
33 | }; | 33 | }; |
34 | bool_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_); | 34 | bool_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. |
7 | struct s_UniqueKey | 7 | struct s_UniqueKey |
8 | { | 8 | { |
9 | void* value; | 9 | void* value; |
10 | }; | 10 | }; |
11 | typedef struct s_UniqueKey UniqueKey; | 11 | typedef 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 | ||
44 | Universe* universe_create( lua_State* L) | 44 | Universe* 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 | ||
56 | void universe_store( lua_State* L, Universe* U) | 56 | void 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 | ||
65 | Universe* universe_get( lua_State* L) | 65 | Universe* 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() |
28 | struct AllocatorDefinition_s | 28 | struct AllocatorDefinition_s |
29 | { | 29 | { |
30 | lua_Alloc allocF; | 30 | lua_Alloc allocF; |
31 | void* allocUD; | 31 | void* allocUD; |
32 | }; | 32 | }; |
33 | typedef struct AllocatorDefinition_s AllocatorDefinition; | 33 | typedef 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 |
36 | struct ProtectedAllocator_s | 36 | struct ProtectedAllocator_s |
37 | { | 37 | { |
38 | AllocatorDefinition definition; | 38 | AllocatorDefinition definition; |
39 | MUTEX_T lock; | 39 | MUTEX_T lock; |
40 | }; | 40 | }; |
41 | typedef struct ProtectedAllocator_s ProtectedAllocator; | 41 | typedef 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() |
48 | struct s_Universe | 48 | struct 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 | }; |
96 | typedef struct s_Universe Universe; | 96 | typedef struct s_Universe Universe; |
97 | 97 | ||