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 | ||