diff options
author | Benoit Germain <bnt period germain arrobase gmail period com> | 2014-05-16 11:22:39 +0200 |
---|---|---|
committer | Benoit Germain <bnt period germain arrobase gmail period com> | 2014-05-16 11:22:39 +0200 |
commit | 55e53f8a24ce42cadfd2887e50bf0248eb10d301 (patch) | |
tree | a201d614669eff9ea5ac75db9c9e86c50e2a681e | |
parent | 555e3c26c733ce653aafec25ea77b25b6b1bed07 (diff) | |
download | lanes-55e53f8a24ce42cadfd2887e50bf0248eb10d301.tar.gz lanes-55e53f8a24ce42cadfd2887e50bf0248eb10d301.tar.bz2 lanes-55e53f8a24ce42cadfd2887e50bf0248eb10d301.zip |
Minor fixes and tweaks
* bumped version to 3.9.5
* lanes.gen() error reporting improvements
* fix linda.__towatch to return non-nil when the linda is empty
-rw-r--r-- | CHANGES | 14 | ||||
-rw-r--r-- | src/lanes.c | 241 | ||||
-rw-r--r-- | src/lanes.lua | 187 |
3 files changed, 241 insertions, 201 deletions
@@ -1,5 +1,19 @@ | |||
1 | CHANGES: | 1 | CHANGES: |
2 | 2 | ||
3 | CHANGE 112 BGe 16-May-14 | ||
4 | * bumped version to 3.9.5 | ||
5 | * fix linda.__towatch to return non-nil when the linda is empty | ||
6 | * lanes.gen() error reporting improvements | ||
7 | |||
8 | CHANGE 111 BGe 24-Apr-14 | ||
9 | * fixed linda:send() possibly returning an undefined value | ||
10 | |||
11 | CHANGE 110 Stepets 20-Apr-14 | ||
12 | * fix LuaJIT detection issues | ||
13 | |||
14 | CHANGE 109 BGe 03-Apr-14 | ||
15 | * moved some Lua-version compatibility code in separate source files | ||
16 | |||
3 | CHANGE 108: BGe 20-Mar-14 | 17 | CHANGE 108: BGe 20-Mar-14 |
4 | * bumped version to 3.9.4 | 18 | * bumped version to 3.9.4 |
5 | * set_finalizer throws an error if provided finalizer isn't a function | 19 | * set_finalizer throws an error if provided finalizer isn't a function |
diff --git a/src/lanes.c b/src/lanes.c index abda889..f45dac7 100644 --- a/src/lanes.c +++ b/src/lanes.c | |||
@@ -52,7 +52,7 @@ | |||
52 | * ... | 52 | * ... |
53 | */ | 53 | */ |
54 | 54 | ||
55 | char const* VERSION = "3.9.4"; | 55 | char const* VERSION = "3.9.5"; |
56 | 56 | ||
57 | /* | 57 | /* |
58 | =============================================================================== | 58 | =============================================================================== |
@@ -1074,6 +1074,24 @@ LUAG_FUNC( linda_dump) | |||
1074 | } | 1074 | } |
1075 | 1075 | ||
1076 | /* | 1076 | /* |
1077 | * table = linda:dump() | ||
1078 | * return a table listing all pending data inside the linda | ||
1079 | */ | ||
1080 | LUAG_FUNC( linda_towatch) | ||
1081 | { | ||
1082 | struct s_Linda* linda = lua_toLinda( L, 1); | ||
1083 | int pushed; | ||
1084 | ASSERT_L( linda->U == get_universe( L)); | ||
1085 | pushed = keeper_push_linda_storage( linda->U, L, linda, LINDA_KEEPER_HASHSEED( linda)); | ||
1086 | if( pushed == 0) | ||
1087 | { | ||
1088 | // if the linda is empty, don't return nil | ||
1089 | pushed = linda_tostring( L, 1, FALSE); | ||
1090 | } | ||
1091 | return pushed; | ||
1092 | } | ||
1093 | |||
1094 | /* | ||
1077 | * Identity function of a shared userdata object. | 1095 | * Identity function of a shared userdata object. |
1078 | * | 1096 | * |
1079 | * lightuserdata= linda_id( "new" [, ...] ) | 1097 | * lightuserdata= linda_id( "new" [, ...] ) |
@@ -1190,7 +1208,7 @@ static void* linda_id( lua_State* L, enum eDeepOp op_) | |||
1190 | lua_setfield( L, -2, "__tostring"); | 1208 | lua_setfield( L, -2, "__tostring"); |
1191 | 1209 | ||
1192 | // Decoda __towatch support | 1210 | // Decoda __towatch support |
1193 | lua_pushcfunction( L, LG_linda_dump); | 1211 | lua_pushcfunction( L, LG_linda_towatch); |
1194 | lua_setfield( L, -2, "__towatch"); | 1212 | lua_setfield( L, -2, "__towatch"); |
1195 | 1213 | ||
1196 | lua_pushcfunction( L, LG_linda_concat); | 1214 | lua_pushcfunction( L, LG_linda_concat); |
@@ -2027,12 +2045,6 @@ static THREAD_RETURN_T THREAD_CALLCONV lane_main( void* vs) | |||
2027 | // Called with the lane function and arguments on the stack | 2045 | // Called with the lane function and arguments on the stack |
2028 | int const nargs = lua_gettop( L) - 1; | 2046 | int const nargs = lua_gettop( L) - 1; |
2029 | DEBUGSPEW_CODE( struct s_Universe* U = get_universe( L)); | 2047 | DEBUGSPEW_CODE( struct s_Universe* U = get_universe( L)); |
2030 | #if HAVE_LANE_TRACKING | ||
2031 | if( s->U->tracking_first) | ||
2032 | { | ||
2033 | tracking_add( s); | ||
2034 | } | ||
2035 | #endif // HAVE_LANE_TRACKING | ||
2036 | THREAD_MAKE_ASYNCH_CANCELLABLE(); | 2048 | THREAD_MAKE_ASYNCH_CANCELLABLE(); |
2037 | THREAD_CLEANUP_PUSH( thread_cleanup_handler, s); | 2049 | THREAD_CLEANUP_PUSH( thread_cleanup_handler, s); |
2038 | s->status = RUNNING; // PENDING -> RUNNING | 2050 | s->status = RUNNING; // PENDING -> RUNNING |
@@ -2147,85 +2159,86 @@ LUAG_FUNC( require) | |||
2147 | LUAG_FUNC( thread_gc); | 2159 | LUAG_FUNC( thread_gc); |
2148 | #define GCCB_KEY (void*)LG_thread_gc | 2160 | #define GCCB_KEY (void*)LG_thread_gc |
2149 | //--- | 2161 | //--- |
2150 | // lane_ud= thread_new( function, [libs_str], | 2162 | // lane_ud = lane_new( function |
2151 | // [cancelstep_uint=0], | 2163 | // , [libs_str] |
2152 | // [prio_int=0], | 2164 | // , [cancelstep_uint=0] |
2153 | // [globals_tbl], | 2165 | // , [priority_int=0] |
2154 | // [package_tbl], | 2166 | // , [globals_tbl] |
2155 | // [required], | 2167 | // , [package_tbl] |
2156 | // [gc_cb], | 2168 | // , [required_tbl] |
2157 | // [... args ...] ) | 2169 | // , [gc_cb_func] |
2170 | // [, ... args ...]) | ||
2158 | // | 2171 | // |
2159 | // Upvalues: metatable to use for 'lane_ud' | 2172 | // Upvalues: metatable to use for 'lane_ud' |
2160 | // | 2173 | // |
2161 | 2174 | LUAG_FUNC( lane_new) | |
2162 | LUAG_FUNC( thread_new) | ||
2163 | { | 2175 | { |
2164 | lua_State* L2; | 2176 | lua_State* L2; |
2165 | struct s_lane* s; | 2177 | struct s_lane* s; |
2166 | struct s_lane** ud; | 2178 | struct s_lane** ud; |
2167 | 2179 | ||
2168 | char const* libs = lua_tostring( L, 2); | 2180 | char const* libs_str = lua_tostring( L, 2); |
2169 | uint_t cs = luaG_optunsigned( L, 3, 0); | 2181 | uint_t cancelstep_idx = luaG_optunsigned( L, 3, 0); |
2170 | int const prio = (int) luaL_optinteger( L, 4, 0); | 2182 | int const priority = (int) luaL_optinteger( L, 4, 0); |
2171 | uint_t glob = lua_isnoneornil( L, 5) ? 0 : 5; | 2183 | uint_t globals_idx = lua_isnoneornil( L, 5) ? 0 : 5; |
2172 | uint_t package = lua_isnoneornil( L, 6) ? 0 : 6; | 2184 | uint_t package_idx = lua_isnoneornil( L, 6) ? 0 : 6; |
2173 | uint_t required = lua_isnoneornil( L, 7) ? 0 : 7; | 2185 | uint_t required_idx = lua_isnoneornil( L, 7) ? 0 : 7; |
2174 | uint_t gc_cb = lua_isnoneornil( L, 8) ? 0 : 8; | 2186 | uint_t gc_cb_idx = lua_isnoneornil( L, 8) ? 0 : 8; |
2175 | 2187 | ||
2176 | #define FIXED_ARGS 8 | 2188 | #define FIXED_ARGS 8 |
2177 | uint_t args = lua_gettop(L) - FIXED_ARGS; | 2189 | int const nargs = lua_gettop(L) - FIXED_ARGS; |
2178 | struct s_Universe* U = get_universe( L); | 2190 | struct s_Universe* U = get_universe( L); |
2191 | ASSERT_L( nargs >= 0); | ||
2179 | 2192 | ||
2180 | // public Lanes API accepts a generic range -3/+3 | 2193 | // public Lanes API accepts a generic range -3/+3 |
2181 | // that will be remapped into the platform-specific scheduler priority scheme | 2194 | // that will be remapped into the platform-specific scheduler priority scheme |
2182 | // On some platforms, -3 is equivalent to -2 and +3 to +2 | 2195 | // On some platforms, -3 is equivalent to -2 and +3 to +2 |
2183 | if( prio < THREAD_PRIO_MIN || prio > THREAD_PRIO_MAX) | 2196 | if( priority < THREAD_PRIO_MIN || priority > THREAD_PRIO_MAX) |
2184 | { | 2197 | { |
2185 | return luaL_error( L, "Priority out of range: %d..+%d (%d)", THREAD_PRIO_MIN, THREAD_PRIO_MAX, prio); | 2198 | return luaL_error( L, "Priority out of range: %d..+%d (%d)", THREAD_PRIO_MIN, THREAD_PRIO_MAX, priority); |
2186 | } | 2199 | } |
2187 | 2200 | ||
2188 | /* --- Create and prepare the sub state --- */ | 2201 | /* --- Create and prepare the sub state --- */ |
2189 | DEBUGSPEW_CODE( fprintf( stderr, INDENT_BEGIN "thread_new: setup\n" INDENT_END)); | 2202 | DEBUGSPEW_CODE( fprintf( stderr, INDENT_BEGIN "lane_new: setup\n" INDENT_END)); |
2190 | DEBUGSPEW_CODE( ++ U->debugspew_indent_depth); | 2203 | DEBUGSPEW_CODE( ++ U->debugspew_indent_depth); |
2191 | 2204 | ||
2192 | // populate with selected libraries at the same time | 2205 | // populate with selected libraries at the same time |
2193 | L2 = luaG_newstate( U, L, libs); | 2206 | L2 = luaG_newstate( U, L, libs_str); // L // L2 |
2194 | 2207 | ||
2195 | STACK_GROW( L, 2); | 2208 | STACK_GROW( L2, nargs + 3); // |
2196 | STACK_GROW( L2, 3); | 2209 | STACK_CHECK( L2); |
2197 | 2210 | ||
2198 | // give a default "Lua" name to the thread to see VM name in Decoda debugger | 2211 | STACK_GROW( L, 3); // func libs cancelstep priority globals package required gc_cb [... args ...] |
2199 | lua_pushfstring( L2, "Lane #%p", L2); | 2212 | STACK_CHECK( L); |
2200 | lua_setglobal( L2, "decoda_name"); | ||
2201 | 2213 | ||
2202 | ASSERT_L( lua_gettop(L2) == 0); | 2214 | // give a default "Lua" name to the thread to see VM name in Decoda debugger |
2215 | lua_pushfstring( L2, "Lane #%p", L2); // "..." | ||
2216 | lua_setglobal( L2, "decoda_name"); // | ||
2217 | ASSERT_L( lua_gettop( L2) == 0); | ||
2203 | 2218 | ||
2204 | DEBUGSPEW_CODE( fprintf( stderr, INDENT_BEGIN "thread_new: update 'package'\n" INDENT_END)); | 2219 | DEBUGSPEW_CODE( fprintf( stderr, INDENT_BEGIN "lane_new: update 'package'\n" INDENT_END)); |
2205 | // package | 2220 | // package |
2206 | if( package != 0) | 2221 | if( package_idx != 0) |
2207 | { | 2222 | { |
2208 | // when copying with mode eLM_LaneBody, should raise an error in case of problem, not leave it one the stack | 2223 | // when copying with mode eLM_LaneBody, should raise an error in case of problem, not leave it one the stack |
2209 | (void) luaG_inter_copy_package( U, L, L2, package, eLM_LaneBody); | 2224 | (void) luaG_inter_copy_package( U, L, L2, package_idx, eLM_LaneBody); |
2210 | } | 2225 | } |
2211 | 2226 | ||
2212 | // modules to require in the target lane *before* the function is transfered! | 2227 | // modules to require in the target lane *before* the function is transfered! |
2213 | 2228 | ||
2214 | STACK_CHECK( L); | 2229 | if( required_idx != 0) |
2215 | STACK_CHECK( L2); | ||
2216 | if( required != 0) | ||
2217 | { | 2230 | { |
2218 | int nbRequired = 1; | 2231 | int nbRequired = 1; |
2219 | DEBUGSPEW_CODE( fprintf( stderr, INDENT_BEGIN "thread_new: require 'required' list\n" INDENT_END)); | 2232 | DEBUGSPEW_CODE( fprintf( stderr, INDENT_BEGIN "lane_new: require 'required' list\n" INDENT_END)); |
2220 | DEBUGSPEW_CODE( ++ U->debugspew_indent_depth); | 2233 | DEBUGSPEW_CODE( ++ U->debugspew_indent_depth); |
2221 | // should not happen, was checked in lanes.lua before calling thread_new() | 2234 | // should not happen, was checked in lanes.lua before calling lane_new() |
2222 | if( lua_type( L, required) != LUA_TTABLE) | 2235 | if( lua_type( L, required_idx) != LUA_TTABLE) |
2223 | { | 2236 | { |
2224 | return luaL_error( L, "expected required module list as a table, got %s", luaL_typename( L, required)); | 2237 | return luaL_error( L, "expected required module list as a table, got %s", luaL_typename( L, required_idx)); |
2225 | } | 2238 | } |
2226 | 2239 | ||
2227 | lua_pushnil( L); | 2240 | lua_pushnil( L); // func libs cancelstep priority globals package required gc_cb [... args ...] nil |
2228 | while( lua_next( L, required) != 0) | 2241 | while( lua_next( L, required_idx) != 0) // func libs cancelstep priority globals package required gc_cb [... args ...] n "modname" |
2229 | { | 2242 | { |
2230 | if( lua_type( L, -1) != LUA_TSTRING || lua_type( L, -2) != LUA_TNUMBER || lua_tonumber( L, -2) != nbRequired) | 2243 | if( lua_type( L, -1) != LUA_TSTRING || lua_type( L, -2) != LUA_TNUMBER || lua_tonumber( L, -2) != nbRequired) |
2231 | { | 2244 | { |
@@ -2238,12 +2251,10 @@ LUAG_FUNC( thread_new) | |||
2238 | char const* name = lua_tolstring( L, -1, &len); | 2251 | char const* name = lua_tolstring( L, -1, &len); |
2239 | 2252 | ||
2240 | // require the module in the target lane | 2253 | // require the module in the target lane |
2241 | STACK_GROW( L2, 2); | 2254 | lua_getglobal( L2, "require"); // require()? |
2242 | STACK_CHECK( L2); | ||
2243 | lua_getglobal( L2, "require"); // require()? | ||
2244 | if( lua_isnil( L2, -1)) | 2255 | if( lua_isnil( L2, -1)) |
2245 | { | 2256 | { |
2246 | lua_pop( L2, 1); // | 2257 | lua_pop( L2, 1); // |
2247 | luaL_error( L, "cannot pre-require modules without loading 'package' library first"); | 2258 | luaL_error( L, "cannot pre-require modules without loading 'package' library first"); |
2248 | } | 2259 | } |
2249 | else | 2260 | else |
@@ -2254,113 +2265,102 @@ LUAG_FUNC( thread_new) | |||
2254 | { | 2265 | { |
2255 | luaG_copy_one_time_settings( U, L, L2); | 2266 | luaG_copy_one_time_settings( U, L, L2); |
2256 | } | 2267 | } |
2257 | lua_pushlstring( L2, name, len); // require() name | 2268 | lua_pushlstring( L2, name, len); // require() name |
2258 | if( lua_pcall( L2, 1, 1, 0) != LUA_OK) // ret/errcode | 2269 | if( lua_pcall( L2, 1, 1, 0) != LUA_OK) // ret/errcode |
2259 | { | 2270 | { |
2260 | // propagate error to main state if any | 2271 | // propagate error to main state if any |
2261 | luaG_inter_move( U, L2, L, 1, eLM_LaneBody); // | 2272 | luaG_inter_move( U, L2, L, 1, eLM_LaneBody); // func libs cancelstep priority globals package required gc_cb [... args ...] n "modname" error |
2262 | return lua_error( L); | 2273 | return lua_error( L); |
2263 | } | 2274 | } |
2264 | STACK_MID( L2, 1); | ||
2265 | // after requiring the module, register the functions it exported in our name<->function database | 2275 | // after requiring the module, register the functions it exported in our name<->function database |
2266 | populate_func_lookup_table( L2, -1, name); | 2276 | populate_func_lookup_table( L2, -1, name); |
2267 | STACK_MID( L2, 1); | 2277 | lua_pop( L2, 1); // |
2268 | lua_pop( L2, 1); | ||
2269 | } | 2278 | } |
2270 | STACK_END( L2, 0); | ||
2271 | } | 2279 | } |
2272 | lua_pop( L, 1); | 2280 | lua_pop( L, 1); // func libs cancelstep priority globals package required gc_cb [... args ...] n |
2273 | ++ nbRequired; | 2281 | ++ nbRequired; |
2274 | } | 2282 | } // func libs cancelstep priority globals package required gc_cb [... args ...] |
2275 | DEBUGSPEW_CODE( -- U->debugspew_indent_depth); | 2283 | DEBUGSPEW_CODE( -- U->debugspew_indent_depth); |
2276 | } | 2284 | } |
2277 | STACK_END( L2, 0); | 2285 | STACK_MID( L, 0); |
2278 | STACK_END( L, 0); | 2286 | STACK_MID( L2, 0); // |
2279 | 2287 | ||
2280 | // Appending the specified globals to the global environment | 2288 | // Appending the specified globals to the global environment |
2281 | // *after* stdlibs have been loaded and modules required, in case we transfer references to native functions they exposed... | 2289 | // *after* stdlibs have been loaded and modules required, in case we transfer references to native functions they exposed... |
2282 | // | 2290 | // |
2283 | if( glob != 0) | 2291 | if( globals_idx != 0) |
2284 | { | 2292 | { |
2285 | DEBUGSPEW_CODE( fprintf( stderr, INDENT_BEGIN "thread_new: transfer globals\n" INDENT_END)); | 2293 | DEBUGSPEW_CODE( fprintf( stderr, INDENT_BEGIN "lane_new: transfer globals\n" INDENT_END)); |
2286 | STACK_CHECK( L); | 2294 | if( !lua_istable( L, globals_idx)) |
2287 | STACK_CHECK( L2); | ||
2288 | if( !lua_istable( L, glob)) | ||
2289 | { | 2295 | { |
2290 | return luaL_error( L, "Expected table, got %s", luaL_typename( L, glob)); | 2296 | return luaL_error( L, "Expected table, got %s", luaL_typename( L, globals_idx)); |
2291 | } | 2297 | } |
2292 | 2298 | ||
2293 | DEBUGSPEW_CODE( ++ U->debugspew_indent_depth); | 2299 | DEBUGSPEW_CODE( ++ U->debugspew_indent_depth); |
2294 | lua_pushnil( L); | 2300 | lua_pushnil( L); // func libs cancelstep priority globals package required gc_cb [... args ...] nil |
2295 | lua_pushglobaltable( L2); // Lua 5.2 wants us to push the globals table on the stack | 2301 | // Lua 5.2 wants us to push the globals table on the stack |
2296 | while( lua_next( L, glob)) | 2302 | lua_pushglobaltable( L2); // _G |
2303 | while( lua_next( L, globals_idx)) // func libs cancelstep priority globals package required gc_cb [... args ...] k v | ||
2297 | { | 2304 | { |
2298 | luaG_inter_copy( U, L, L2, 2, eLM_LaneBody); // moves the key/value pair to the L2 stack | 2305 | luaG_inter_copy( U, L, L2, 2, eLM_LaneBody); // _G k v |
2299 | // assign it in L2's globals table | 2306 | // assign it in L2's globals table |
2300 | lua_rawset( L2, -3); | 2307 | lua_rawset( L2, -3); // _G |
2301 | lua_pop( L, 1); | 2308 | lua_pop( L, 1); // func libs cancelstep priority globals package required gc_cb [... args ...] k |
2302 | } | 2309 | } // func libs cancelstep priority globals package required gc_cb [... args ...] |
2303 | lua_pop( L2, 1); | 2310 | lua_pop( L2, 1); // |
2304 | 2311 | ||
2305 | STACK_END( L2, 0); | ||
2306 | STACK_END( L, 0); | ||
2307 | DEBUGSPEW_CODE( -- U->debugspew_indent_depth); | 2312 | DEBUGSPEW_CODE( -- U->debugspew_indent_depth); |
2308 | } | 2313 | } |
2309 | 2314 | STACK_MID( L, 0); | |
2310 | 2315 | STACK_MID( L2, 0); | |
2311 | STACK_CHECK( L); | ||
2312 | STACK_CHECK( L2); | ||
2313 | ASSERT_L( lua_gettop( L2) == 0); | ||
2314 | 2316 | ||
2315 | // Lane main function | 2317 | // Lane main function |
2316 | if( lua_type( L, 1) == LUA_TFUNCTION) | 2318 | if( lua_type( L, 1) == LUA_TFUNCTION) |
2317 | { | 2319 | { |
2318 | int res; | 2320 | int res; |
2319 | DEBUGSPEW_CODE( fprintf( stderr, INDENT_BEGIN "thread_new: transfer lane body\n" INDENT_END)); | 2321 | DEBUGSPEW_CODE( fprintf( stderr, INDENT_BEGIN "lane_new: transfer lane body\n" INDENT_END)); |
2320 | DEBUGSPEW_CODE( ++ U->debugspew_indent_depth); | 2322 | DEBUGSPEW_CODE( ++ U->debugspew_indent_depth); |
2321 | lua_pushvalue( L, 1); | 2323 | lua_pushvalue( L, 1); // func libs cancelstep priority globals package required gc_cb [... args ...] func |
2322 | res = luaG_inter_move( U, L, L2, 1, eLM_LaneBody); // L->L2 | 2324 | res = luaG_inter_move( U, L, L2, 1, eLM_LaneBody); // func libs cancelstep priority globals package required gc_cb [... args ...] // func |
2323 | DEBUGSPEW_CODE( -- U->debugspew_indent_depth); | 2325 | DEBUGSPEW_CODE( -- U->debugspew_indent_depth); |
2324 | if( res != 0) | 2326 | if( res != 0) |
2325 | { | 2327 | { |
2326 | return luaL_error( L, "tried to copy unsupported types"); | 2328 | return luaL_error( L, "tried to copy unsupported types"); |
2327 | } | 2329 | } |
2328 | STACK_MID( L, 0); | ||
2329 | } | 2330 | } |
2330 | else if( lua_type( L, 1) == LUA_TSTRING) | 2331 | else if( lua_type( L, 1) == LUA_TSTRING) |
2331 | { | 2332 | { |
2332 | // compile the string | 2333 | // compile the string |
2333 | if( luaL_loadstring( L2, lua_tostring( L, 1)) != 0) | 2334 | if( luaL_loadstring( L2, lua_tostring( L, 1)) != 0) // func |
2334 | { | 2335 | { |
2335 | return luaL_error( L, "error when parsing lane function code"); | 2336 | return luaL_error( L, "error when parsing lane function code"); |
2336 | } | 2337 | } |
2337 | } | 2338 | } |
2338 | 2339 | STACK_MID( L, 0); | |
2339 | STACK_MID( L2, 1); | 2340 | STACK_MID( L2, 1); |
2340 | ASSERT_L( lua_isfunction( L2, 1)); | 2341 | ASSERT_L( lua_isfunction( L2, 1)); |
2341 | 2342 | ||
2342 | // revive arguments | 2343 | // revive arguments |
2343 | // | 2344 | if( nargs > 0) |
2344 | if( args > 0) | ||
2345 | { | 2345 | { |
2346 | int res; | 2346 | int res; |
2347 | DEBUGSPEW_CODE( fprintf( stderr, INDENT_BEGIN "thread_new: transfer lane arguments\n" INDENT_END)); | 2347 | DEBUGSPEW_CODE( fprintf( stderr, INDENT_BEGIN "lane_new: transfer lane arguments\n" INDENT_END)); |
2348 | DEBUGSPEW_CODE( ++ U->debugspew_indent_depth); | 2348 | DEBUGSPEW_CODE( ++ U->debugspew_indent_depth); |
2349 | res = luaG_inter_copy( U, L, L2, args, eLM_LaneBody); // L->L2 | 2349 | res = luaG_inter_move( U, L, L2, nargs, eLM_LaneBody); // func libs cancelstep priority globals package required gc_cb // func [... args ...] |
2350 | DEBUGSPEW_CODE( -- U->debugspew_indent_depth); | 2350 | DEBUGSPEW_CODE( -- U->debugspew_indent_depth); |
2351 | if( res != 0) | 2351 | if( res != 0) |
2352 | { | 2352 | { |
2353 | return luaL_error( L, "tried to copy unsupported types"); | 2353 | return luaL_error( L, "tried to copy unsupported types"); |
2354 | } | 2354 | } |
2355 | } | 2355 | } |
2356 | STACK_MID( L, 0); | 2356 | STACK_END( L, -nargs); |
2357 | 2357 | ASSERT_L( lua_gettop( L) == FIXED_ARGS); | |
2358 | STACK_END( L2, 1 + args); | 2358 | STACK_CHECK( L); |
2359 | STACK_MID( L2, 1 + nargs); | ||
2359 | 2360 | ||
2360 | // 's' is allocated from heap, not Lua, since its life span may surpass | 2361 | // 's' is allocated from heap, not Lua, since its life span may surpass the handle's (if free running thread) |
2361 | // the handle's (if free running thread) | ||
2362 | // | 2362 | // |
2363 | ud = lua_newuserdata( L, sizeof( struct s_lane*)); | 2363 | ud = lua_newuserdata( L, sizeof( struct s_lane*)); // func libs cancelstep priority globals package required gc_cb lane |
2364 | s = *ud = (struct s_lane*) malloc( sizeof( struct s_lane)); | 2364 | s = *ud = (struct s_lane*) malloc( sizeof( struct s_lane)); |
2365 | if( s == NULL) | 2365 | if( s == NULL) |
2366 | { | 2366 | { |
@@ -2382,44 +2382,49 @@ LUAG_FUNC( thread_new) | |||
2382 | s->selfdestruct_next = NULL; | 2382 | s->selfdestruct_next = NULL; |
2383 | #if HAVE_LANE_TRACKING | 2383 | #if HAVE_LANE_TRACKING |
2384 | s->tracking_next = NULL; | 2384 | s->tracking_next = NULL; |
2385 | if( s->U->tracking_first) | ||
2386 | { | ||
2387 | tracking_add( s); | ||
2388 | } | ||
2385 | #endif // HAVE_LANE_TRACKING | 2389 | #endif // HAVE_LANE_TRACKING |
2386 | 2390 | ||
2387 | // Set metatable for the userdata | 2391 | // Set metatable for the userdata |
2388 | // | 2392 | // |
2389 | lua_pushvalue( L, lua_upvalueindex( 1)); | 2393 | lua_pushvalue( L, lua_upvalueindex( 1)); // func libs cancelstep priority globals package required gc_cb lane mt |
2390 | lua_setmetatable( L, -2); | 2394 | lua_setmetatable( L, -2); // func libs cancelstep priority globals package required gc_cb lane |
2391 | STACK_MID( L, 1); | 2395 | STACK_MID( L, 1); |
2392 | 2396 | ||
2393 | // Create uservalue for the userdata | 2397 | // Create uservalue for the userdata |
2394 | // (this is where lane body return values will be stored when the handle is indexed by a numeric key) | 2398 | // (this is where lane body return values will be stored when the handle is indexed by a numeric key) |
2395 | lua_newtable( L); | 2399 | lua_newtable( L); // func libs cancelstep priority globals package required gc_cb lane uv |
2396 | 2400 | ||
2397 | // Store the gc_cb callback in the uservalue | 2401 | // Store the gc_cb callback in the uservalue |
2398 | if( gc_cb > 0) | 2402 | if( gc_cb_idx > 0) |
2399 | { | 2403 | { |
2400 | lua_pushlightuserdata( L, GCCB_KEY); | 2404 | lua_pushlightuserdata( L, GCCB_KEY); // func libs cancelstep priority globals package required gc_cb lane uv k |
2401 | lua_pushvalue( L, gc_cb); | 2405 | lua_pushvalue( L, gc_cb_idx); // func libs cancelstep priority globals package required gc_cb lane uv k gc_cb |
2402 | lua_rawset( L, -3); | 2406 | lua_rawset( L, -3); // func libs cancelstep priority globals package required gc_cb lane uv |
2403 | } | 2407 | } |
2404 | 2408 | ||
2405 | lua_setuservalue( L, -2); | 2409 | lua_setuservalue( L, -2); // func libs cancelstep priority globals package required gc_cb lane |
2406 | 2410 | ||
2407 | // Store 's' in the lane's registry, for 'cancel_test()' (even if 'cs'==0 we still do cancel tests at pending send/receive). | 2411 | // Store 's' in the lane's registry, for 'cancel_test()' (even if 'cs'==0 we still do cancel tests at pending send/receive). |
2408 | lua_pushlightuserdata( L2, CANCEL_TEST_KEY); | 2412 | lua_pushlightuserdata( L2, CANCEL_TEST_KEY); // func [... args ...] k |
2409 | lua_pushlightuserdata( L2, s); | 2413 | lua_pushlightuserdata( L2, s); // func [... args ...] k s |
2410 | lua_rawset( L2, LUA_REGISTRYINDEX); | 2414 | lua_rawset( L2, LUA_REGISTRYINDEX); // func [... args ...] |
2411 | 2415 | ||
2412 | if( cs) | 2416 | if( cancelstep_idx) |
2413 | { | 2417 | { |
2414 | lua_sethook( L2, cancel_hook, LUA_MASKCOUNT, cs); | 2418 | lua_sethook( L2, cancel_hook, LUA_MASKCOUNT, cancelstep_idx); |
2415 | } | 2419 | } |
2416 | 2420 | ||
2417 | DEBUGSPEW_CODE( fprintf( stderr, INDENT_BEGIN "thread_new: launching thread\n" INDENT_END)); | 2421 | DEBUGSPEW_CODE( fprintf( stderr, INDENT_BEGIN "lane_new: launching thread\n" INDENT_END)); |
2418 | THREAD_CREATE( &s->thread, lane_main, s, prio); | 2422 | THREAD_CREATE( &s->thread, lane_main, s, priority); |
2419 | STACK_END( L, 1); | ||
2420 | 2423 | ||
2421 | DEBUGSPEW_CODE( -- U->debugspew_indent_depth); | 2424 | DEBUGSPEW_CODE( -- U->debugspew_indent_depth); |
2422 | 2425 | ||
2426 | STACK_END( L, 1); | ||
2427 | STACK_END( L2, 1 + nargs); | ||
2423 | return 1; | 2428 | return 1; |
2424 | } | 2429 | } |
2425 | 2430 | ||
@@ -3161,8 +3166,8 @@ LUAG_FUNC( configure) | |||
3161 | lua_setfield( L, -2, "__metatable"); // settings M mt | 3166 | lua_setfield( L, -2, "__metatable"); // settings M mt |
3162 | } | 3167 | } |
3163 | 3168 | ||
3164 | lua_pushcclosure( L, LG_thread_new, 1); // settings M LG_thread_new | 3169 | lua_pushcclosure( L, LG_lane_new, 1); // settings M lane_new |
3165 | lua_setfield( L, -2, "thread_new"); // settings M | 3170 | lua_setfield( L, -2, "lane_new"); // settings M |
3166 | 3171 | ||
3167 | // we can't register 'lanes.require' normally because we want to create an upvalued closure | 3172 | // we can't register 'lanes.require' normally because we want to create an upvalued closure |
3168 | lua_getglobal( L, "require"); // settings M require | 3173 | lua_getglobal( L, "require"); // settings M require |
diff --git a/src/lanes.lua b/src/lanes.lua index 5dfe41d..57aa0fe 100644 --- a/src/lanes.lua +++ b/src/lanes.lua | |||
@@ -56,8 +56,9 @@ lanes.configure = function( settings_) | |||
56 | -- | 56 | -- |
57 | -- Cache globals for code that might run under sandboxing | 57 | -- Cache globals for code that might run under sandboxing |
58 | -- | 58 | -- |
59 | local assert = assert | 59 | local assert = assert( assert) |
60 | local string_gmatch = assert( string.gmatch) | 60 | local string_gmatch = assert( string.gmatch) |
61 | local string_format = assert( string.format) | ||
61 | local select = assert( select) | 62 | local select = assert( select) |
62 | local type = assert( type) | 63 | local type = assert( type) |
63 | local pairs = assert( pairs) | 64 | local pairs = assert( pairs) |
@@ -127,7 +128,7 @@ lanes.configure = function( settings_) | |||
127 | return settings | 128 | return settings |
128 | end | 129 | end |
129 | local settings = core.configure and core.configure( params_checker( settings_)) or core.settings | 130 | local settings = core.configure and core.configure( params_checker( settings_)) or core.settings |
130 | local thread_new = assert( core.thread_new) | 131 | local core_lane_new = assert( core.lane_new) |
131 | local max_prio = assert( core.max_prio) | 132 | local max_prio = assert( core.max_prio) |
132 | 133 | ||
133 | lanes.ABOUT = | 134 | lanes.ABOUT = |
@@ -203,11 +204,9 @@ end | |||
203 | -- ... (more options may be introduced later) ... | 204 | -- ... (more options may be introduced later) ... |
204 | -- | 205 | -- |
205 | -- Calling with a function parameter ('lane_func') ends the string/table | 206 | -- Calling with a function parameter ('lane_func') ends the string/table |
206 | -- modifiers, and prepares a lane generator. One can either finish here, | 207 | -- modifiers, and prepares a lane generator. |
207 | -- and call the generator later (maybe multiple times, with different parameters) | ||
208 | -- or add on actual thread arguments to also ignite the thread on the same call. | ||
209 | 208 | ||
210 | local valid_libs= | 209 | local valid_libs = |
211 | { | 210 | { |
212 | ["package"] = true, | 211 | ["package"] = true, |
213 | ["table"] = true, | 212 | ["table"] = true, |
@@ -223,88 +222,110 @@ local valid_libs= | |||
223 | ["lanes.core"] = true | 222 | ["lanes.core"] = true |
224 | } | 223 | } |
225 | 224 | ||
225 | local raise_option_error = function( name_, tv_, v_) | ||
226 | error( "Bad '" .. name_ .. "' option: " .. tv_ .. " " .. string_format( "%q", tostring( v_)), 4) | ||
227 | end | ||
228 | |||
229 | local opt_validators = | ||
230 | { | ||
231 | priority = function( v_) | ||
232 | local tv = type( v_) | ||
233 | return (tv == "number") and v_ or raise_option_error( "priority", tv, v_) | ||
234 | end, | ||
235 | cancelstep = function( v_) | ||
236 | local tv = type( v_) | ||
237 | return (tv == "number") and v_ or (v_ == true) and 100 or (v_ == false) and 0 or raise_option_error( "cancelstep", tv, v_) | ||
238 | end, | ||
239 | globals = function( v_) | ||
240 | local tv = type( v_) | ||
241 | return (tv == "table") and v_ or raise_option_error( "globals", tv, v_) | ||
242 | end, | ||
243 | package = function( v_) | ||
244 | local tv = type( v_) | ||
245 | return (tv == "table") and v_ or raise_option_error( "package", tv, v_) | ||
246 | end, | ||
247 | required = function( v_) | ||
248 | local tv = type( v_) | ||
249 | return (tv == "table") and v_ or raise_option_error( "required", tv, v_) | ||
250 | end, | ||
251 | gc_cb = function( v_) | ||
252 | local tv = type( v_) | ||
253 | return (tv == "function") and v_ or raise_option_error( "gc_cb", tv, v_) | ||
254 | end | ||
255 | } | ||
256 | |||
226 | -- PUBLIC LANES API | 257 | -- PUBLIC LANES API |
227 | local function gen( ... ) | 258 | -- receives a sequence of strings and tables, plus a function |
228 | local opt= {} | 259 | local gen = function( ...) |
229 | local libs= nil | 260 | -- aggregrate all strings together, separated by "," as well as tables |
230 | local lev= 2 -- level for errors | 261 | -- the strings are a list of libraries to open |
231 | 262 | -- the tables contain the lane options | |
232 | local n= select('#',...) | 263 | local opt = {} |
233 | 264 | local libs = nil | |
234 | if n==0 then | 265 | |
235 | error( "No parameters!" ) | 266 | local n = select( '#', ...) |
236 | end | 267 | |
268 | -- we need at least a function | ||
269 | if n == 0 then | ||
270 | error( "No parameters!", 2) | ||
271 | end | ||
237 | 272 | ||
238 | for i=1,n-1 do | 273 | -- all arguments but the last must be nil, strings, or tables |
239 | local v= select(i,...) | 274 | for i = 1, n - 1 do |
240 | if type(v)=="string" then | 275 | local v = select( i, ...) |
241 | libs= libs and libs..","..v or v | 276 | local tv = type( v) |
242 | elseif type(v)=="table" then | 277 | if tv == "string" then |
243 | for k,vv in pairs(v) do | 278 | libs = libs and libs .. "," .. v or v |
244 | opt[k]= vv | 279 | elseif tv == "table" then |
245 | end | 280 | for k, vv in pairs( v) do |
246 | elseif v==nil then | 281 | opt[k]= vv |
247 | -- skip | 282 | end |
248 | else | 283 | elseif v == nil then |
249 | error( "Bad parameter: "..tostring(v) ) | 284 | -- skip |
250 | end | 285 | else |
251 | end | 286 | error( "Bad parameter " .. i .. ": " .. tv .. " " .. string_format( "%q", tostring( v)), 2) |
287 | end | ||
288 | end | ||
252 | 289 | ||
253 | local func= select(n,...) | 290 | -- the last argument should be a function or a string |
254 | local functype = type(func) | 291 | local func = select( n, ...) |
255 | if functype ~= "function" and functype ~= "string" then | 292 | local functype = type( func) |
256 | error( "Last parameter not function or string: "..tostring(func)) | 293 | if functype ~= "function" and functype ~= "string" then |
257 | end | 294 | error( "Last parameter not function or string: " .. functype .. " " .. string_format( "%q", tostring( func)), 2) |
295 | end | ||
258 | 296 | ||
259 | -- Check 'libs' already here, so the error goes in the right place | 297 | -- check that the caller only provides reserved library names, and those only once |
260 | -- (otherwise will be noticed only once the generator is called) | 298 | -- "*" is a special case that doesn't require individual checking |
261 | -- "*" is a special case that doesn't require individual checking | 299 | if libs and libs ~= "*" then |
262 | -- | 300 | local found = {} |
263 | if libs and libs ~= "*" then | 301 | for s in string_gmatch(libs, "[%a%d.]+") do |
264 | local found = {} | 302 | if not valid_libs[s] then |
265 | -- check that the caller only provides reserved library names | 303 | error( "Bad library name: " .. s, 2) |
266 | for s in string_gmatch(libs, "[%a%d.]+") do | 304 | else |
267 | if not valid_libs[s] then | 305 | found[s] = (found[s] or 0) + 1 |
268 | error( "Bad library name: " .. s) | 306 | if found[s] > 1 then |
269 | else | 307 | error( "libs specification contains '" .. s .. "' more than once", 2) |
270 | found[s] = (found[s] or 0) + 1 | 308 | end |
271 | if found[s] > 1 then | 309 | end |
272 | error( "libs specification contains '" .. s .. "' more than once") | 310 | end |
273 | end | 311 | end |
274 | end | ||
275 | end | ||
276 | end | ||
277 | |||
278 | local prio, cs, g_tbl, package_tbl, required, gc_cb | ||
279 | |||
280 | for k,v in pairs(opt) do | ||
281 | if k == "priority" then | ||
282 | prio = (type( v) == "number") and v or error( "Bad 'prio' option: expecting number, got " .. type( v), lev) | ||
283 | elseif k=="cancelstep" then | ||
284 | cs = (v==true) and 100 or | ||
285 | (v==false) and 0 or | ||
286 | type(v)=="number" and v or | ||
287 | error( "Bad cancelstep: "..tostring(v), lev ) | ||
288 | elseif k=="globals" then g_tbl= v | ||
289 | elseif k=="package" then | ||
290 | package_tbl = (type( v) == "table") and v or error( "Bad package: " .. tostring( v), lev) | ||
291 | elseif k=="required" then | ||
292 | required= (type( v) == "table") and v or error( "Bad 'required' option: expecting table, got " .. type( v), lev) | ||
293 | elseif k == "gc_cb" then | ||
294 | gc_cb = (type( v) == "function") and v or error( "Bad 'gc_cb' option: expecting function, got " .. type( v), lev) | ||
295 | --.. | ||
296 | elseif k==1 then error( "unkeyed option: ".. tostring(v), lev ) | ||
297 | else error( "Bad option: ".. tostring(k), lev ) | ||
298 | end | ||
299 | end | ||
300 | 312 | ||
301 | if not package_tbl then package_tbl = package end | 313 | -- validate that each option is known and properly valued |
302 | -- Lane generator | 314 | for k, v in pairs( opt) do |
303 | -- | 315 | local validator = opt_validators[k] |
304 | return function(...) | 316 | if not validator then |
305 | return thread_new( func, libs, cs, prio, g_tbl, package_tbl, required, gc_cb, ...) -- args | 317 | error( (type( k) == "number" and "Unkeyed option: " .. type( v) .. " " .. string_format( "%q", tostring( v)) or "Bad '" .. tostring( k) .. "' option"), 2) |
306 | end | 318 | else |
307 | end | 319 | opt[k] = validator( v) |
320 | end | ||
321 | end | ||
322 | |||
323 | local cancelstep, priority, globals, package, required, gc_cb = opt.cancelstep, opt.priority, opt.globals, opt.package or package, opt.required, opt.gc_cb | ||
324 | return function( ...) | ||
325 | -- must pass functions args last else they will be truncated to the first one | ||
326 | return core_lane_new( func, libs, cancelstep, priority, globals, package, required, gc_cb, ...) | ||
327 | end | ||
328 | end -- gen() | ||
308 | 329 | ||
309 | ---=== Timers ===--- | 330 | ---=== Timers ===--- |
310 | 331 | ||