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 /src | |
| 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.
Diffstat (limited to 'src')
| -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 | ||
