diff options
| author | Benoit Germain <bnt.germain@gmail.com> | 2011-11-05 17:31:02 +0100 |
|---|---|---|
| committer | Benoit Germain <bnt.germain@gmail.com> | 2011-11-05 17:31:02 +0100 |
| commit | 053f7cff3c95acb915e6babfd306971f11bb7986 (patch) | |
| tree | ee38c60b1119d34eb96aea1105ef033e851d266e /src | |
| parent | 717eadee9c3644fabb32c7ee59949f2846143690 (diff) | |
| download | lanes-053f7cff3c95acb915e6babfd306971f11bb7986.tar.gz lanes-053f7cff3c95acb915e6babfd306971f11bb7986.tar.bz2 lanes-053f7cff3c95acb915e6babfd306971f11bb7986.zip | |
* process exit change: close everything at GC when main state closes, not when atexit() handlers are processed
* Lua 5.2-style module:
* module() is no longer used to implement lanes.lua
* a global "lanes" variable is no longer created when the module is required
* the Lanes module table is returned instead
* Lanes must be initialized before used:
* the first occurence of 'require "lanes"' produces a minimal interface that only contains a configure() function
* the remainder of the interface is made available once this function is called
* subsequent calls to configure() do nothing
* configure() controls the number of keeper states and the startup of timers
* LuaJIT 2 compatibility
* non-Lua functions are no longer copied by creating a C closure from a C pointer, but through 2-way lookup tables
* this means that if a lane function body pulls non-Lua functions, the lane generator description must contain the list of libraries and modules that exports them
* introduces a change in configuration .globals management: contents are copied *after* std libs are loaded
* new .required configuration entry to list modules that must be require()'ed before lane body is transferred
* lane:cancel() wakes up waiting lindas like what is done at lane shutdown
Diffstat (limited to 'src')
| -rw-r--r-- | src/keeper.c | 112 | ||||
| -rw-r--r-- | src/keeper.h | 1 | ||||
| -rw-r--r-- | src/lanes.c | 311 | ||||
| -rw-r--r-- | src/lanes.lua | 79 | ||||
| -rw-r--r-- | src/threading.c | 1 | ||||
| -rw-r--r-- | src/tools.c | 649 | ||||
| -rw-r--r-- | src/tools.h | 9 |
7 files changed, 877 insertions, 285 deletions
diff --git a/src/keeper.c b/src/keeper.c index 5b355cb..1f69d40 100644 --- a/src/keeper.c +++ b/src/keeper.c | |||
| @@ -90,61 +90,119 @@ char const *init_keepers( int const _nbKeepers) | |||
| 90 | // Initialize Keeper states with bare minimum of libs (those required | 90 | // Initialize Keeper states with bare minimum of libs (those required |
| 91 | // by 'keeper.lua') | 91 | // by 'keeper.lua') |
| 92 | // | 92 | // |
| 93 | lua_State *L= luaL_newstate(); | 93 | lua_State *K = luaL_newstate(); |
| 94 | if (!L) | 94 | if (!K) |
| 95 | return "out of memory"; | 95 | return "out of memory"; |
| 96 | 96 | ||
| 97 | // to see VM name in Decoda debugger | 97 | // to see VM name in Decoda debugger |
| 98 | lua_pushliteral( L, "Keeper #"); | 98 | lua_pushliteral( K, "Keeper #"); |
| 99 | lua_pushinteger( L, i + 1); | 99 | lua_pushinteger( K, i + 1); |
| 100 | lua_concat( L, 2); | 100 | lua_concat( K, 2); |
| 101 | lua_setglobal( L, "decoda_name"); | 101 | lua_setglobal( K, "decoda_name"); |
| 102 | |||
| 103 | luaG_openlibs( L, "io,table,package" ); // 'io' for debugging messages, package because we need to require modules exporting idfuncs | ||
| 104 | serialize_require( L); | ||
| 105 | 102 | ||
| 103 | // 'io' for debugging messages, 'package' because we need to require modules exporting idfuncs | ||
| 104 | // the others because they export functions that we may store in a keeper for transfer between lanes | ||
| 105 | luaG_openlibs( K, "*"); | ||
| 106 | serialize_require( K); | ||
| 106 | 107 | ||
| 107 | // Read in the preloaded chunk (and run it) | 108 | // Read in the preloaded chunk (and run it) |
| 108 | // | 109 | // |
| 109 | if (luaL_loadbuffer( L, keeper_chunk, sizeof(keeper_chunk), "@keeper.lua")) | 110 | if( luaL_loadbuffer( K, keeper_chunk, sizeof(keeper_chunk), "@keeper.lua")) |
| 110 | return "luaL_loadbuffer() failed"; // LUA_ERRMEM | 111 | return "luaL_loadbuffer() failed"; // LUA_ERRMEM |
| 111 | 112 | ||
| 112 | if (lua_pcall( L, 0 /*args*/, 0 /*results*/, 0 /*errfunc*/ )) | 113 | if( lua_pcall( K, 0 /*args*/, 0 /*results*/, 0 /*errfunc*/)) |
| 113 | { | 114 | { |
| 114 | // LUA_ERRRUN / LUA_ERRMEM / LUA_ERRERR | 115 | // LUA_ERRRUN / LUA_ERRMEM / LUA_ERRERR |
| 115 | // | 116 | // |
| 116 | const char *err= lua_tostring(L,-1); | 117 | const char *err = lua_tostring(K, -1); |
| 117 | assert(err); | 118 | assert( err); |
| 118 | return err; | 119 | return err; |
| 119 | } | 120 | } |
| 120 | 121 | ||
| 121 | MUTEX_INIT( &GKeepers[i].lock_ ); | 122 | MUTEX_INIT( &GKeepers[i].lock_); |
| 122 | GKeepers[i].L= L; | 123 | GKeepers[i].L = K; |
| 123 | //GKeepers[i].count = 0; | 124 | //GKeepers[i].count = 0; |
| 124 | } | 125 | } |
| 125 | return NULL; // ok | 126 | return NULL; // ok |
| 126 | } | 127 | } |
| 127 | 128 | ||
| 129 | // cause each keeper state to populate its database of transferable functions with those from the specified module | ||
| 130 | void populate_keepers( lua_State *L) | ||
| 131 | { | ||
| 132 | size_t name_len; | ||
| 133 | char const *name = luaL_checklstring( L, -1, &name_len); | ||
| 134 | size_t package_path_len; | ||
| 135 | char const *package_path; | ||
| 136 | size_t package_cpath_len; | ||
| 137 | char const *package_cpath; | ||
| 138 | int i; | ||
| 139 | |||
| 140 | // we need to make sure that package.path & package.cpath are the same in the keepers | ||
| 141 | // than what is currently in use when the module is required in the caller's Lua state | ||
| 142 | STACK_CHECK(L) | ||
| 143 | STACK_GROW( L, 3); | ||
| 144 | lua_getglobal( L, "package"); | ||
| 145 | lua_getfield( L, -1, "path"); | ||
| 146 | package_path = luaL_checklstring( L, -1, &package_path_len); | ||
| 147 | lua_getfield( L, -2, "cpath"); | ||
| 148 | package_cpath = luaL_checklstring( L, -1, &package_cpath_len); | ||
| 149 | |||
| 150 | for( i = 0; i < GNbKeepers; ++ i) | ||
| 151 | { | ||
| 152 | lua_State *K = GKeepers[i].L; | ||
| 153 | int res; | ||
| 154 | MUTEX_LOCK( &GKeepers[i].lock_); | ||
| 155 | STACK_CHECK(K) | ||
| 156 | STACK_GROW( K, 2); | ||
| 157 | lua_getglobal( K, "package"); | ||
| 158 | lua_pushlstring( K, package_path, package_path_len); | ||
| 159 | lua_setfield( K, -2, "path"); | ||
| 160 | lua_pushlstring( K, package_cpath, package_cpath_len); | ||
| 161 | lua_setfield( K, -2, "cpath"); | ||
| 162 | lua_pop( K, 1); | ||
| 163 | lua_getglobal( K, "require"); | ||
| 164 | lua_pushlstring( K, name, name_len); | ||
| 165 | res = lua_pcall( K, 1, 0, 0); | ||
| 166 | if( res != 0) | ||
| 167 | { | ||
| 168 | char const *err = luaL_checkstring( K, -1); | ||
| 169 | luaL_error( L, "error requiring '%s' in keeper state: %s", name, err); | ||
| 170 | } | ||
| 171 | STACK_END(K, 0) | ||
| 172 | MUTEX_UNLOCK( &GKeepers[i].lock_); | ||
| 173 | } | ||
| 174 | lua_pop( L, 3); | ||
| 175 | STACK_END(L, 0) | ||
| 176 | } | ||
| 177 | |||
| 128 | struct s_Keeper *keeper_acquire( const void *ptr) | 178 | struct s_Keeper *keeper_acquire( const void *ptr) |
| 129 | { | 179 | { |
| 130 | /* | 180 | // can be 0 if this happens during main state shutdown (lanes is being GC'ed -> no keepers) |
| 131 | * Any hashing will do that maps pointers to 0..GNbKeepers-1 | 181 | if( GNbKeepers == 0) |
| 132 | * consistently. | 182 | { |
| 133 | * | 183 | return NULL; |
| 134 | * Pointers are often aligned by 8 or so - ignore the low order bits | 184 | } |
| 135 | */ | 185 | else |
| 136 | unsigned int i= ((unsigned long)(ptr) >> 3) % GNbKeepers; | 186 | { |
| 137 | struct s_Keeper *K= &GKeepers[i]; | 187 | /* |
| 188 | * Any hashing will do that maps pointers to 0..GNbKeepers-1 | ||
| 189 | * consistently. | ||
| 190 | * | ||
| 191 | * Pointers are often aligned by 8 or so - ignore the low order bits | ||
| 192 | */ | ||
| 193 | unsigned int i= ((unsigned long)(ptr) >> 3) % GNbKeepers; | ||
| 194 | struct s_Keeper *K= &GKeepers[i]; | ||
| 138 | 195 | ||
| 139 | MUTEX_LOCK( &K->lock_); | 196 | MUTEX_LOCK( &K->lock_); |
| 140 | //++ K->count; | 197 | //++ K->count; |
| 141 | return K; | 198 | return K; |
| 199 | } | ||
| 142 | } | 200 | } |
| 143 | 201 | ||
| 144 | void keeper_release( struct s_Keeper *K) | 202 | void keeper_release( struct s_Keeper *K) |
| 145 | { | 203 | { |
| 146 | //-- K->count; | 204 | //-- K->count; |
| 147 | MUTEX_UNLOCK( &K->lock_); | 205 | if( K) MUTEX_UNLOCK( &K->lock_); |
| 148 | } | 206 | } |
| 149 | 207 | ||
| 150 | void keeper_toggle_nil_sentinels( lua_State *L, int _val_i, int _nil_to_sentinel) | 208 | void keeper_toggle_nil_sentinels( lua_State *L, int _val_i, int _nil_to_sentinel) |
diff --git a/src/keeper.h b/src/keeper.h index 27a0f68..1bcb36b 100644 --- a/src/keeper.h +++ b/src/keeper.h | |||
| @@ -9,6 +9,7 @@ struct s_Keeper | |||
| 9 | }; | 9 | }; |
| 10 | 10 | ||
| 11 | const char *init_keepers( int const _nbKeepers); | 11 | const char *init_keepers( int const _nbKeepers); |
| 12 | void populate_keepers( lua_State *L); | ||
| 12 | struct s_Keeper *keeper_acquire( const void *ptr); | 13 | struct s_Keeper *keeper_acquire( const void *ptr); |
| 13 | void keeper_release( struct s_Keeper *K); | 14 | void keeper_release( struct s_Keeper *K); |
| 14 | void keeper_toggle_nil_sentinels( lua_State *L, int _val_i, int _nil_to_sentinel); | 15 | void keeper_toggle_nil_sentinels( lua_State *L, int _val_i, int _nil_to_sentinel); |
diff --git a/src/lanes.c b/src/lanes.c index ed54b0f..44db625 100644 --- a/src/lanes.c +++ b/src/lanes.c | |||
| @@ -51,7 +51,7 @@ | |||
| 51 | * ... | 51 | * ... |
| 52 | */ | 52 | */ |
| 53 | 53 | ||
| 54 | const char *VERSION= "2.2.0"; | 54 | const char *VERSION= "3.0-beta"; |
| 55 | 55 | ||
| 56 | /* | 56 | /* |
| 57 | =============================================================================== | 57 | =============================================================================== |
| @@ -787,10 +787,11 @@ static void linda_id( lua_State *L, char const * const which) | |||
| 787 | /* Clean associated structures in the keeper state. | 787 | /* Clean associated structures in the keeper state. |
| 788 | */ | 788 | */ |
| 789 | K= keeper_acquire(s); | 789 | K= keeper_acquire(s); |
| 790 | if( K) // can be NULL if this happens during main state shutdown (lanes is GC'ed -> no keepers -> no need to cleanup) | ||
| 790 | { | 791 | { |
| 791 | keeper_call( K->L, "clear", L, s, 0 ); | 792 | keeper_call( K->L, "clear", L, s, 0 ); |
| 793 | keeper_release(K); | ||
| 792 | } | 794 | } |
| 793 | keeper_release(K); | ||
| 794 | 795 | ||
| 795 | /* There aren't any lanes waiting on these lindas, since all proxies | 796 | /* There aren't any lanes waiting on these lindas, since all proxies |
| 796 | * have been gc'ed. Right? | 797 | * have been gc'ed. Right? |
| @@ -1028,9 +1029,9 @@ volatile DEEP_PRELUDE *timer_deep; // = NULL | |||
| 1028 | /* | 1029 | /* |
| 1029 | * Process end; cancel any still free-running threads | 1030 | * Process end; cancel any still free-running threads |
| 1030 | */ | 1031 | */ |
| 1031 | static void selfdestruct_atexit( void ) | 1032 | static int selfdestruct_atexit( lua_State *L) |
| 1032 | { | 1033 | { |
| 1033 | if (selfdestruct_first == SELFDESTRUCT_END) return; // no free-running threads | 1034 | if (selfdestruct_first == SELFDESTRUCT_END) return 0; // no free-running threads |
| 1034 | 1035 | ||
| 1035 | // Signal _all_ still running threads to exit (including the timer thread) | 1036 | // Signal _all_ still running threads to exit (including the timer thread) |
| 1036 | // | 1037 | // |
| @@ -1047,7 +1048,7 @@ static void selfdestruct_atexit( void ) | |||
| 1047 | // signal the linda the wake up the thread so that it can react to the cancel query | 1048 | // signal the linda the wake up the thread so that it can react to the cancel query |
| 1048 | // let us hope we never land here with a pointer on a linda that has been destroyed... | 1049 | // let us hope we never land here with a pointer on a linda that has been destroyed... |
| 1049 | SIGNAL_T *waiting_on = s->waiting_on; | 1050 | SIGNAL_T *waiting_on = s->waiting_on; |
| 1050 | s->waiting_on = NULL; | 1051 | //s->waiting_on = NULL; // useful, or not? |
| 1051 | SIGNAL_ALL( waiting_on); | 1052 | SIGNAL_ALL( waiting_on); |
| 1052 | } | 1053 | } |
| 1053 | s = s->selfdestruct_next; | 1054 | s = s->selfdestruct_next; |
| @@ -1117,6 +1118,7 @@ static void selfdestruct_atexit( void ) | |||
| 1117 | // | 1118 | // |
| 1118 | if ( selfdestruct_first != SELFDESTRUCT_END ) { | 1119 | if ( selfdestruct_first != SELFDESTRUCT_END ) { |
| 1119 | unsigned n=0; | 1120 | unsigned n=0; |
| 1121 | #if 0 | ||
| 1120 | MUTEX_LOCK( &selfdestruct_cs ); | 1122 | MUTEX_LOCK( &selfdestruct_cs ); |
| 1121 | { | 1123 | { |
| 1122 | struct s_lane *s= selfdestruct_first; | 1124 | struct s_lane *s= selfdestruct_first; |
| @@ -1131,15 +1133,14 @@ static void selfdestruct_atexit( void ) | |||
| 1131 | // and works without the block (so let's leave those lanes running) | 1133 | // and works without the block (so let's leave those lanes running) |
| 1132 | // | 1134 | // |
| 1133 | //we want to free memory and such when we exit. | 1135 | //we want to free memory and such when we exit. |
| 1134 | #if 0 | ||
| 1135 | // 2.0.2: at least timer lane is still here | 1136 | // 2.0.2: at least timer lane is still here |
| 1136 | // | 1137 | // |
| 1137 | DEBUGEXEC(fprintf( stderr, "Left %d lane(s) with cancel request at process end.\n", n )); | 1138 | DEBUGEXEC(fprintf( stderr, "Left %d lane(s) with cancel request at process end.\n", n )); |
| 1139 | n=0; | ||
| 1138 | #else | 1140 | #else |
| 1139 | // first thing we did was to raise the linda signals the threads were waiting on (if any) | 1141 | // first thing we did was to raise the linda signals the threads were waiting on (if any) |
| 1140 | // therefore, any well-behaved thread should be in CANCELLED state | 1142 | // therefore, any well-behaved thread should be in CANCELLED state |
| 1141 | // these are not running, and the state can be closed | 1143 | // these are not running, and the state can be closed |
| 1142 | n=0; | ||
| 1143 | MUTEX_LOCK( &selfdestruct_cs ); | 1144 | MUTEX_LOCK( &selfdestruct_cs ); |
| 1144 | { | 1145 | { |
| 1145 | struct s_lane *s= selfdestruct_first; | 1146 | struct s_lane *s= selfdestruct_first; |
| @@ -1147,7 +1148,8 @@ static void selfdestruct_atexit( void ) | |||
| 1147 | { | 1148 | { |
| 1148 | struct s_lane *next_s= s->selfdestruct_next; | 1149 | struct s_lane *next_s= s->selfdestruct_next; |
| 1149 | s->selfdestruct_next= NULL; // detach from selfdestruct chain | 1150 | s->selfdestruct_next= NULL; // detach from selfdestruct chain |
| 1150 | THREAD_KILL( &s->thread); | 1151 | if( s->thread) // can be NULL if previous 'soft' termination succeeded |
| 1152 | THREAD_KILL( &s->thread); | ||
| 1151 | // NO lua_close() in this case because we don't know where execution of the state was interrupted | 1153 | // NO lua_close() in this case because we don't know where execution of the state was interrupted |
| 1152 | free( s); | 1154 | free( s); |
| 1153 | s = next_s; | 1155 | s = next_s; |
| @@ -1161,6 +1163,7 @@ static void selfdestruct_atexit( void ) | |||
| 1161 | #endif | 1163 | #endif |
| 1162 | } | 1164 | } |
| 1163 | close_keepers(); | 1165 | close_keepers(); |
| 1166 | return 0; | ||
| 1164 | } | 1167 | } |
| 1165 | 1168 | ||
| 1166 | 1169 | ||
| @@ -1508,10 +1511,36 @@ LUAG_FUNC( set_debug_threadname) | |||
| 1508 | // [prio_int=0], | 1511 | // [prio_int=0], |
| 1509 | // [globals_tbl], | 1512 | // [globals_tbl], |
| 1510 | // [packagepath], | 1513 | // [packagepath], |
| 1514 | // [required], | ||
| 1511 | // [... args ...] ) | 1515 | // [... args ...] ) |
| 1512 | // | 1516 | // |
| 1513 | // Upvalues: metatable to use for 'lane_ud' | 1517 | // Upvalues: metatable to use for 'lane_ud' |
| 1514 | // | 1518 | // |
| 1519 | |||
| 1520 | // helper function to require a module in the keeper states and in the target state | ||
| 1521 | // source state contains module name at the top of the stack | ||
| 1522 | static void require_one_module( lua_State *L, lua_State *L2, bool_t _fatal) | ||
| 1523 | { | ||
| 1524 | size_t len; | ||
| 1525 | char const *name = lua_tolstring( L, -1, &len); | ||
| 1526 | // require the module in the target lane | ||
| 1527 | STACK_GROW( L2, 2); | ||
| 1528 | lua_getglobal( L2, "require"); | ||
| 1529 | if( lua_isnil( L2, -1)) | ||
| 1530 | { | ||
| 1531 | lua_pop( L2, 1); | ||
| 1532 | if( _fatal) | ||
| 1533 | luaL_error( L, "cannot pre-require modules without loading package library first"); | ||
| 1534 | } | ||
| 1535 | else | ||
| 1536 | { | ||
| 1537 | lua_pushlstring( L2, name, len); | ||
| 1538 | lua_pcall( L2, 1, 0, 0); | ||
| 1539 | // we need to require this module in the keeper states as well | ||
| 1540 | populate_keepers( L); | ||
| 1541 | } | ||
| 1542 | } | ||
| 1543 | |||
| 1515 | LUAG_FUNC( thread_new ) | 1544 | LUAG_FUNC( thread_new ) |
| 1516 | { | 1545 | { |
| 1517 | lua_State *L2; | 1546 | lua_State *L2; |
| @@ -1524,8 +1553,9 @@ LUAG_FUNC( thread_new ) | |||
| 1524 | uint_t glob= luaG_isany(L,5) ? 5:0; | 1553 | uint_t glob= luaG_isany(L,5) ? 5:0; |
| 1525 | uint_t ppath = luaG_isany(L,6) ? 6:0; | 1554 | uint_t ppath = luaG_isany(L,6) ? 6:0; |
| 1526 | uint_t pcpath = luaG_isany(L,7) ? 7:0; | 1555 | uint_t pcpath = luaG_isany(L,7) ? 7:0; |
| 1556 | uint_t required = luaG_isany(L,8) ? 8:0; | ||
| 1527 | 1557 | ||
| 1528 | #define FIXED_ARGS (7) | 1558 | #define FIXED_ARGS (8) |
| 1529 | uint_t args= lua_gettop(L) - FIXED_ARGS; | 1559 | uint_t args= lua_gettop(L) - FIXED_ARGS; |
| 1530 | 1560 | ||
| 1531 | if (prio < THREAD_PRIO_MIN || prio > THREAD_PRIO_MAX) | 1561 | if (prio < THREAD_PRIO_MIN || prio > THREAD_PRIO_MAX) |
| @@ -1541,40 +1571,20 @@ LUAG_FUNC( thread_new ) | |||
| 1541 | 1571 | ||
| 1542 | if (!L2) luaL_error( L, "'luaL_newstate()' failed; out of memory" ); | 1572 | if (!L2) luaL_error( L, "'luaL_newstate()' failed; out of memory" ); |
| 1543 | 1573 | ||
| 1544 | STACK_GROW( L,2 ); | 1574 | STACK_GROW( L, 2); |
| 1545 | |||
| 1546 | // Setting the globals table (needs to be done before loading stdlibs, | ||
| 1547 | // and the lane function) | ||
| 1548 | // | ||
| 1549 | if (glob!=0) | ||
| 1550 | { | ||
| 1551 | STACK_CHECK(L) | ||
| 1552 | if (!lua_istable(L,glob)) | ||
| 1553 | luaL_error( L, "Expected table, got %s", luaG_typename(L,glob) ); | ||
| 1554 | |||
| 1555 | lua_pushvalue( L, glob ); | ||
| 1556 | |||
| 1557 | luaG_inter_move( L, L2, 1); // moves the table to L2 | ||
| 1558 | |||
| 1559 | // L2 [-1]: table of globals | ||
| 1560 | |||
| 1561 | // "You can change the global environment of a Lua thread using lua_replace" | ||
| 1562 | // (refman-5.0.pdf p. 30) | ||
| 1563 | // | ||
| 1564 | lua_replace( L2, LUA_GLOBALSINDEX ); | ||
| 1565 | STACK_END(L,0) | ||
| 1566 | } | ||
| 1567 | 1575 | ||
| 1568 | // Selected libraries | 1576 | // Selected libraries |
| 1569 | // | 1577 | // |
| 1570 | if (libs) | 1578 | if (libs) |
| 1571 | { | 1579 | { |
| 1572 | const char *err= luaG_openlibs( L2, libs ); | 1580 | const char *err= luaG_openlibs( L2, libs); |
| 1573 | ASSERT_L( !err ); // bad libs should have been noticed by 'lanes.lua' | 1581 | ASSERT_L( !err ); // bad libs should have been noticed by 'lanes.lua' |
| 1574 | 1582 | ||
| 1575 | serialize_require( L2 ); | 1583 | serialize_require( L2); |
| 1576 | } | 1584 | } |
| 1577 | 1585 | ||
| 1586 | ASSERT_L( lua_gettop(L2) == 0); | ||
| 1587 | |||
| 1578 | // package.path | 1588 | // package.path |
| 1579 | STACK_CHECK(L2) | 1589 | STACK_CHECK(L2) |
| 1580 | if( ppath) | 1590 | if( ppath) |
| @@ -1582,15 +1592,17 @@ LUAG_FUNC( thread_new ) | |||
| 1582 | if (lua_type(L,ppath) != LUA_TSTRING) | 1592 | if (lua_type(L,ppath) != LUA_TSTRING) |
| 1583 | luaL_error( L, "expected packagepath as string, got %s", luaG_typename(L,ppath)); | 1593 | luaL_error( L, "expected packagepath as string, got %s", luaG_typename(L,ppath)); |
| 1584 | lua_getglobal( L2, "package"); | 1594 | lua_getglobal( L2, "package"); |
| 1585 | if( lua_isnil( L2, -1)) | 1595 | if( lua_isnil( L2, -1)) // package library not loaded: do nothing |
| 1586 | { | 1596 | { |
| 1587 | lua_pop( L2, 1); | 1597 | lua_pop( L2, 1); |
| 1588 | luaL_error( L, "specifying a new path for packages, but lane doesn't load package library"); | ||
| 1589 | } | 1598 | } |
| 1590 | lua_pushvalue( L, ppath); | 1599 | else |
| 1591 | luaG_inter_move( L, L2, 1); // moves the new path to L2 | 1600 | { |
| 1592 | lua_setfield( L2, -2, "path"); // set package.path | 1601 | lua_pushvalue( L, ppath); |
| 1593 | lua_pop( L2, 1); | 1602 | luaG_inter_move( L, L2, 1); // moves the new path to L2 |
| 1603 | lua_setfield( L2, -2, "path"); // set package.path | ||
| 1604 | lua_pop( L2, 1); | ||
| 1605 | } | ||
| 1594 | } | 1606 | } |
| 1595 | STACK_END(L2,0) | 1607 | STACK_END(L2,0) |
| 1596 | 1608 | ||
| @@ -1601,18 +1613,83 @@ LUAG_FUNC( thread_new ) | |||
| 1601 | if (lua_type(L,pcpath) != LUA_TSTRING) | 1613 | if (lua_type(L,pcpath) != LUA_TSTRING) |
| 1602 | luaL_error( L, "expected packagecpath as string, got %s", luaG_typename(L,pcpath)); | 1614 | luaL_error( L, "expected packagecpath as string, got %s", luaG_typename(L,pcpath)); |
| 1603 | lua_getglobal( L2, "package"); | 1615 | lua_getglobal( L2, "package"); |
| 1604 | if( lua_isnil( L2, -1)) | 1616 | if( lua_isnil( L2, -1)) // // package library not loaded: do nothing |
| 1605 | { | 1617 | { |
| 1606 | lua_pop( L2, 1); | 1618 | lua_pop( L2, 1); |
| 1607 | luaL_error( L, "specifying a new cpath for packages, but lane doesn't load package library"); | ||
| 1608 | } | 1619 | } |
| 1609 | lua_pushvalue( L, pcpath); | 1620 | else |
| 1610 | luaG_inter_move( L, L2, 1); // moves the new cpath to L2 | 1621 | { |
| 1611 | lua_setfield( L2, -2, "cpath"); // set package.cpath | 1622 | lua_pushvalue( L, pcpath); |
| 1612 | lua_pop( L2, 1); | 1623 | luaG_inter_move( L, L2, 1); // moves the new cpath to L2 |
| 1624 | lua_setfield( L2, -2, "cpath"); // set package.cpath | ||
| 1625 | lua_pop( L2, 1); | ||
| 1626 | } | ||
| 1613 | } | 1627 | } |
| 1614 | STACK_END(L2,0) | 1628 | STACK_END(L2,0) |
| 1615 | 1629 | ||
| 1630 | // modules to require in the target lane *before* the function is transfered! | ||
| 1631 | |||
| 1632 | //start by requiring lua51-lanes, since it is a bit special | ||
| 1633 | // it not fatal if 'require' isn't loaded, just ignore (may cause function transfer errors later on if the lane pulls the lanes module itself) | ||
| 1634 | STACK_CHECK(L) | ||
| 1635 | STACK_CHECK(L2) | ||
| 1636 | lua_pushliteral( L, "lua51-lanes"); | ||
| 1637 | require_one_module( L, L2, FALSE); | ||
| 1638 | lua_pop( L, 1); | ||
| 1639 | STACK_END(L2,0) | ||
| 1640 | STACK_END(L,0) | ||
| 1641 | |||
| 1642 | STACK_CHECK(L) | ||
| 1643 | STACK_CHECK(L2) | ||
| 1644 | if( required) | ||
| 1645 | { | ||
| 1646 | int nbRequired = 1; | ||
| 1647 | // should not happen, was checked in lanes.lua before calling thread_new() | ||
| 1648 | if (lua_type(L, required) != LUA_TTABLE) | ||
| 1649 | luaL_error( L, "expected required module list as a table, got %s", luaG_typename( L, required)); | ||
| 1650 | lua_pushnil( L); | ||
| 1651 | while( lua_next( L, required) != 0) | ||
| 1652 | { | ||
| 1653 | if (lua_type(L,-1) != LUA_TSTRING || lua_type(L,-2) != LUA_TNUMBER || lua_tonumber( L, -2) != nbRequired) | ||
| 1654 | { | ||
| 1655 | luaL_error( L, "required module list should be a list of strings."); | ||
| 1656 | } | ||
| 1657 | else | ||
| 1658 | { | ||
| 1659 | require_one_module( L, L2, TRUE); | ||
| 1660 | } | ||
| 1661 | lua_pop( L, 1); | ||
| 1662 | ++ nbRequired; | ||
| 1663 | } | ||
| 1664 | } | ||
| 1665 | STACK_END(L2,0) | ||
| 1666 | STACK_END(L,0) | ||
| 1667 | |||
| 1668 | // Appending the specified globals to the global environment | ||
| 1669 | // *after* stdlibs have been loaded and modules required, in case we transfer references to native functions they exposed... | ||
| 1670 | // | ||
| 1671 | if (glob!=0) | ||
| 1672 | { | ||
| 1673 | STACK_CHECK(L) | ||
| 1674 | STACK_CHECK(L2) | ||
| 1675 | if (!lua_istable(L,glob)) | ||
| 1676 | luaL_error( L, "Expected table, got %s", luaG_typename(L,glob)); | ||
| 1677 | |||
| 1678 | lua_pushnil( L); | ||
| 1679 | while( lua_next( L, glob)) | ||
| 1680 | { | ||
| 1681 | luaG_inter_copy( L, L2, 2); // moves the key/value pair to the L2 stack | ||
| 1682 | // assign it in the globals table | ||
| 1683 | lua_rawset( L2, LUA_GLOBALSINDEX); | ||
| 1684 | lua_pop( L, 1); | ||
| 1685 | } | ||
| 1686 | |||
| 1687 | STACK_END(L2, 0) | ||
| 1688 | STACK_END(L, 0) | ||
| 1689 | } | ||
| 1690 | |||
| 1691 | ASSERT_L( lua_gettop(L2) == 0); | ||
| 1692 | |||
| 1616 | // Lane main function | 1693 | // Lane main function |
| 1617 | // | 1694 | // |
| 1618 | STACK_CHECK(L) | 1695 | STACK_CHECK(L) |
| @@ -1677,7 +1754,7 @@ LUAG_FUNC( thread_new ) | |||
| 1677 | lua_newtable( L); | 1754 | lua_newtable( L); |
| 1678 | lua_setfenv( L, -2); | 1755 | lua_setfenv( L, -2); |
| 1679 | 1756 | ||
| 1680 | // Place 's' to registry, for 'cancel_test()' (even if 'cs'==0 we still | 1757 | // Place 's' in registry, for 'cancel_test()' (even if 'cs'==0 we still |
| 1681 | // do cancel tests at pending send/receive). | 1758 | // do cancel tests at pending send/receive). |
| 1682 | // | 1759 | // |
| 1683 | lua_pushlightuserdata( L2, CANCEL_TEST_KEY ); | 1760 | lua_pushlightuserdata( L2, CANCEL_TEST_KEY ); |
| @@ -1792,6 +1869,17 @@ static bool_t thread_cancel( struct s_lane *s, double secs, bool_t force) | |||
| 1792 | // | 1869 | // |
| 1793 | if( s->status < DONE) | 1870 | if( s->status < DONE) |
| 1794 | { | 1871 | { |
| 1872 | // signal the linda the wake up the thread so that it can react to the cancel query | ||
| 1873 | // let us hope we never land here with a pointer on a linda that has been destroyed... | ||
| 1874 | //MUTEX_LOCK( &selfdestruct_cs ); | ||
| 1875 | { | ||
| 1876 | SIGNAL_T *waiting_on = s->waiting_on; | ||
| 1877 | if( s->status == WAITING && waiting_on != NULL) | ||
| 1878 | { | ||
| 1879 | SIGNAL_ALL( waiting_on); | ||
| 1880 | } | ||
| 1881 | } | ||
| 1882 | //MUTEX_UNLOCK( &selfdestruct_cs ); | ||
| 1795 | s->cancel_request = TRUE; // it's now signalled to stop | 1883 | s->cancel_request = TRUE; // it's now signalled to stop |
| 1796 | done= | 1884 | done= |
| 1797 | #if (defined PLATFORM_WIN32) || (defined PLATFORM_POCKETPC) || (defined PTHREAD_TIMEDJOIN) | 1885 | #if (defined PLATFORM_WIN32) || (defined PLATFORM_POCKETPC) || (defined PTHREAD_TIMEDJOIN) |
| @@ -1816,23 +1904,32 @@ static bool_t thread_cancel( struct s_lane *s, double secs, bool_t force) | |||
| 1816 | 1904 | ||
| 1817 | LUAG_FUNC( thread_cancel) | 1905 | LUAG_FUNC( thread_cancel) |
| 1818 | { | 1906 | { |
| 1819 | struct s_lane *s= lua_toLane(L,1); | 1907 | if( lua_gettop( L) != 1 || lua_type( L, 1) != LUA_TUSERDATA) |
| 1820 | double secs= 0.0; | 1908 | { |
| 1821 | uint_t force_i=2; | 1909 | return luaL_error( L, "invalid argument #1, did you use ':' as you should?"); |
| 1822 | bool_t force, done= TRUE; | 1910 | } |
| 1911 | else | ||
| 1912 | { | ||
| 1913 | struct s_lane *s = lua_toLane( L, 1); | ||
| 1914 | double secs = 0.0; | ||
| 1915 | uint_t force_i = 2; | ||
| 1916 | bool_t force, done= TRUE; | ||
| 1823 | 1917 | ||
| 1824 | if (lua_isnumber(L,2)) { | 1918 | if( lua_isnumber( L, 2)) |
| 1825 | secs= lua_tonumber(L,2); | 1919 | { |
| 1826 | force_i++; | 1920 | secs = lua_tonumber( L, 2); |
| 1827 | } else if (lua_isnil(L,2)) | 1921 | ++ force_i; |
| 1828 | force_i++; | 1922 | } |
| 1923 | else if( lua_isnil( L, 2)) | ||
| 1924 | ++ force_i; | ||
| 1829 | 1925 | ||
| 1830 | force= lua_toboolean(L,force_i); // FALSE if nothing there | 1926 | force = lua_toboolean( L, force_i); // FALSE if nothing there |
| 1831 | 1927 | ||
| 1832 | done = thread_cancel( s, secs, force); | 1928 | done = thread_cancel( s, secs, force); |
| 1833 | 1929 | ||
| 1834 | lua_pushboolean( L, done); | 1930 | lua_pushboolean( L, done); |
| 1835 | return 1; | 1931 | return 1; |
| 1932 | } | ||
| 1836 | } | 1933 | } |
| 1837 | 1934 | ||
| 1838 | //--- | 1935 | //--- |
| @@ -2183,7 +2280,7 @@ static void init_once_LOCKED( lua_State *L, volatile DEEP_PRELUDE ** timer_deep_ | |||
| 2183 | // Selfdestruct chain handling | 2280 | // Selfdestruct chain handling |
| 2184 | // | 2281 | // |
| 2185 | MUTEX_INIT( &selfdestruct_cs ); | 2282 | MUTEX_INIT( &selfdestruct_cs ); |
| 2186 | atexit( selfdestruct_atexit ); | 2283 | //atexit( selfdestruct_atexit ); |
| 2187 | 2284 | ||
| 2188 | //--- | 2285 | //--- |
| 2189 | // Linux needs SCHED_RR to change thread priorities, and that is only | 2286 | // Linux needs SCHED_RR to change thread priorities, and that is only |
| @@ -2233,7 +2330,17 @@ static void init_once_LOCKED( lua_State *L, volatile DEEP_PRELUDE ** timer_deep_ | |||
| 2233 | 2330 | ||
| 2234 | // The host Lua state must always have a reference to this Linda object in order for our 'timer_deep_ref' to be valid. | 2331 | // The host Lua state must always have a reference to this Linda object in order for our 'timer_deep_ref' to be valid. |
| 2235 | // So store a reference that we will never actually use. | 2332 | // So store a reference that we will never actually use. |
| 2236 | lua_pushlightuserdata(L, (void *)init_once_LOCKED); | 2333 | // at the same time, use this object as a 'desinit' marker: |
| 2334 | // when the main lua State is closed, this object will be GC'ed | ||
| 2335 | { | ||
| 2336 | lua_newuserdata( L, 1); | ||
| 2337 | lua_newtable( L); | ||
| 2338 | lua_pushcfunction( L, selfdestruct_atexit); | ||
| 2339 | lua_setfield( L, -2, "__gc"); | ||
| 2340 | lua_pushliteral( L, "AtExit"); | ||
| 2341 | lua_setfield( L, -2, "__metatable"); | ||
| 2342 | lua_setmetatable( L, -2); | ||
| 2343 | } | ||
| 2237 | lua_insert(L, -2); // Swap key with the Linda object | 2344 | lua_insert(L, -2); // Swap key with the Linda object |
| 2238 | lua_rawset(L, LUA_REGISTRYINDEX); | 2345 | lua_rawset(L, LUA_REGISTRYINDEX); |
| 2239 | 2346 | ||
| @@ -2241,14 +2348,12 @@ static void init_once_LOCKED( lua_State *L, volatile DEEP_PRELUDE ** timer_deep_ | |||
| 2241 | STACK_END(L,0) | 2348 | STACK_END(L,0) |
| 2242 | } | 2349 | } |
| 2243 | 2350 | ||
| 2244 | int | 2351 | static volatile long s_initCount = 0; |
| 2245 | #if (defined PLATFORM_WIN32) || (defined PLATFORM_POCKETPC) | 2352 | |
| 2246 | __declspec(dllexport) | 2353 | LUAG_FUNC( configure ) |
| 2247 | #endif | ||
| 2248 | luaopen_lanes( lua_State *L ) | ||
| 2249 | { | 2354 | { |
| 2250 | static volatile int /*bool*/ go_ahead; // = 0 | 2355 | char const *name = luaL_checkstring( L, lua_upvalueindex( 1)); |
| 2251 | int const nbKeepers = luaL_optint( L, 2, 1); | 2356 | int const nbKeepers = luaL_optint( L, 1, 1); |
| 2252 | luaL_argcheck( L, nbKeepers > 0, 2, "Number of keeper states must be > 0"); | 2357 | luaL_argcheck( L, nbKeepers > 0, 2, "Number of keeper states must be > 0"); |
| 2253 | /* | 2358 | /* |
| 2254 | * Making one-time initializations. | 2359 | * Making one-time initializations. |
| @@ -2259,31 +2364,29 @@ luaopen_lanes( lua_State *L ) | |||
| 2259 | */ | 2364 | */ |
| 2260 | #ifdef PLATFORM_WIN32 | 2365 | #ifdef PLATFORM_WIN32 |
| 2261 | { | 2366 | { |
| 2262 | // TBD: Someone please replace this with reliable Win32 API code. Problem is, | 2367 | static volatile int /*bool*/ go_ahead; // = 0 |
| 2263 | // there's no autoinitializing locks (s.a. PTHREAD_MUTEX_INITIALIZER) in | 2368 | if( InterlockedCompareExchange( &s_initCount, 1, 0) == 0) |
| 2264 | // Windows so 'InterlockedIncrement' or something needs to be used. | 2369 | { |
| 2265 | // This is 99.9999% safe, though (and always safe if host is single-threaded) | ||
| 2266 | // -- AKa 24-Jun-2009 | ||
| 2267 | // | ||
| 2268 | static volatile unsigned my_number; // = 0 | ||
| 2269 | |||
| 2270 | if (my_number++ == 0) { // almost atomic | ||
| 2271 | init_once_LOCKED(L, &timer_deep, nbKeepers); | 2370 | init_once_LOCKED(L, &timer_deep, nbKeepers); |
| 2272 | go_ahead= 1; // let others pass | 2371 | go_ahead= 1; // let others pass |
| 2273 | } else { | 2372 | } |
| 2373 | else | ||
| 2374 | { | ||
| 2274 | while( !go_ahead ) { Sleep(1); } // changes threads | 2375 | while( !go_ahead ) { Sleep(1); } // changes threads |
| 2275 | } | 2376 | } |
| 2276 | } | 2377 | } |
| 2277 | #else | 2378 | #else |
| 2278 | if (!go_ahead) { | 2379 | if( s_initCount == 0) |
| 2380 | { | ||
| 2279 | static pthread_mutex_t my_lock= PTHREAD_MUTEX_INITIALIZER; | 2381 | static pthread_mutex_t my_lock= PTHREAD_MUTEX_INITIALIZER; |
| 2280 | pthread_mutex_lock(&my_lock); | 2382 | pthread_mutex_lock(&my_lock); |
| 2281 | { | 2383 | { |
| 2282 | // Recheck now that we're within the lock | 2384 | // Recheck now that we're within the lock |
| 2283 | // | 2385 | // |
| 2284 | if (!go_ahead) { | 2386 | if (s_initCount == 0) |
| 2387 | { | ||
| 2285 | init_once_LOCKED(L, &timer_deep, nbKeepers); | 2388 | init_once_LOCKED(L, &timer_deep, nbKeepers); |
| 2286 | go_ahead= 1; | 2389 | s_initCount = 1; |
| 2287 | } | 2390 | } |
| 2288 | } | 2391 | } |
| 2289 | pthread_mutex_unlock(&my_lock); | 2392 | pthread_mutex_unlock(&my_lock); |
| @@ -2292,7 +2395,10 @@ luaopen_lanes( lua_State *L ) | |||
| 2292 | assert( timer_deep != 0 ); | 2395 | assert( timer_deep != 0 ); |
| 2293 | 2396 | ||
| 2294 | // Create main module interface table | 2397 | // Create main module interface table |
| 2295 | lua_newtable(L); | 2398 | lua_pushvalue( L, lua_upvalueindex( 2)); |
| 2399 | // remove configure() (this function) from the module interface | ||
| 2400 | lua_pushnil( L); | ||
| 2401 | lua_setfield( L, -2, "configure"); | ||
| 2296 | luaL_register(L, NULL, lanes_functions); | 2402 | luaL_register(L, NULL, lanes_functions); |
| 2297 | 2403 | ||
| 2298 | // metatable for threads | 2404 | // metatable for threads |
| @@ -2313,7 +2419,7 @@ luaopen_lanes( lua_State *L ) | |||
| 2313 | lua_setfield( L, -2, "join"); | 2419 | lua_setfield( L, -2, "join"); |
| 2314 | lua_pushcfunction( L, LG_thread_cancel); | 2420 | lua_pushcfunction( L, LG_thread_cancel); |
| 2315 | lua_setfield( L, -2, "cancel"); | 2421 | lua_setfield( L, -2, "cancel"); |
| 2316 | lua_pushboolean( L, 0); | 2422 | lua_pushliteral( L, "Lane"); |
| 2317 | lua_setfield( L, -2, "__metatable"); | 2423 | lua_setfield( L, -2, "__metatable"); |
| 2318 | 2424 | ||
| 2319 | lua_pushcclosure( L, LG_thread_new, 1 ); // metatable as closure param | 2425 | lua_pushcclosure( L, LG_thread_new, 1 ); // metatable as closure param |
| @@ -2331,8 +2437,41 @@ luaopen_lanes( lua_State *L ) | |||
| 2331 | lua_pushlightuserdata( L, CANCEL_ERROR ); | 2437 | lua_pushlightuserdata( L, CANCEL_ERROR ); |
| 2332 | lua_setfield(L, -2, "cancel_error"); | 2438 | lua_setfield(L, -2, "cancel_error"); |
| 2333 | 2439 | ||
| 2334 | // Return the local module table | 2440 | // register all native functions found in that module in the transferable functions database |
| 2335 | return 1; | 2441 | // we process it before _G because we don't want to find the module when scanning _G (this would generate longer names) |
| 2442 | populate_func_lookup_table( L, -1, name); | ||
| 2443 | // record all existing C/JIT-fast functions | ||
| 2444 | populate_func_lookup_table( L, LUA_GLOBALSINDEX, NULL); | ||
| 2445 | // Return nothing | ||
| 2446 | lua_pop( L, 1); | ||
| 2447 | return 0; | ||
| 2448 | } | ||
| 2449 | |||
| 2450 | int | ||
| 2451 | #if (defined PLATFORM_WIN32) || (defined PLATFORM_POCKETPC) | ||
| 2452 | __declspec(dllexport) | ||
| 2453 | #endif | ||
| 2454 | luaopen_lanes( lua_State *L ) | ||
| 2455 | { | ||
| 2456 | // Create main module interface table | ||
| 2457 | // we only have 1 closure, which must be called to configure Lanes | ||
| 2458 | STACK_GROW( L, 3); | ||
| 2459 | STACK_CHECK( L) | ||
| 2460 | lua_newtable(L); | ||
| 2461 | lua_pushvalue(L, 1); // module name | ||
| 2462 | lua_pushvalue(L, -2); // module table | ||
| 2463 | lua_pushcclosure( L, LG_configure, 2); | ||
| 2464 | if( s_initCount == 0) | ||
| 2465 | { | ||
| 2466 | lua_setfield( L, -2, "configure"); | ||
| 2467 | } | ||
| 2468 | else // already initialized: call it mmediately and be done | ||
| 2469 | { | ||
| 2470 | lua_pushinteger( L, 666); // any value will do, it will be ignored | ||
| 2471 | lua_call( L, 1, 0); | ||
| 2472 | } | ||
| 2473 | STACK_END( L, 1) | ||
| 2474 | return 1; | ||
| 2336 | } | 2475 | } |
| 2337 | 2476 | ||
| 2338 | 2477 | ||
diff --git a/src/lanes.lua b/src/lanes.lua index 252d151..8837e4b 100644 --- a/src/lanes.lua +++ b/src/lanes.lua | |||
| @@ -39,11 +39,19 @@ THE SOFTWARE. | |||
| 39 | =============================================================================== | 39 | =============================================================================== |
| 40 | ]]-- | 40 | ]]-- |
| 41 | 41 | ||
| 42 | module( "lanes", package.seeall ) | 42 | -- Lua 5.1: module() creates a global variable |
| 43 | -- Lua 5.2: module() might go away | ||
| 44 | -- almost everything module() does is done by require() | ||
| 45 | -- -> simply create a table, populate it, return it, and be done | ||
| 46 | local lanes = {} | ||
| 47 | |||
| 48 | lanes.configure = function( _nb_keepers, _timers) | ||
| 43 | 49 | ||
| 44 | local mm = require "lua51-lanes" | 50 | local mm = require "lua51-lanes" |
| 45 | assert( type(mm)=="table" ) | 51 | assert( type(mm)=="table" ) |
| 46 | 52 | ||
| 53 | -- configure() is available only the first time lua51-lanes is required process-wide, and we *must* call it to have the other functions in the interface | ||
| 54 | if mm.configure then mm.configure( _nb_keepers) end | ||
| 47 | 55 | ||
| 48 | local thread_new = assert(mm.thread_new) | 56 | local thread_new = assert(mm.thread_new) |
| 49 | 57 | ||
| @@ -140,6 +148,7 @@ end | |||
| 140 | -- | 148 | -- |
| 141 | -- .globals: table of globals to set for a new thread (passed by value) | 149 | -- .globals: table of globals to set for a new thread (passed by value) |
| 142 | -- | 150 | -- |
| 151 | -- .required: table of packages to require | ||
| 143 | -- ... (more options may be introduced later) ... | 152 | -- ... (more options may be introduced later) ... |
| 144 | -- | 153 | -- |
| 145 | -- Calling with a function parameter ('lane_func') ends the string/table | 154 | -- Calling with a function parameter ('lane_func') ends the string/table |
| @@ -161,7 +170,8 @@ local valid_libs= { | |||
| 161 | ["*"]= true | 170 | ["*"]= true |
| 162 | } | 171 | } |
| 163 | 172 | ||
| 164 | function gen( ... ) | 173 | -- PUBLIC LANES API |
| 174 | local function gen( ... ) | ||
| 165 | local opt= {} | 175 | local opt= {} |
| 166 | local libs= nil | 176 | local libs= nil |
| 167 | local lev= 2 -- level for errors | 177 | local lev= 2 -- level for errors |
| @@ -204,27 +214,34 @@ function gen( ... ) | |||
| 204 | end | 214 | end |
| 205 | end | 215 | end |
| 206 | 216 | ||
| 207 | local prio, cs, g_tbl, packagepath, packagecpath | 217 | local prio, cs, g_tbl, packagepath, packagecpath, required |
| 208 | 218 | ||
| 209 | for k,v in pairs(opt) do | 219 | for k,v in pairs(opt) do |
| 210 | if k=="priority" then prio= v | 220 | if k=="priority" then prio= v |
| 211 | elseif k=="cancelstep" then cs= (v==true) and 100 or | 221 | elseif k=="cancelstep" then |
| 212 | (v==false) and 0 or | 222 | cs = (v==true) and 100 or |
| 213 | type(v)=="number" and v or | 223 | (v==false) and 0 or |
| 214 | error( "Bad cancelstep: "..tostring(v), lev ) | 224 | type(v)=="number" and v or |
| 225 | error( "Bad cancelstep: "..tostring(v), lev ) | ||
| 215 | elseif k=="globals" then g_tbl= v | 226 | elseif k=="globals" then g_tbl= v |
| 216 | elseif k=="packagepath" then packagepath= v | 227 | elseif k=="packagepath" then |
| 217 | elseif k=="packagecpath" then packagecpath= v | 228 | packagepath = (type( v) == "string") and v or error( "Bad packagepath: " .. tostring( v), lev) |
| 229 | elseif k=="packagecpath" then | ||
| 230 | packagecpath = (type( v) == "string") and v or error( "Bad packagecpath: " .. tostring( v), lev) | ||
| 231 | elseif k=="required" then | ||
| 232 | required= (type( v) == "table") and v or error( "Bad required: " .. tostring( v), lev) | ||
| 218 | --.. | 233 | --.. |
| 219 | elseif k==1 then error( "unkeyed option: ".. tostring(v), lev ) | 234 | elseif k==1 then error( "unkeyed option: ".. tostring(v), lev ) |
| 220 | else error( "Bad option: ".. tostring(k), lev ) | 235 | else error( "Bad option: ".. tostring(k), lev ) |
| 221 | end | 236 | end |
| 222 | end | 237 | end |
| 223 | 238 | ||
| 239 | if not packagepath then packagepath = package.path end | ||
| 240 | if not packagecpath then packagecpath = package.cpath end | ||
| 224 | -- Lane generator | 241 | -- Lane generator |
| 225 | -- | 242 | -- |
| 226 | return function(...) | 243 | return function(...) |
| 227 | return thread_new( func, libs, cs, prio, g_tbl, packagepath, packagecpath, ...) -- args | 244 | return thread_new( func, libs, cs, prio, g_tbl, packagepath, packagecpath, required, ...) -- args |
| 228 | end | 245 | end |
| 229 | end | 246 | end |
| 230 | 247 | ||
| @@ -235,12 +252,16 @@ end | |||
| 235 | ----- | 252 | ----- |
| 236 | -- lanes.linda() -> linda_ud | 253 | -- lanes.linda() -> linda_ud |
| 237 | -- | 254 | -- |
| 238 | linda = mm.linda | 255 | -- PUBLIC LANES API |
| 256 | local linda = mm.linda | ||
| 239 | 257 | ||
| 240 | 258 | ||
| 241 | ---=== Timers ===--- | 259 | ---=== Timers ===--- |
| 242 | local want_timers = true | 260 | |
| 243 | if want_timers then | 261 | -- PUBLIC LANES API |
| 262 | local timer = function() error "timers are not active" end | ||
| 263 | |||
| 264 | if _timers ~= "NO_TIMERS" then | ||
| 244 | 265 | ||
| 245 | local timer_gateway= assert( mm.timer_gateway ) | 266 | local timer_gateway= assert( mm.timer_gateway ) |
| 246 | -- | 267 | -- |
| @@ -424,6 +445,8 @@ if first_time then | |||
| 424 | assert( key and wakeup_at and period ) | 445 | assert( key and wakeup_at and period ) |
| 425 | 446 | ||
| 426 | set_timer( linda, key, wakeup_at, period>0 and period or nil ) | 447 | set_timer( linda, key, wakeup_at, period>0 and period or nil ) |
| 448 | elseif secs == 0 then -- got no value while block-waiting? | ||
| 449 | WR( "timer lane: no linda, aborted?") | ||
| 427 | end | 450 | end |
| 428 | end | 451 | end |
| 429 | end )() | 452 | end )() |
| @@ -432,7 +455,8 @@ end | |||
| 432 | ----- | 455 | ----- |
| 433 | -- = timer( linda_h, key_val, date_tbl|first_secs [,period_secs] ) | 456 | -- = timer( linda_h, key_val, date_tbl|first_secs [,period_secs] ) |
| 434 | -- | 457 | -- |
| 435 | function timer( linda, key, a, period ) | 458 | -- PUBLIC LANES API |
| 459 | timer = function( linda, key, a, period ) | ||
| 436 | 460 | ||
| 437 | if a==0.0 then | 461 | if a==0.0 then |
| 438 | -- Caller expects to get current time stamp in Linda, on return | 462 | -- Caller expects to get current time stamp in Linda, on return |
| @@ -456,7 +480,7 @@ function timer( linda, key, a, period ) | |||
| 456 | timer_gateway:send( TGW_KEY, linda, key, wakeup_at, period ) | 480 | timer_gateway:send( TGW_KEY, linda, key, wakeup_at, period ) |
| 457 | end | 481 | end |
| 458 | 482 | ||
| 459 | end -- want_timers | 483 | end -- _timers |
| 460 | 484 | ||
| 461 | ---=== Lock & atomic generators ===--- | 485 | ---=== Lock & atomic generators ===--- |
| 462 | 486 | ||
| @@ -473,7 +497,8 @@ end -- want_timers | |||
| 473 | -- Returns an access function that allows 'N' simultaneous entries between | 497 | -- Returns an access function that allows 'N' simultaneous entries between |
| 474 | -- acquire (+M) and release (-M). For binary locks, use M==1. | 498 | -- acquire (+M) and release (-M). For binary locks, use M==1. |
| 475 | -- | 499 | -- |
| 476 | function genlock( linda, key, N ) | 500 | -- PUBLIC LANES API |
| 501 | local function genlock( linda, key, N ) | ||
| 477 | linda:limit(key,N) | 502 | linda:limit(key,N) |
| 478 | linda:set(key,nil) -- clears existing data | 503 | linda:set(key,nil) -- clears existing data |
| 479 | 504 | ||
| @@ -506,7 +531,8 @@ end | |||
| 506 | -- Returns an access function that allows atomic increment/decrement of the | 531 | -- Returns an access function that allows atomic increment/decrement of the |
| 507 | -- number in 'key'. | 532 | -- number in 'key'. |
| 508 | -- | 533 | -- |
| 509 | function genatomic( linda, key, initial_val ) | 534 | -- PUBLIC LANES API |
| 535 | local function genatomic( linda, key, initial_val ) | ||
| 510 | linda:limit(key,2) -- value [,true] | 536 | linda:limit(key,2) -- value [,true] |
| 511 | linda:set(key,initial_val or 0.0) -- clears existing data (also queue) | 537 | linda:set(key,initial_val or 0.0) -- clears existing data (also queue) |
| 512 | 538 | ||
| @@ -522,4 +548,23 @@ end | |||
| 522 | 548 | ||
| 523 | -- newuserdata = mm.newuserdata | 549 | -- newuserdata = mm.newuserdata |
| 524 | 550 | ||
| 551 | -- activate full interface | ||
| 552 | lanes.gen = gen | ||
| 553 | lanes.linda = mm.linda | ||
| 554 | lanes.timer = timer | ||
| 555 | lanes.genlock = genlock | ||
| 556 | lanes.genatomic = genatomic | ||
| 557 | -- from now on, calling configure does nothing but checking that we don't call it with parameters that changed compared to the first invocation | ||
| 558 | lanes.configure = function( _nk, _t) | ||
| 559 | if _nk ~= _nb_keepers then | ||
| 560 | error( "mismatched configuration: " .. tostring( _nk) .. " keepers instead of " .. tostring( _nb_keepers)) | ||
| 561 | end | ||
| 562 | if _t ~= _timers then | ||
| 563 | error( "mismatched configuration: " .. tostring( _t) .. " timer activity instead of " .. tostring( _timers)) | ||
| 564 | end | ||
| 565 | end | ||
| 566 | end -- lanes.configure | ||
| 567 | |||
| 525 | --the end | 568 | --the end |
| 569 | return lanes | ||
| 570 | |||
diff --git a/src/threading.c b/src/threading.c index 00be243..0a07d47 100644 --- a/src/threading.c +++ b/src/threading.c | |||
| @@ -74,6 +74,7 @@ THE SOFTWARE. | |||
| 74 | #if (defined PLATFORM_WIN32) || (defined PLATFORM_POCKETPC) | 74 | #if (defined PLATFORM_WIN32) || (defined PLATFORM_POCKETPC) |
| 75 | static void FAIL( const char *funcname, int rc ) { | 75 | static void FAIL( const char *funcname, int rc ) { |
| 76 | fprintf( stderr, "%s() failed! (%d)\n", funcname, rc ); | 76 | fprintf( stderr, "%s() failed! (%d)\n", funcname, rc ); |
| 77 | __debugbreak(); // give a chance to the debugger! | ||
| 77 | abort(); | 78 | abort(); |
| 78 | } | 79 | } |
| 79 | #endif | 80 | #endif |
diff --git a/src/tools.c b/src/tools.c index f9854db..b412e84 100644 --- a/src/tools.c +++ b/src/tools.c | |||
| @@ -31,6 +31,7 @@ THE SOFTWARE. | |||
| 31 | */ | 31 | */ |
| 32 | 32 | ||
| 33 | #include "tools.h" | 33 | #include "tools.h" |
| 34 | #include "keeper.h" | ||
| 34 | 35 | ||
| 35 | #include "lualib.h" | 36 | #include "lualib.h" |
| 36 | #include "lauxlib.h" | 37 | #include "lauxlib.h" |
| @@ -66,7 +67,7 @@ void luaG_dump( lua_State* L ) { | |||
| 66 | // enable it for more debugging. | 67 | // enable it for more debugging. |
| 67 | // | 68 | // |
| 68 | STACK_CHECK(L) | 69 | STACK_CHECK(L) |
| 69 | STACK_GROW( L, 2 ) | 70 | STACK_GROW( L, 2); |
| 70 | 71 | ||
| 71 | lua_getglobal( L, "tostring" ); | 72 | lua_getglobal( L, "tostring" ); |
| 72 | // | 73 | // |
| @@ -108,21 +109,302 @@ static const luaL_Reg libs[] = { | |||
| 108 | 109 | ||
| 109 | static bool_t openlib( lua_State *L, const char *name, size_t len ) { | 110 | static bool_t openlib( lua_State *L, const char *name, size_t len ) { |
| 110 | 111 | ||
| 111 | unsigned i; | 112 | unsigned i; |
| 112 | bool_t all= strncmp( name, "*", len ) == 0; | 113 | bool_t all= strncmp( name, "*", len ) == 0; |
| 113 | 114 | ||
| 114 | for( i=0; libs[i].name; i++ ) { | 115 | for( i=0; libs[i].name; i++ ) |
| 115 | if (all || (strncmp(name, libs[i].name, len) ==0)) { | 116 | { |
| 116 | if (libs[i].func) { | 117 | if (all || (strncmp(name, libs[i].name, len) ==0)) |
| 117 | STACK_GROW(L,2); | 118 | { |
| 118 | lua_pushcfunction( L, libs[i].func ); | 119 | if (libs[i].func) |
| 119 | lua_pushstring( L, libs[i].name ); | 120 | { |
| 120 | lua_call( L, 1, 0 ); | 121 | STACK_GROW(L,1); |
| 121 | } | 122 | STACK_CHECK(L) |
| 122 | if (!all) return TRUE; | 123 | lua_pushcfunction( L, libs[i].func); |
| 123 | } | 124 | // pushes the module table on the stack |
| 124 | } | 125 | lua_call( L, 0, 1); |
| 125 | return all; | 126 | populate_func_lookup_table( L, -1, libs[i].name); |
| 127 | // remove the module when we are done | ||
| 128 | lua_pop( L, 1); | ||
| 129 | STACK_END(L, 0) | ||
| 130 | } | ||
| 131 | if (!all) return TRUE; | ||
| 132 | } | ||
| 133 | } | ||
| 134 | return all; | ||
| 135 | } | ||
| 136 | |||
| 137 | static int dummy_writer(lua_State *L, const void* p, size_t sz, void* ud) | ||
| 138 | { | ||
| 139 | return 666; | ||
| 140 | } | ||
| 141 | |||
| 142 | |||
| 143 | /* | ||
| 144 | * differentiation between C, bytecode and JIT-fast functions | ||
| 145 | * | ||
| 146 | * | ||
| 147 | * +----------+------------+----------+ | ||
| 148 | * | bytecode | C function | JIT-fast | | ||
| 149 | * +-----------------+----------+------------+----------+ | ||
| 150 | * | lua_topointer | | | | | ||
| 151 | * +-----------------+----------+------------+----------+ | ||
| 152 | * | lua_tocfunction | NULL | | NULL | | ||
| 153 | * +-----------------+----------+------------+----------+ | ||
| 154 | * | lua_dump | 666 | 1 | 1 | | ||
| 155 | * +-----------------+----------+------------+----------+ | ||
| 156 | */ | ||
| 157 | |||
| 158 | typedef enum | ||
| 159 | { | ||
| 160 | FST_Bytecode, | ||
| 161 | FST_Native, | ||
| 162 | FST_FastJIT | ||
| 163 | } FuncSubType; | ||
| 164 | |||
| 165 | FuncSubType luaG_getfuncsubtype( lua_State *L, int _i) | ||
| 166 | { | ||
| 167 | if( lua_tocfunction( L, _i)) | ||
| 168 | { | ||
| 169 | return FST_Native; | ||
| 170 | } | ||
| 171 | { | ||
| 172 | int mustpush = 0, dumpres; | ||
| 173 | if( STACK_ABS( L, _i) != lua_gettop( L)) | ||
| 174 | { | ||
| 175 | lua_pushvalue( L, _i); | ||
| 176 | mustpush = 1; | ||
| 177 | } | ||
| 178 | // the provided writer fails with code 666 | ||
| 179 | // therefore, anytime we get 666, this means that lua_dump() attempted a dump | ||
| 180 | // all other cases mean this is either a C or LuaJIT-fast function | ||
| 181 | dumpres = lua_dump( L, dummy_writer, NULL); | ||
| 182 | lua_pop( L, mustpush); | ||
| 183 | if( dumpres == 666) | ||
| 184 | { | ||
| 185 | return FST_Bytecode; | ||
| 186 | } | ||
| 187 | } | ||
| 188 | return FST_FastJIT; | ||
| 189 | } | ||
| 190 | |||
| 191 | static lua_CFunction luaG_tocfunction( lua_State *L, int _i, FuncSubType *_out) | ||
| 192 | { | ||
| 193 | lua_CFunction p = lua_tocfunction( L, _i); | ||
| 194 | *_out = luaG_getfuncsubtype( L, _i); | ||
| 195 | return p; | ||
| 196 | } | ||
| 197 | |||
| 198 | |||
| 199 | #define LOOKUP_KEY "ddea37aa-50c7-4d3f-8e0b-fb7a9d62bac5" | ||
| 200 | #define LOOKUP_KEY_CACHE "d1059270-4976-4193-a55b-c952db5ab7cd" | ||
| 201 | |||
| 202 | |||
| 203 | // inspired from tconcat() in ltablib.c | ||
| 204 | static char const * luaG_pushFQN(lua_State *L, int t, int last) | ||
| 205 | { | ||
| 206 | int i = 1; | ||
| 207 | luaL_Buffer b; | ||
| 208 | STACK_CHECK( L) | ||
| 209 | luaL_buffinit(L, &b); | ||
| 210 | for( ; i < last; i++) | ||
| 211 | { | ||
| 212 | lua_rawgeti( L, t, i); | ||
| 213 | luaL_addvalue( &b); | ||
| 214 | luaL_addlstring(&b, ".", 1); | ||
| 215 | } | ||
| 216 | if (i == last) /* add last value (if interval was not empty) */ | ||
| 217 | { | ||
| 218 | lua_rawgeti( L, t, i); | ||
| 219 | luaL_addvalue( &b); | ||
| 220 | } | ||
| 221 | luaL_pushresult( &b); | ||
| 222 | STACK_END( L, 1) | ||
| 223 | return lua_tostring( L, -1); | ||
| 224 | } | ||
| 225 | |||
| 226 | |||
| 227 | static void populate_func_lookup_table_recur( lua_State *L, int _ctx_base, int _i, int _depth) | ||
| 228 | { | ||
| 229 | lua_Integer visit_count; | ||
| 230 | // slot 1 in the stack contains the table that receives everything we found | ||
| 231 | int const dest = _ctx_base; | ||
| 232 | // slot 2 contains a table that, when concatenated, produces the fully qualified name of scanned elements in the table provided at slot _i | ||
| 233 | int const fqn = _ctx_base + 1; | ||
| 234 | // slot 3 contains a cache that stores all already visited tables to avoid infinite recursion loops | ||
| 235 | int const cache = _ctx_base + 2; | ||
| 236 | // we need to remember subtables to process them after functions encountered at the current depth (breadth-first search) | ||
| 237 | int const breadth_first_cache = lua_gettop( L) + 1; | ||
| 238 | |||
| 239 | STACK_GROW( L, 6); | ||
| 240 | // slot _i contains a table where we search for functions | ||
| 241 | STACK_CHECK( L) // ... {_i} | ||
| 242 | |||
| 243 | // if table is already visited, we are done | ||
| 244 | lua_pushvalue( L, _i); // ... {_i} {} | ||
| 245 | lua_rawget( L, cache); // ... {_i} nil|n | ||
| 246 | visit_count = lua_tointeger( L, -1); // 0 if nil, else n | ||
| 247 | lua_pop( L, 1); // ... {_i} | ||
| 248 | STACK_MID( L, 0) | ||
| 249 | if( visit_count > 0) | ||
| 250 | { | ||
| 251 | return; | ||
| 252 | } | ||
| 253 | |||
| 254 | // remember we visited this table (1-visit count) | ||
| 255 | lua_pushvalue( L, _i); // ... {_i} {} | ||
| 256 | lua_pushinteger( L, visit_count + 1); // ... {_i} {} 1 | ||
| 257 | lua_rawset( L, cache); // ... {_i} | ||
| 258 | STACK_MID( L, 0) | ||
| 259 | |||
| 260 | // this table is at breadth_first_cache index | ||
| 261 | lua_newtable( L); // ... {_i} {bfc} | ||
| 262 | ASSERT_L( lua_gettop( L) == breadth_first_cache); | ||
| 263 | // iterate over all entries in the processed table | ||
| 264 | lua_pushnil( L); // ... {_i} {bfc} nil | ||
| 265 | while( lua_next( L, _i) != 0) // ... {_i} {bfc} k v | ||
| 266 | { | ||
| 267 | // just for debug, not actually needed | ||
| 268 | //char const * key = (lua_type( L, -2) == LUA_TSTRING) ? lua_tostring( L, -2) : "not a string"; | ||
| 269 | // subtable: process it recursively | ||
| 270 | if( lua_istable( L, -1)) // ... {_i} {bfc} k {} | ||
| 271 | { | ||
| 272 | // increment visit count to make sure we will actually scan it at this recursive level | ||
| 273 | lua_pushvalue( L, -1); // ... {_i} {bfc} k {} {} | ||
| 274 | lua_pushvalue( L, -1); // ... {_i} {bfc} k {} {} {} | ||
| 275 | lua_rawget( L, cache); // ... {_i} {bfc} k {} {} n? | ||
| 276 | visit_count = lua_tointeger( L, -1) + 1; // 1 if we got nil, else n+1 | ||
| 277 | lua_pop( L, 1); // ... {_i} {bfc} k {} {} | ||
| 278 | lua_pushinteger( L, visit_count); // ... {_i} {bfc} k {} {} n | ||
| 279 | lua_rawset( L, cache); // ... {_i} {bfc} k {} | ||
| 280 | // store the table in the breadth-first cache | ||
| 281 | lua_pushvalue( L, -2); // ... {_i} {bfc} k {} k | ||
| 282 | lua_insert( L, -2); // ... {_i} {bfc} k k {} | ||
| 283 | lua_rawset( L, breadth_first_cache); // ... {_i} {bfc} k | ||
| 284 | STACK_MID( L, 2) | ||
| 285 | } | ||
| 286 | else if( lua_isfunction( L, -1)) // ... {_i} {bfc} k func | ||
| 287 | { | ||
| 288 | if( luaG_getfuncsubtype( L, -1) != FST_Bytecode) | ||
| 289 | { | ||
| 290 | char const *fqnString; | ||
| 291 | bool_t not_registered; | ||
| 292 | // first, skip everything if the function is already known | ||
| 293 | lua_pushvalue( L, -1); // ... {_i} {bfc} k func func | ||
| 294 | lua_rawget( L, dest); // ... {_i} {bfc} k func name? | ||
| 295 | not_registered = lua_isnil( L, -1); | ||
| 296 | lua_pop( L, 1); // ... {_i} {bfc} k func | ||
| 297 | if( not_registered) | ||
| 298 | { | ||
| 299 | ++ _depth; | ||
| 300 | // push function name in fqn stack (note that concatenation will crash if name is a not string!) | ||
| 301 | lua_pushvalue( L, -2); // ... {_i} {bfc} k func k | ||
| 302 | lua_rawseti( L, fqn, _depth); // ... {_i} {bfc} k func | ||
| 303 | // generate name | ||
| 304 | fqnString = luaG_pushFQN( L, fqn, _depth); // ... {_i} {bfc} k func "f.q.n" | ||
| 305 | //puts( fqnString); | ||
| 306 | // prepare the stack for database feed | ||
| 307 | lua_pushvalue( L, -1); // ... {_i} {bfc} k func "f.q.n" "f.q.n" | ||
| 308 | lua_pushvalue( L, -3); // ... {_i} {bfc} k func "f.q.n" "f.q.n" func | ||
| 309 | // t["f.q.n"] = func | ||
| 310 | lua_rawset( L, dest); // ... {_i} {bfc} k func "f.q.n" | ||
| 311 | // t[func] = "f.q.n" | ||
| 312 | lua_rawset( L, dest); // ... {_i} {bfc} k | ||
| 313 | // remove table name from fqn stack | ||
| 314 | lua_pushnil( L); // ... {_i} {bfc} k nil | ||
| 315 | lua_rawseti( L, fqn, _depth); // ... {_i} {bfc} k | ||
| 316 | -- _depth; | ||
| 317 | } | ||
| 318 | else | ||
| 319 | { | ||
| 320 | lua_pop( L, 1); // ... {_i} {bfc} k | ||
| 321 | } | ||
| 322 | } | ||
| 323 | else | ||
| 324 | { | ||
| 325 | lua_pop( L, 1); // ... {_i} {bfc} k | ||
| 326 | } | ||
| 327 | } | ||
| 328 | else | ||
| 329 | { | ||
| 330 | lua_pop( L, 1); // ... {_i} {bfc} k | ||
| 331 | } | ||
| 332 | STACK_MID( L, 2) | ||
| 333 | } | ||
| 334 | // now process the tables we encountered at that depth | ||
| 335 | ++ _depth; | ||
| 336 | lua_pushnil( L); // ... {_i} {bfc} nil | ||
| 337 | while( lua_next( L, breadth_first_cache) != 0) // ... {_i} {bfc} k {} | ||
| 338 | { | ||
| 339 | // un-visit this table in case we do need to process it | ||
| 340 | lua_pushvalue( L, -1); // ... {_i} {bfc} k {} {} | ||
| 341 | lua_rawget( L, cache); // ... {_i} {bfc} k {} n | ||
| 342 | ASSERT_L( lua_type( L, -1) == LUA_TNUMBER); | ||
| 343 | visit_count = lua_tointeger( L, -1) - 1; | ||
| 344 | lua_pop( L, 1); // ... {_i} {bfc} k {} | ||
| 345 | lua_pushvalue( L, -1); // ... {_i} {bfc} k {} {} | ||
| 346 | if( visit_count > 0) | ||
| 347 | { | ||
| 348 | lua_pushinteger( L, visit_count); // ... {_i} {bfc} k {} {} n | ||
| 349 | } | ||
| 350 | else | ||
| 351 | { | ||
| 352 | lua_pushnil( L); // ... {_i} {bfc} k {} {} nil | ||
| 353 | } | ||
| 354 | lua_rawset( L, cache); // ... {_i} {bfc} k {} | ||
| 355 | // push table name in fqn stack (note that concatenation will crash if name is a not string!) | ||
| 356 | lua_pushvalue( L, -2); // ... {_i} {bfc} k {} k | ||
| 357 | lua_rawseti( L, fqn, _depth); // ... {_i} {bfc} k {} | ||
| 358 | populate_func_lookup_table_recur( L, _ctx_base, lua_gettop( L), _depth); // ... {_i} {bfc} k {} | ||
| 359 | lua_pop( L, 1); // ... {_i} {bfc} k | ||
| 360 | STACK_MID( L, 2) | ||
| 361 | } | ||
| 362 | // remove table name from fqn stack | ||
| 363 | lua_pushnil( L); // ... {_i} {bfc} nil | ||
| 364 | lua_rawseti( L, fqn, _depth); // ... {_i} {bfc} | ||
| 365 | -- _depth; | ||
| 366 | // we are done with our cache | ||
| 367 | lua_pop( L, 1); // ... {_i} | ||
| 368 | STACK_END( L, 0) | ||
| 369 | // we are done // ... {_i} {bfc} | ||
| 370 | } | ||
| 371 | |||
| 372 | /* | ||
| 373 | * create a "fully.qualified.name" <-> function equivalence dabase | ||
| 374 | */ | ||
| 375 | void populate_func_lookup_table( lua_State *L, int _i, char const *_name) | ||
| 376 | { | ||
| 377 | int const ctx_base = lua_gettop( L) + 1; | ||
| 378 | int const in_base = STACK_ABS( L, _i); | ||
| 379 | int const start_depth = _name ? 1 : 0; | ||
| 380 | //printf( "%p: populate_func_lookup_table('%s')\n", L, _name ? _name : "NULL"); | ||
| 381 | STACK_GROW( L, 3); | ||
| 382 | STACK_CHECK( L) | ||
| 383 | lua_getfield( L, LUA_REGISTRYINDEX, LOOKUP_KEY); // {}? | ||
| 384 | if( lua_isnil( L, -1)) // nil | ||
| 385 | { | ||
| 386 | lua_pop( L, 1); // | ||
| 387 | lua_newtable( L); // {} | ||
| 388 | lua_pushvalue( L, -1); // {} {} | ||
| 389 | lua_setfield( L, LUA_REGISTRYINDEX, LOOKUP_KEY); // {} | ||
| 390 | } | ||
| 391 | lua_newtable( L); // {} {fqn} | ||
| 392 | if( _name) | ||
| 393 | { | ||
| 394 | lua_pushstring( L, _name); // {} {fqn} "name" | ||
| 395 | lua_rawseti( L, -2, start_depth); // {} {fqn} | ||
| 396 | } | ||
| 397 | lua_getfield( L, LUA_REGISTRYINDEX, LOOKUP_KEY_CACHE); // {} {fqn} {cache}? | ||
| 398 | if( lua_isnil( L, -1)) | ||
| 399 | { | ||
| 400 | lua_pop( L, 1); // {} {fqn} | ||
| 401 | lua_newtable( L); // {} {fqn} {cache} | ||
| 402 | lua_pushvalue( L, -1); // {} {fqn} {cache} {cache} | ||
| 403 | lua_setfield( L, LUA_REGISTRYINDEX, LOOKUP_KEY_CACHE); // {} {fqn} {cache} | ||
| 404 | } | ||
| 405 | populate_func_lookup_table_recur( L, ctx_base, in_base, start_depth); // {...} {fqn} {cache} | ||
| 406 | lua_pop( L, 3); | ||
| 407 | STACK_END( L, 0) | ||
| 126 | } | 408 | } |
| 127 | 409 | ||
| 128 | /* | 410 | /* |
| @@ -139,33 +421,39 @@ static bool_t openlib( lua_State *L, const char *name, size_t len ) { | |||
| 139 | */ | 421 | */ |
| 140 | #define is_name_char(c) (isalpha(c) || (c)=='*') | 422 | #define is_name_char(c) (isalpha(c) || (c)=='*') |
| 141 | 423 | ||
| 142 | const char *luaG_openlibs( lua_State *L, const char *libs ) { | 424 | const char *luaG_openlibs( lua_State *L, const char *libs) |
| 143 | const char *p; | 425 | { |
| 144 | unsigned len; | 426 | const char *p; |
| 427 | unsigned len; | ||
| 145 | 428 | ||
| 146 | if (!libs) return NULL; // no libs, not even 'base' | 429 | if (!libs) return NULL; // no libs, not even 'base' |
| 147 | 430 | ||
| 148 | // 'lua.c' stops GC during initialization so perhaps its a good idea. :) | 431 | // 'lua.c' stops GC during initialization so perhaps its a good idea. :) |
| 149 | // | 432 | // |
| 150 | lua_gc(L, LUA_GCSTOP, 0); | 433 | lua_gc( L, LUA_GCSTOP, 0); |
| 151 | 434 | ||
| 152 | // Anything causes 'base' to be taken in | 435 | // Anything causes 'base' to be taken in |
| 153 | // | 436 | // |
| 154 | STACK_GROW(L,2); | 437 | STACK_GROW(L,2); |
| 155 | lua_pushcfunction( L, luaopen_base ); | 438 | STACK_CHECK(L) |
| 156 | lua_pushliteral( L, "" ); | 439 | lua_pushcfunction( L, luaopen_base); |
| 157 | lua_call( L, 1, 0 ); | 440 | lua_call( L, 0, 1); |
| 158 | 441 | // after opening base, register the functions they exported in our name<->function database | |
| 159 | for( p= libs; *p; p+=len ) { | 442 | populate_func_lookup_table( L, LUA_GLOBALSINDEX, NULL); |
| 160 | len=0; | 443 | lua_pop( L, 1); |
| 161 | while (*p && !is_name_char(*p)) p++; // bypass delimiters | 444 | STACK_MID( L, 0); |
| 162 | while (is_name_char(p[len])) len++; // bypass name | 445 | for( p= libs; *p; p+=len ) |
| 163 | if (len && (!openlib( L, p, len ))) | 446 | { |
| 164 | break; | 447 | len=0; |
| 165 | } | 448 | while (*p && !is_name_char(*p)) p++; // bypass delimiters |
| 166 | lua_gc(L, LUA_GCRESTART, 0); | 449 | while (is_name_char(p[len])) len++; // bypass name |
| 450 | if (len && (!openlib( L, p, len ))) | ||
| 451 | break; | ||
| 452 | } | ||
| 453 | STACK_END(L,0) | ||
| 454 | lua_gc(L, LUA_GCRESTART, 0); | ||
| 167 | 455 | ||
| 168 | return *p ? p : NULL; | 456 | return *p ? p : NULL; |
| 169 | } | 457 | } |
| 170 | 458 | ||
| 171 | 459 | ||
| @@ -284,7 +572,7 @@ luaG_IdFunction get_idfunc( lua_State *L, int index ) | |||
| 284 | { | 572 | { |
| 285 | luaG_IdFunction ret; | 573 | luaG_IdFunction ret; |
| 286 | 574 | ||
| 287 | index= STACK_ABS(L,index); | 575 | index = STACK_ABS( L, index); |
| 288 | 576 | ||
| 289 | STACK_GROW(L,1); | 577 | STACK_GROW(L,1); |
| 290 | 578 | ||
| @@ -696,7 +984,7 @@ uint_t get_mt_id( lua_State *L, int i ) { | |||
| 696 | static uint_t last_id= 0; | 984 | static uint_t last_id= 0; |
| 697 | uint_t id; | 985 | uint_t id; |
| 698 | 986 | ||
| 699 | i= STACK_ABS(L,i); | 987 | i = STACK_ABS( L, i); |
| 700 | 988 | ||
| 701 | STACK_GROW(L,3); | 989 | STACK_GROW(L,3); |
| 702 | 990 | ||
| @@ -819,8 +1107,8 @@ static void inter_copy_func( lua_State *L2, uint_t L2_cache_i, lua_State *L, uin | |||
| 819 | 1107 | ||
| 820 | static void push_cached_func( lua_State *L2, uint_t L2_cache_i, lua_State *L, uint_t i ) | 1108 | static void push_cached_func( lua_State *L2, uint_t L2_cache_i, lua_State *L, uint_t i ) |
| 821 | { | 1109 | { |
| 1110 | void * const aspointer = (void*)lua_topointer( L, i ); | ||
| 822 | // TBD: Merge this and same code for tables | 1111 | // TBD: Merge this and same code for tables |
| 823 | |||
| 824 | ASSERT_L( L2_cache_i != 0 ); | 1112 | ASSERT_L( L2_cache_i != 0 ); |
| 825 | 1113 | ||
| 826 | STACK_GROW(L2,3); | 1114 | STACK_GROW(L2,3); |
| @@ -832,7 +1120,7 @@ static void push_cached_func( lua_State *L2, uint_t L2_cache_i, lua_State *L, ui | |||
| 832 | // We don't need to use the from state ('L') in ID since the life span | 1120 | // We don't need to use the from state ('L') in ID since the life span |
| 833 | // is only for the duration of a copy (both states are locked). | 1121 | // is only for the duration of a copy (both states are locked). |
| 834 | // | 1122 | // |
| 835 | lua_pushlightuserdata( L2, (void*)lua_topointer( L, i )); // push a light userdata uniquely representing the function | 1123 | lua_pushlightuserdata( L2, aspointer); // push a light userdata uniquely representing the function |
| 836 | 1124 | ||
| 837 | //fprintf( stderr, "<< ID: %s >>\n", lua_tostring(L2,-1) ); | 1125 | //fprintf( stderr, "<< ID: %s >>\n", lua_tostring(L2,-1) ); |
| 838 | 1126 | ||
| @@ -886,9 +1174,46 @@ static void push_cached_func( lua_State *L2, uint_t L2_cache_i, lua_State *L, ui | |||
| 886 | // | 1174 | // |
| 887 | // L2 [-1]: function | 1175 | // L2 [-1]: function |
| 888 | 1176 | ||
| 889 | ASSERT_L( lua_isfunction(L2,-1) ); | 1177 | ASSERT_L( lua_isfunction(L2,-1)); |
| 890 | } | 1178 | } |
| 891 | 1179 | ||
| 1180 | /* | ||
| 1181 | * Push a looked-up native/LuaJIT function. | ||
| 1182 | */ | ||
| 1183 | static void lookup_native_func( lua_State *L2, lua_State *L, uint_t i) | ||
| 1184 | { | ||
| 1185 | char const *fqn; | ||
| 1186 | size_t len; | ||
| 1187 | _ASSERT_L( L, lua_isfunction( L, i)); | ||
| 1188 | STACK_CHECK( L) | ||
| 1189 | STACK_CHECK( L2) | ||
| 1190 | // fetch the name from the source state's lookup table | ||
| 1191 | lua_getfield( L, LUA_REGISTRYINDEX, LOOKUP_KEY); // {} | ||
| 1192 | _ASSERT_L( L, lua_istable( L, -1)); | ||
| 1193 | lua_pushvalue( L, i); // {} f | ||
| 1194 | lua_rawget( L, -2); // {} "f.q.n" | ||
| 1195 | fqn = lua_tolstring( L, -1, &len); | ||
| 1196 | if( !fqn) | ||
| 1197 | { | ||
| 1198 | luaL_error( L, "function not found in origin transfer database."); | ||
| 1199 | } | ||
| 1200 | // push the equivalent function in the destination's stack, retrieved from the lookup table | ||
| 1201 | lua_getfield( L2, LUA_REGISTRYINDEX, LOOKUP_KEY); // {} | ||
| 1202 | _ASSERT_L( L2, lua_istable( L2, -1)); | ||
| 1203 | lua_pushlstring( L2, fqn, len); // {} "f.q.n" | ||
| 1204 | lua_pop( L, 2); // | ||
| 1205 | lua_rawget( L2, -2); // {} f | ||
| 1206 | if( !lua_isfunction( L2, -1)) | ||
| 1207 | { | ||
| 1208 | // yarglah: luaL_error formatting doesn't support string width modifier! | ||
| 1209 | char message[256]; | ||
| 1210 | sprintf( message, "function %*s not found in destination transfer database.", len, fqn); | ||
| 1211 | luaL_error( L, message); | ||
| 1212 | } | ||
| 1213 | lua_remove( L2, -2); // f | ||
| 1214 | STACK_END( L2, 1) | ||
| 1215 | STACK_END( L, 0) | ||
| 1216 | } | ||
| 892 | 1217 | ||
| 893 | #define LOG_FUNC_INFO 0 | 1218 | #define LOG_FUNC_INFO 0 |
| 894 | 1219 | ||
| @@ -900,114 +1225,132 @@ enum e_vt { | |||
| 900 | }; | 1225 | }; |
| 901 | static bool_t inter_copy_one_( lua_State *L2, uint_t L2_cache_i, lua_State *L, uint_t i, enum e_vt value_type ); | 1226 | static bool_t inter_copy_one_( lua_State *L2, uint_t L2_cache_i, lua_State *L, uint_t i, enum e_vt value_type ); |
| 902 | 1227 | ||
| 903 | static void inter_copy_func( lua_State *L2, uint_t L2_cache_i, lua_State *L, uint_t i ) { | 1228 | static void inter_copy_func( lua_State *L2, uint_t L2_cache_i, lua_State *L, uint_t i ) |
| 904 | 1229 | { | |
| 905 | lua_CFunction cfunc= lua_tocfunction( L,i ); | 1230 | FuncSubType funcSubType; |
| 906 | unsigned n; | 1231 | lua_CFunction cfunc = luaG_tocfunction( L, i, &funcSubType); // NULL for LuaJIT-fast && bytecode functions |
| 907 | 1232 | i = STACK_ABS( L, i); | |
| 908 | ASSERT_L( L2_cache_i != 0 ); | ||
| 909 | |||
| 910 | STACK_GROW(L,2); | ||
| 911 | |||
| 912 | STACK_CHECK(L) | ||
| 913 | if (!cfunc) { // Lua function | ||
| 914 | luaL_Buffer b; | ||
| 915 | const char *s; | ||
| 916 | size_t sz; | ||
| 917 | int tmp; | ||
| 918 | const char *name= NULL; | ||
| 919 | int linedefined = 0; | ||
| 920 | 1233 | ||
| 921 | #if LOG_FUNC_INFO | 1234 | ASSERT_L( L2_cache_i != 0 ); |
| 922 | // "To get information about a function you push it onto the | 1235 | STACK_GROW(L,2); |
| 923 | // stack and start the what string with the character '>'." | 1236 | STACK_CHECK(L) |
| 924 | // | ||
| 925 | { lua_Debug ar; | ||
| 926 | lua_pushvalue( L, i ); | ||
| 927 | lua_getinfo(L, ">nS", &ar); // fills 'name' 'namewhat' and 'linedefined', pops function | ||
| 928 | name= ar.namewhat; | ||
| 929 | linedefined = ar.linedefined; | ||
| 930 | fprintf( stderr, "NAME: %s @ %d\n", ar.short_src, linedefined); // just gives NULL | ||
| 931 | } | ||
| 932 | #endif // LOG_FUNC_INFO | ||
| 933 | // 'lua_dump()' needs the function at top of stack | ||
| 934 | // | ||
| 935 | if (i!=-1) lua_pushvalue( L, i ); | ||
| 936 | 1237 | ||
| 937 | luaL_buffinit(L,&b); | 1238 | if( funcSubType == FST_Bytecode) |
| 938 | tmp= lua_dump(L, buf_writer, &b); | 1239 | { |
| 939 | ASSERT_L(tmp==0); | 1240 | unsigned n; |
| 940 | // | 1241 | luaL_Buffer b; |
| 941 | // "value returned is the error code returned by the last call | 1242 | // 'lua_dump()' needs the function at top of stack |
| 942 | // to the writer" (and we only return 0) | 1243 | // if already on top of the stack, no need to push again |
| 1244 | int needToPush = (i != lua_gettop( L)); | ||
| 1245 | if( needToPush) | ||
| 1246 | lua_pushvalue( L, i); | ||
| 1247 | |||
| 1248 | luaL_buffinit( L, &b); | ||
| 1249 | // | ||
| 1250 | // "value returned is the error code returned by the last call | ||
| 1251 | // to the writer" (and we only return 0) | ||
| 1252 | // not sure this could ever fail but for memory shortage reasons | ||
| 1253 | if( lua_dump( L, buf_writer, &b) != 0) | ||
| 1254 | { | ||
| 1255 | luaL_error( L, "internal error: function dump failed."); | ||
| 1256 | } | ||
| 943 | 1257 | ||
| 944 | luaL_pushresult(&b); // pushes dumped string on 'L' | 1258 | luaL_pushresult( &b); // pushes dumped string on 'L' |
| 945 | s= lua_tolstring(L,-1,&sz); | ||
| 946 | ASSERT_L( s && sz ); | ||
| 947 | 1259 | ||
| 948 | if (i!=-1) lua_remove( L, -2 ); | 1260 | // if not pushed, no need to pop |
| 1261 | if( needToPush) | ||
| 1262 | { | ||
| 1263 | lua_remove( L, -2); | ||
| 1264 | } | ||
| 949 | 1265 | ||
| 950 | // Note: Line numbers seem to be taken precisely from the | 1266 | // transfer the bytecode, then the upvalues, to create a similar closure |
| 951 | // original function. 'name' is not used since the chunk | 1267 | { |
| 952 | // is precompiled (it seems...). | 1268 | const char *name= NULL; |
| 953 | // | 1269 | |
| 954 | // TBD: Can we get the function's original name through, as well? | 1270 | #if LOG_FUNC_INFO |
| 955 | // | 1271 | // "To get information about a function you push it onto the |
| 956 | if (luaL_loadbuffer(L2, s, sz, name) != 0) { | 1272 | // stack and start the what string with the character '>'." |
| 957 | // chunk is precompiled so only LUA_ERRMEM can happen | 1273 | // |
| 958 | // "Otherwise, it pushes an error message" | 1274 | { |
| 959 | // | 1275 | lua_Debug ar; |
| 960 | STACK_GROW( L,1 ); | 1276 | lua_pushvalue( L, i ); |
| 961 | luaL_error( L, "%s", lua_tostring(L2,-1) ); | 1277 | lua_getinfo(L, ">nS", &ar); // fills 'name' 'namewhat' and 'linedefined', pops function |
| 962 | } | 1278 | name= ar.namewhat; |
| 963 | lua_pop(L,1); // remove the dumped string | 1279 | fprintf( stderr, "NAME: %s @ %d\n", ar.short_src, ar.linedefined); // just gives NULL |
| 964 | STACK_MID(L,0) | 1280 | } |
| 965 | } | 1281 | #endif // LOG_FUNC_INFO |
| 1282 | { | ||
| 1283 | const char *s; | ||
| 1284 | size_t sz; | ||
| 1285 | s = lua_tolstring( L, -1, &sz); | ||
| 1286 | ASSERT_L( s && sz); | ||
| 1287 | |||
| 1288 | // Note: Line numbers seem to be taken precisely from the | ||
| 1289 | // original function. 'name' is not used since the chunk | ||
| 1290 | // is precompiled (it seems...). | ||
| 1291 | // | ||
| 1292 | // TBD: Can we get the function's original name through, as well? | ||
| 1293 | // | ||
| 1294 | if (luaL_loadbuffer(L2, s, sz, name) != 0) | ||
| 1295 | { | ||
| 1296 | // chunk is precompiled so only LUA_ERRMEM can happen | ||
| 1297 | // "Otherwise, it pushes an error message" | ||
| 1298 | // | ||
| 1299 | STACK_GROW( L,1); | ||
| 1300 | luaL_error( L, "%s", lua_tostring(L2,-1)); | ||
| 1301 | } | ||
| 1302 | lua_pop( L, 1); // remove the dumped string | ||
| 1303 | } | ||
| 1304 | STACK_MID( L, 0) | ||
| 1305 | |||
| 1306 | /* push over any upvalues; references to this function will come from | ||
| 1307 | * cache so we don't end up in eternal loop. | ||
| 1308 | */ | ||
| 1309 | for( n=0; lua_getupvalue( L, i, 1+n ) != NULL; n++ ) | ||
| 1310 | { | ||
| 1311 | if ((!cfunc) && lua_equal(L,i,-1)) | ||
| 1312 | { | ||
| 1313 | /* Lua closure that has a (recursive) upvalue to itself | ||
| 1314 | */ | ||
| 1315 | lua_pushvalue( L2, -((int)n)-1 ); | ||
| 1316 | } | ||
| 1317 | else | ||
| 1318 | { | ||
| 1319 | if( !inter_copy_one_( L2, L2_cache_i, L, lua_gettop(L), VT_NORMAL)) | ||
| 1320 | luaL_error( L, "Cannot copy upvalue type '%s'", luaG_typename( L, -1)); | ||
| 1321 | } | ||
| 1322 | lua_pop( L, 1); | ||
| 1323 | } | ||
| 1324 | // L2: function + 'n' upvalues (>=0) | ||
| 1325 | |||
| 1326 | STACK_MID(L,0) | ||
| 1327 | |||
| 1328 | // Set upvalues (originally set to 'nil' by 'lua_load') | ||
| 1329 | { | ||
| 1330 | int func_index = lua_gettop( L2) - n; | ||
| 1331 | for( ; n > 0; -- n) | ||
| 1332 | { | ||
| 1333 | char const *rc = lua_setupvalue( L2, func_index, n); | ||
| 1334 | // | ||
| 1335 | // "assigns the value at the top of the stack to the upvalue and returns its name. | ||
| 1336 | // It also pops the value from the stack." | ||
| 1337 | |||
| 1338 | ASSERT_L(rc); // not having enough slots? | ||
| 1339 | } | ||
| 1340 | } | ||
| 1341 | } | ||
| 1342 | } | ||
| 1343 | else // C function OR LuaJIT fast function!!! | ||
| 1344 | { | ||
| 966 | #if LOG_FUNC_INFO | 1345 | #if LOG_FUNC_INFO |
| 967 | else | 1346 | fprintf( stderr, "NAME: [C] function %p \n", cfunc); |
| 968 | { | ||
| 969 | fprintf( stderr, "NAME: [C] function %p \n", cfunc); | ||
| 970 | } | ||
| 971 | #endif // LOG_FUNC_INFO | 1347 | #endif // LOG_FUNC_INFO |
| 972 | 1348 | // No need to transfer upvalues for C/JIT functions since they weren't actually copied, only looked up | |
| 973 | /* push over any upvalues; references to this function will come from | 1349 | lookup_native_func( L2, L, i); |
| 974 | * cache so we don't end up in eternal loop. | 1350 | } |
| 975 | */ | 1351 | STACK_END(L,0) |
| 976 | for( n=0; lua_getupvalue( L, i, 1+n ) != NULL; n++ ) { | ||
| 977 | if ((!cfunc) && lua_equal(L,i,-1)) { | ||
| 978 | /* Lua closure that has a (recursive) upvalue to itself | ||
| 979 | */ | ||
| 980 | lua_pushvalue( L2, -((int)n)-1 ); | ||
| 981 | } else { | ||
| 982 | if (!inter_copy_one_( L2, L2_cache_i, L, lua_gettop(L), VT_NORMAL )) | ||
| 983 | luaL_error( L, "Cannot copy upvalue type '%s'", luaG_typename(L,-1) ); | ||
| 984 | } | ||
| 985 | lua_pop(L,1); | ||
| 986 | } | ||
| 987 | // L2: function + 'n' upvalues (>=0) | ||
| 988 | |||
| 989 | STACK_MID(L,0) | ||
| 990 | |||
| 991 | if (cfunc) { | ||
| 992 | lua_pushcclosure( L2, cfunc, n ); // eats up upvalues | ||
| 993 | } else { | ||
| 994 | // Set upvalues (originally set to 'nil' by 'lua_load') | ||
| 995 | // | ||
| 996 | int func_index= lua_gettop(L2)-n; | ||
| 997 | |||
| 998 | for( ; n>0; n-- ) { | ||
| 999 | const char *rc= lua_setupvalue( L2, func_index, n ); | ||
| 1000 | // | ||
| 1001 | // "assigns the value at the top of the stack to the upvalue and returns its name. | ||
| 1002 | // It also pops the value from the stack." | ||
| 1003 | |||
| 1004 | ASSERT_L(rc); // not having enough slots? | ||
| 1005 | } | ||
| 1006 | } | ||
| 1007 | STACK_END(L,0) | ||
| 1008 | } | 1352 | } |
| 1009 | 1353 | ||
| 1010 | |||
| 1011 | /* | 1354 | /* |
| 1012 | * Copies a value from 'L' state (at index 'i') to 'L2' state. Does not remove | 1355 | * Copies a value from 'L' state (at index 'i') to 'L2' state. Does not remove |
| 1013 | * the original value. | 1356 | * the original value. |
| @@ -1300,32 +1643,36 @@ MUTEX_T require_cs; | |||
| 1300 | // | 1643 | // |
| 1301 | // Upvalues: [1]: original 'require' function | 1644 | // Upvalues: [1]: original 'require' function |
| 1302 | // | 1645 | // |
| 1303 | static int new_require( lua_State *L ) | 1646 | static int new_require( lua_State *L) |
| 1304 | { | 1647 | { |
| 1305 | int rc; | 1648 | int rc, i; |
| 1306 | int args= lua_gettop(L); | 1649 | int args = lua_gettop( L); |
| 1650 | //char const *modname = luaL_checkstring( L, 1); | ||
| 1307 | 1651 | ||
| 1308 | STACK_GROW(L,1); | 1652 | STACK_GROW( L, args + 1); |
| 1309 | STACK_CHECK(L) | 1653 | STACK_CHECK( L) |
| 1654 | |||
| 1655 | lua_pushvalue( L, lua_upvalueindex(1)); | ||
| 1656 | for( i = 1; i <= args; ++ i) | ||
| 1657 | lua_pushvalue( L, i); | ||
| 1310 | 1658 | ||
| 1311 | // Using 'lua_pcall()' to catch errors; otherwise a failing 'require' would | 1659 | // Using 'lua_pcall()' to catch errors; otherwise a failing 'require' would |
| 1312 | // leave us locked, blocking any future 'require' calls from other lanes. | 1660 | // leave us locked, blocking any future 'require' calls from other lanes. |
| 1313 | // | 1661 | // |
| 1314 | MUTEX_LOCK( &require_cs); | 1662 | MUTEX_LOCK( &require_cs); |
| 1315 | { | 1663 | { |
| 1316 | lua_pushvalue( L, lua_upvalueindex(1) ); | 1664 | rc = lua_pcall( L, args, 1 /*retvals*/, 0 /*errfunc*/ ); |
| 1317 | lua_insert( L, 1 ); | ||
| 1318 | |||
| 1319 | rc= lua_pcall( L, args, 1 /*retvals*/, 0 /*errfunc*/ ); | ||
| 1320 | // | 1665 | // |
| 1321 | // LUA_ERRRUN / LUA_ERRMEM | 1666 | // LUA_ERRRUN / LUA_ERRMEM |
| 1322 | } | 1667 | } |
| 1323 | MUTEX_UNLOCK( &require_cs); | 1668 | MUTEX_UNLOCK( &require_cs); |
| 1324 | 1669 | ||
| 1670 | // the required module (or an error message) is left on the stack as returned value by original require function | ||
| 1671 | STACK_END( L, 1) | ||
| 1672 | |||
| 1325 | if (rc) | 1673 | if (rc) |
| 1326 | lua_error(L); // error message already at [-1] | 1674 | lua_error(L); // error message already at [-1] |
| 1327 | 1675 | ||
| 1328 | STACK_END(L,0) | ||
| 1329 | return 1; | 1676 | return 1; |
| 1330 | } | 1677 | } |
| 1331 | 1678 | ||
diff --git a/src/tools.h b/src/tools.h index a080257..1c9b00a 100644 --- a/src/tools.h +++ b/src/tools.h | |||
| @@ -10,10 +10,10 @@ | |||
| 10 | 10 | ||
| 11 | #include <assert.h> | 11 | #include <assert.h> |
| 12 | 12 | ||
| 13 | // Note: The < -10000 test is to leave registry/global/upvalue indices untouched | 13 | // Note: The < LUA_REGISTRYINDEX test is to leave registry/global/upvalue indices untouched |
| 14 | // | 14 | // |
| 15 | #define /*int*/ STACK_ABS(L,n) \ | 15 | #define /*int*/ STACK_ABS(L,n) \ |
| 16 | ( ((n) >= 0 || (n) <= -10000) ? (n) : lua_gettop(L) +(n) +1 ) | 16 | ( ((n) >= 0 || (n) <= LUA_REGISTRYINDEX) ? (n) : lua_gettop(L) +(n) +1 ) |
| 17 | 17 | ||
| 18 | #ifdef NDEBUG | 18 | #ifdef NDEBUG |
| 19 | #define _ASSERT_L(lua,c) /*nothing*/ | 19 | #define _ASSERT_L(lua,c) /*nothing*/ |
| @@ -24,7 +24,7 @@ | |||
| 24 | #define DEBUG() /*nothing*/ | 24 | #define DEBUG() /*nothing*/ |
| 25 | #define DEBUGEXEC(_code) {} /*nothing*/ | 25 | #define DEBUGEXEC(_code) {} /*nothing*/ |
| 26 | #else | 26 | #else |
| 27 | #define _ASSERT_L(lua,c) { if (!(c)) luaL_error( lua, "ASSERT failed: %s:%d '%s'", __FILE__, __LINE__, #c ); } | 27 | #define _ASSERT_L(lua,c) do { if (!(c)) luaL_error( lua, "ASSERT failed: %s:%d '%s'", __FILE__, __LINE__, #c ); } while( 0) |
| 28 | // | 28 | // |
| 29 | #define STACK_CHECK(L) { int _oldtop_##L = lua_gettop(L); | 29 | #define STACK_CHECK(L) { int _oldtop_##L = lua_gettop(L); |
| 30 | #define STACK_MID(L,change) { int a= lua_gettop(L)-_oldtop_##L; int b= (change); \ | 30 | #define STACK_MID(L,change) { int a= lua_gettop(L)-_oldtop_##L; int b= (change); \ |
| @@ -37,7 +37,7 @@ | |||
| 37 | #endif | 37 | #endif |
| 38 | #define ASSERT_L(c) _ASSERT_L(L,c) | 38 | #define ASSERT_L(c) _ASSERT_L(L,c) |
| 39 | 39 | ||
| 40 | #define STACK_GROW(L,n) { if (!lua_checkstack(L,n)) luaL_error( L, "Cannot grow stack!" ); } | 40 | #define STACK_GROW(L,n) do { if (!lua_checkstack(L,n)) luaL_error( L, "Cannot grow stack!" ); } while( 0) |
| 41 | 41 | ||
| 42 | #define LUAG_FUNC( func_name ) static int LG_##func_name( lua_State *L ) | 42 | #define LUAG_FUNC( func_name ) static int LG_##func_name( lua_State *L ) |
| 43 | 43 | ||
| @@ -72,6 +72,7 @@ int luaG_inter_move( lua_State *L, lua_State *L2, uint_t n); | |||
| 72 | extern MUTEX_T deep_lock; | 72 | extern MUTEX_T deep_lock; |
| 73 | extern MUTEX_T mtid_lock; | 73 | extern MUTEX_T mtid_lock; |
| 74 | 74 | ||
| 75 | void populate_func_lookup_table( lua_State *L, int _i, char const *_name); | ||
| 75 | void serialize_require( lua_State *L); | 76 | void serialize_require( lua_State *L); |
| 76 | extern MUTEX_T require_cs; | 77 | extern MUTEX_T require_cs; |
| 77 | 78 | ||
