From 55e53f8a24ce42cadfd2887e50bf0248eb10d301 Mon Sep 17 00:00:00 2001 From: Benoit Germain Date: Fri, 16 May 2014 11:22:39 +0200 Subject: 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 --- CHANGES | 14 ++++ src/lanes.c | 241 ++++++++++++++++++++++++++++++---------------------------- src/lanes.lua | 187 +++++++++++++++++++++++++-------------------- 3 files changed, 241 insertions(+), 201 deletions(-) diff --git a/CHANGES b/CHANGES index 3a7ff89..31599c4 100644 --- a/CHANGES +++ b/CHANGES @@ -1,5 +1,19 @@ CHANGES: +CHANGE 112 BGe 16-May-14 + * bumped version to 3.9.5 + * fix linda.__towatch to return non-nil when the linda is empty + * lanes.gen() error reporting improvements + +CHANGE 111 BGe 24-Apr-14 + * fixed linda:send() possibly returning an undefined value + +CHANGE 110 Stepets 20-Apr-14 + * fix LuaJIT detection issues + +CHANGE 109 BGe 03-Apr-14 + * moved some Lua-version compatibility code in separate source files + CHANGE 108: BGe 20-Mar-14 * bumped version to 3.9.4 * 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 @@ * ... */ -char const* VERSION = "3.9.4"; +char const* VERSION = "3.9.5"; /* =============================================================================== @@ -1073,6 +1073,24 @@ LUAG_FUNC( linda_dump) return keeper_push_linda_storage( linda->U, L, linda, LINDA_KEEPER_HASHSEED( linda)); } +/* + * table = linda:dump() + * return a table listing all pending data inside the linda + */ +LUAG_FUNC( linda_towatch) +{ + struct s_Linda* linda = lua_toLinda( L, 1); + int pushed; + ASSERT_L( linda->U == get_universe( L)); + pushed = keeper_push_linda_storage( linda->U, L, linda, LINDA_KEEPER_HASHSEED( linda)); + if( pushed == 0) + { + // if the linda is empty, don't return nil + pushed = linda_tostring( L, 1, FALSE); + } + return pushed; +} + /* * Identity function of a shared userdata object. * @@ -1190,7 +1208,7 @@ static void* linda_id( lua_State* L, enum eDeepOp op_) lua_setfield( L, -2, "__tostring"); // Decoda __towatch support - lua_pushcfunction( L, LG_linda_dump); + lua_pushcfunction( L, LG_linda_towatch); lua_setfield( L, -2, "__towatch"); lua_pushcfunction( L, LG_linda_concat); @@ -2027,12 +2045,6 @@ static THREAD_RETURN_T THREAD_CALLCONV lane_main( void* vs) // Called with the lane function and arguments on the stack int const nargs = lua_gettop( L) - 1; DEBUGSPEW_CODE( struct s_Universe* U = get_universe( L)); -#if HAVE_LANE_TRACKING - if( s->U->tracking_first) - { - tracking_add( s); - } -#endif // HAVE_LANE_TRACKING THREAD_MAKE_ASYNCH_CANCELLABLE(); THREAD_CLEANUP_PUSH( thread_cleanup_handler, s); s->status = RUNNING; // PENDING -> RUNNING @@ -2147,85 +2159,86 @@ LUAG_FUNC( require) LUAG_FUNC( thread_gc); #define GCCB_KEY (void*)LG_thread_gc //--- -// lane_ud= thread_new( function, [libs_str], -// [cancelstep_uint=0], -// [prio_int=0], -// [globals_tbl], -// [package_tbl], -// [required], -// [gc_cb], -// [... args ...] ) +// lane_ud = lane_new( function +// , [libs_str] +// , [cancelstep_uint=0] +// , [priority_int=0] +// , [globals_tbl] +// , [package_tbl] +// , [required_tbl] +// , [gc_cb_func] +// [, ... args ...]) // // Upvalues: metatable to use for 'lane_ud' // - -LUAG_FUNC( thread_new) +LUAG_FUNC( lane_new) { lua_State* L2; struct s_lane* s; struct s_lane** ud; - char const* libs = lua_tostring( L, 2); - uint_t cs = luaG_optunsigned( L, 3, 0); - int const prio = (int) luaL_optinteger( L, 4, 0); - uint_t glob = lua_isnoneornil( L, 5) ? 0 : 5; - uint_t package = lua_isnoneornil( L, 6) ? 0 : 6; - uint_t required = lua_isnoneornil( L, 7) ? 0 : 7; - uint_t gc_cb = lua_isnoneornil( L, 8) ? 0 : 8; + char const* libs_str = lua_tostring( L, 2); + uint_t cancelstep_idx = luaG_optunsigned( L, 3, 0); + int const priority = (int) luaL_optinteger( L, 4, 0); + uint_t globals_idx = lua_isnoneornil( L, 5) ? 0 : 5; + uint_t package_idx = lua_isnoneornil( L, 6) ? 0 : 6; + uint_t required_idx = lua_isnoneornil( L, 7) ? 0 : 7; + uint_t gc_cb_idx = lua_isnoneornil( L, 8) ? 0 : 8; #define FIXED_ARGS 8 - uint_t args = lua_gettop(L) - FIXED_ARGS; + int const nargs = lua_gettop(L) - FIXED_ARGS; struct s_Universe* U = get_universe( L); + ASSERT_L( nargs >= 0); // public Lanes API accepts a generic range -3/+3 // that will be remapped into the platform-specific scheduler priority scheme // On some platforms, -3 is equivalent to -2 and +3 to +2 - if( prio < THREAD_PRIO_MIN || prio > THREAD_PRIO_MAX) + if( priority < THREAD_PRIO_MIN || priority > THREAD_PRIO_MAX) { - return luaL_error( L, "Priority out of range: %d..+%d (%d)", THREAD_PRIO_MIN, THREAD_PRIO_MAX, prio); + return luaL_error( L, "Priority out of range: %d..+%d (%d)", THREAD_PRIO_MIN, THREAD_PRIO_MAX, priority); } /* --- Create and prepare the sub state --- */ - DEBUGSPEW_CODE( fprintf( stderr, INDENT_BEGIN "thread_new: setup\n" INDENT_END)); + DEBUGSPEW_CODE( fprintf( stderr, INDENT_BEGIN "lane_new: setup\n" INDENT_END)); DEBUGSPEW_CODE( ++ U->debugspew_indent_depth); // populate with selected libraries at the same time - L2 = luaG_newstate( U, L, libs); + L2 = luaG_newstate( U, L, libs_str); // L // L2 - STACK_GROW( L, 2); - STACK_GROW( L2, 3); + STACK_GROW( L2, nargs + 3); // + STACK_CHECK( L2); - // give a default "Lua" name to the thread to see VM name in Decoda debugger - lua_pushfstring( L2, "Lane #%p", L2); - lua_setglobal( L2, "decoda_name"); + STACK_GROW( L, 3); // func libs cancelstep priority globals package required gc_cb [... args ...] + STACK_CHECK( L); - ASSERT_L( lua_gettop(L2) == 0); + // give a default "Lua" name to the thread to see VM name in Decoda debugger + lua_pushfstring( L2, "Lane #%p", L2); // "..." + lua_setglobal( L2, "decoda_name"); // + ASSERT_L( lua_gettop( L2) == 0); - DEBUGSPEW_CODE( fprintf( stderr, INDENT_BEGIN "thread_new: update 'package'\n" INDENT_END)); + DEBUGSPEW_CODE( fprintf( stderr, INDENT_BEGIN "lane_new: update 'package'\n" INDENT_END)); // package - if( package != 0) + if( package_idx != 0) { // when copying with mode eLM_LaneBody, should raise an error in case of problem, not leave it one the stack - (void) luaG_inter_copy_package( U, L, L2, package, eLM_LaneBody); + (void) luaG_inter_copy_package( U, L, L2, package_idx, eLM_LaneBody); } // modules to require in the target lane *before* the function is transfered! - STACK_CHECK( L); - STACK_CHECK( L2); - if( required != 0) + if( required_idx != 0) { int nbRequired = 1; - DEBUGSPEW_CODE( fprintf( stderr, INDENT_BEGIN "thread_new: require 'required' list\n" INDENT_END)); + DEBUGSPEW_CODE( fprintf( stderr, INDENT_BEGIN "lane_new: require 'required' list\n" INDENT_END)); DEBUGSPEW_CODE( ++ U->debugspew_indent_depth); - // should not happen, was checked in lanes.lua before calling thread_new() - if( lua_type( L, required) != LUA_TTABLE) + // should not happen, was checked in lanes.lua before calling lane_new() + if( lua_type( L, required_idx) != LUA_TTABLE) { - return luaL_error( L, "expected required module list as a table, got %s", luaL_typename( L, required)); + return luaL_error( L, "expected required module list as a table, got %s", luaL_typename( L, required_idx)); } - lua_pushnil( L); - while( lua_next( L, required) != 0) + lua_pushnil( L); // func libs cancelstep priority globals package required gc_cb [... args ...] nil + while( lua_next( L, required_idx) != 0) // func libs cancelstep priority globals package required gc_cb [... args ...] n "modname" { if( lua_type( L, -1) != LUA_TSTRING || lua_type( L, -2) != LUA_TNUMBER || lua_tonumber( L, -2) != nbRequired) { @@ -2238,12 +2251,10 @@ LUAG_FUNC( thread_new) char const* name = lua_tolstring( L, -1, &len); // require the module in the target lane - STACK_GROW( L2, 2); - STACK_CHECK( L2); - lua_getglobal( L2, "require"); // require()? + lua_getglobal( L2, "require"); // require()? if( lua_isnil( L2, -1)) { - lua_pop( L2, 1); // + lua_pop( L2, 1); // luaL_error( L, "cannot pre-require modules without loading 'package' library first"); } else @@ -2254,113 +2265,102 @@ LUAG_FUNC( thread_new) { luaG_copy_one_time_settings( U, L, L2); } - lua_pushlstring( L2, name, len); // require() name - if( lua_pcall( L2, 1, 1, 0) != LUA_OK) // ret/errcode + lua_pushlstring( L2, name, len); // require() name + if( lua_pcall( L2, 1, 1, 0) != LUA_OK) // ret/errcode { // propagate error to main state if any - luaG_inter_move( U, L2, L, 1, eLM_LaneBody); // + luaG_inter_move( U, L2, L, 1, eLM_LaneBody); // func libs cancelstep priority globals package required gc_cb [... args ...] n "modname" error return lua_error( L); } - STACK_MID( L2, 1); // after requiring the module, register the functions it exported in our name<->function database populate_func_lookup_table( L2, -1, name); - STACK_MID( L2, 1); - lua_pop( L2, 1); + lua_pop( L2, 1); // } - STACK_END( L2, 0); } - lua_pop( L, 1); + lua_pop( L, 1); // func libs cancelstep priority globals package required gc_cb [... args ...] n ++ nbRequired; - } + } // func libs cancelstep priority globals package required gc_cb [... args ...] DEBUGSPEW_CODE( -- U->debugspew_indent_depth); } - STACK_END( L2, 0); - STACK_END( L, 0); + STACK_MID( L, 0); + STACK_MID( L2, 0); // // Appending the specified globals to the global environment // *after* stdlibs have been loaded and modules required, in case we transfer references to native functions they exposed... // - if( glob != 0) + if( globals_idx != 0) { - DEBUGSPEW_CODE( fprintf( stderr, INDENT_BEGIN "thread_new: transfer globals\n" INDENT_END)); - STACK_CHECK( L); - STACK_CHECK( L2); - if( !lua_istable( L, glob)) + DEBUGSPEW_CODE( fprintf( stderr, INDENT_BEGIN "lane_new: transfer globals\n" INDENT_END)); + if( !lua_istable( L, globals_idx)) { - return luaL_error( L, "Expected table, got %s", luaL_typename( L, glob)); + return luaL_error( L, "Expected table, got %s", luaL_typename( L, globals_idx)); } DEBUGSPEW_CODE( ++ U->debugspew_indent_depth); - lua_pushnil( L); - lua_pushglobaltable( L2); // Lua 5.2 wants us to push the globals table on the stack - while( lua_next( L, glob)) + lua_pushnil( L); // func libs cancelstep priority globals package required gc_cb [... args ...] nil + // Lua 5.2 wants us to push the globals table on the stack + lua_pushglobaltable( L2); // _G + while( lua_next( L, globals_idx)) // func libs cancelstep priority globals package required gc_cb [... args ...] k v { - luaG_inter_copy( U, L, L2, 2, eLM_LaneBody); // moves the key/value pair to the L2 stack + luaG_inter_copy( U, L, L2, 2, eLM_LaneBody); // _G k v // assign it in L2's globals table - lua_rawset( L2, -3); - lua_pop( L, 1); - } - lua_pop( L2, 1); + lua_rawset( L2, -3); // _G + lua_pop( L, 1); // func libs cancelstep priority globals package required gc_cb [... args ...] k + } // func libs cancelstep priority globals package required gc_cb [... args ...] + lua_pop( L2, 1); // - STACK_END( L2, 0); - STACK_END( L, 0); DEBUGSPEW_CODE( -- U->debugspew_indent_depth); } - - - STACK_CHECK( L); - STACK_CHECK( L2); - ASSERT_L( lua_gettop( L2) == 0); + STACK_MID( L, 0); + STACK_MID( L2, 0); // Lane main function if( lua_type( L, 1) == LUA_TFUNCTION) { int res; - DEBUGSPEW_CODE( fprintf( stderr, INDENT_BEGIN "thread_new: transfer lane body\n" INDENT_END)); + DEBUGSPEW_CODE( fprintf( stderr, INDENT_BEGIN "lane_new: transfer lane body\n" INDENT_END)); DEBUGSPEW_CODE( ++ U->debugspew_indent_depth); - lua_pushvalue( L, 1); - res = luaG_inter_move( U, L, L2, 1, eLM_LaneBody); // L->L2 + lua_pushvalue( L, 1); // func libs cancelstep priority globals package required gc_cb [... args ...] func + res = luaG_inter_move( U, L, L2, 1, eLM_LaneBody); // func libs cancelstep priority globals package required gc_cb [... args ...] // func DEBUGSPEW_CODE( -- U->debugspew_indent_depth); if( res != 0) { return luaL_error( L, "tried to copy unsupported types"); } - STACK_MID( L, 0); } else if( lua_type( L, 1) == LUA_TSTRING) { // compile the string - if( luaL_loadstring( L2, lua_tostring( L, 1)) != 0) + if( luaL_loadstring( L2, lua_tostring( L, 1)) != 0) // func { return luaL_error( L, "error when parsing lane function code"); } } - + STACK_MID( L, 0); STACK_MID( L2, 1); ASSERT_L( lua_isfunction( L2, 1)); // revive arguments - // - if( args > 0) + if( nargs > 0) { int res; - DEBUGSPEW_CODE( fprintf( stderr, INDENT_BEGIN "thread_new: transfer lane arguments\n" INDENT_END)); + DEBUGSPEW_CODE( fprintf( stderr, INDENT_BEGIN "lane_new: transfer lane arguments\n" INDENT_END)); DEBUGSPEW_CODE( ++ U->debugspew_indent_depth); - res = luaG_inter_copy( U, L, L2, args, eLM_LaneBody); // L->L2 + res = luaG_inter_move( U, L, L2, nargs, eLM_LaneBody); // func libs cancelstep priority globals package required gc_cb // func [... args ...] DEBUGSPEW_CODE( -- U->debugspew_indent_depth); if( res != 0) { return luaL_error( L, "tried to copy unsupported types"); } } - STACK_MID( L, 0); - - STACK_END( L2, 1 + args); + STACK_END( L, -nargs); + ASSERT_L( lua_gettop( L) == FIXED_ARGS); + STACK_CHECK( L); + STACK_MID( L2, 1 + nargs); - // 's' is allocated from heap, not Lua, since its life span may surpass - // the handle's (if free running thread) + // 's' is allocated from heap, not Lua, since its life span may surpass the handle's (if free running thread) // - ud = lua_newuserdata( L, sizeof( struct s_lane*)); + ud = lua_newuserdata( L, sizeof( struct s_lane*)); // func libs cancelstep priority globals package required gc_cb lane s = *ud = (struct s_lane*) malloc( sizeof( struct s_lane)); if( s == NULL) { @@ -2382,44 +2382,49 @@ LUAG_FUNC( thread_new) s->selfdestruct_next = NULL; #if HAVE_LANE_TRACKING s->tracking_next = NULL; + if( s->U->tracking_first) + { + tracking_add( s); + } #endif // HAVE_LANE_TRACKING // Set metatable for the userdata // - lua_pushvalue( L, lua_upvalueindex( 1)); - lua_setmetatable( L, -2); + lua_pushvalue( L, lua_upvalueindex( 1)); // func libs cancelstep priority globals package required gc_cb lane mt + lua_setmetatable( L, -2); // func libs cancelstep priority globals package required gc_cb lane STACK_MID( L, 1); // Create uservalue for the userdata // (this is where lane body return values will be stored when the handle is indexed by a numeric key) - lua_newtable( L); + lua_newtable( L); // func libs cancelstep priority globals package required gc_cb lane uv // Store the gc_cb callback in the uservalue - if( gc_cb > 0) + if( gc_cb_idx > 0) { - lua_pushlightuserdata( L, GCCB_KEY); - lua_pushvalue( L, gc_cb); - lua_rawset( L, -3); + lua_pushlightuserdata( L, GCCB_KEY); // func libs cancelstep priority globals package required gc_cb lane uv k + lua_pushvalue( L, gc_cb_idx); // func libs cancelstep priority globals package required gc_cb lane uv k gc_cb + lua_rawset( L, -3); // func libs cancelstep priority globals package required gc_cb lane uv } - lua_setuservalue( L, -2); + lua_setuservalue( L, -2); // func libs cancelstep priority globals package required gc_cb lane // Store 's' in the lane's registry, for 'cancel_test()' (even if 'cs'==0 we still do cancel tests at pending send/receive). - lua_pushlightuserdata( L2, CANCEL_TEST_KEY); - lua_pushlightuserdata( L2, s); - lua_rawset( L2, LUA_REGISTRYINDEX); + lua_pushlightuserdata( L2, CANCEL_TEST_KEY); // func [... args ...] k + lua_pushlightuserdata( L2, s); // func [... args ...] k s + lua_rawset( L2, LUA_REGISTRYINDEX); // func [... args ...] - if( cs) + if( cancelstep_idx) { - lua_sethook( L2, cancel_hook, LUA_MASKCOUNT, cs); + lua_sethook( L2, cancel_hook, LUA_MASKCOUNT, cancelstep_idx); } - DEBUGSPEW_CODE( fprintf( stderr, INDENT_BEGIN "thread_new: launching thread\n" INDENT_END)); - THREAD_CREATE( &s->thread, lane_main, s, prio); - STACK_END( L, 1); + DEBUGSPEW_CODE( fprintf( stderr, INDENT_BEGIN "lane_new: launching thread\n" INDENT_END)); + THREAD_CREATE( &s->thread, lane_main, s, priority); DEBUGSPEW_CODE( -- U->debugspew_indent_depth); + STACK_END( L, 1); + STACK_END( L2, 1 + nargs); return 1; } @@ -3161,8 +3166,8 @@ LUAG_FUNC( configure) lua_setfield( L, -2, "__metatable"); // settings M mt } - lua_pushcclosure( L, LG_thread_new, 1); // settings M LG_thread_new - lua_setfield( L, -2, "thread_new"); // settings M + lua_pushcclosure( L, LG_lane_new, 1); // settings M lane_new + lua_setfield( L, -2, "lane_new"); // settings M // we can't register 'lanes.require' normally because we want to create an upvalued closure 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_) -- -- Cache globals for code that might run under sandboxing -- - local assert = assert + local assert = assert( assert) local string_gmatch = assert( string.gmatch) + local string_format = assert( string.format) local select = assert( select) local type = assert( type) local pairs = assert( pairs) @@ -127,7 +128,7 @@ lanes.configure = function( settings_) return settings end local settings = core.configure and core.configure( params_checker( settings_)) or core.settings - local thread_new = assert( core.thread_new) + local core_lane_new = assert( core.lane_new) local max_prio = assert( core.max_prio) lanes.ABOUT = @@ -203,11 +204,9 @@ end -- ... (more options may be introduced later) ... -- -- Calling with a function parameter ('lane_func') ends the string/table --- modifiers, and prepares a lane generator. One can either finish here, --- and call the generator later (maybe multiple times, with different parameters) --- or add on actual thread arguments to also ignite the thread on the same call. +-- modifiers, and prepares a lane generator. -local valid_libs= +local valid_libs = { ["package"] = true, ["table"] = true, @@ -223,88 +222,110 @@ local valid_libs= ["lanes.core"] = true } +local raise_option_error = function( name_, tv_, v_) + error( "Bad '" .. name_ .. "' option: " .. tv_ .. " " .. string_format( "%q", tostring( v_)), 4) +end + +local opt_validators = +{ + priority = function( v_) + local tv = type( v_) + return (tv == "number") and v_ or raise_option_error( "priority", tv, v_) + end, + cancelstep = function( v_) + local tv = type( v_) + return (tv == "number") and v_ or (v_ == true) and 100 or (v_ == false) and 0 or raise_option_error( "cancelstep", tv, v_) + end, + globals = function( v_) + local tv = type( v_) + return (tv == "table") and v_ or raise_option_error( "globals", tv, v_) + end, + package = function( v_) + local tv = type( v_) + return (tv == "table") and v_ or raise_option_error( "package", tv, v_) + end, + required = function( v_) + local tv = type( v_) + return (tv == "table") and v_ or raise_option_error( "required", tv, v_) + end, + gc_cb = function( v_) + local tv = type( v_) + return (tv == "function") and v_ or raise_option_error( "gc_cb", tv, v_) + end +} + -- PUBLIC LANES API -local function gen( ... ) - local opt= {} - local libs= nil - local lev= 2 -- level for errors - - local n= select('#',...) - - if n==0 then - error( "No parameters!" ) - end +-- receives a sequence of strings and tables, plus a function +local gen = function( ...) + -- aggregrate all strings together, separated by "," as well as tables + -- the strings are a list of libraries to open + -- the tables contain the lane options + local opt = {} + local libs = nil + + local n = select( '#', ...) + + -- we need at least a function + if n == 0 then + error( "No parameters!", 2) + end - for i=1,n-1 do - local v= select(i,...) - if type(v)=="string" then - libs= libs and libs..","..v or v - elseif type(v)=="table" then - for k,vv in pairs(v) do - opt[k]= vv - end - elseif v==nil then - -- skip - else - error( "Bad parameter: "..tostring(v) ) - end - end + -- all arguments but the last must be nil, strings, or tables + for i = 1, n - 1 do + local v = select( i, ...) + local tv = type( v) + if tv == "string" then + libs = libs and libs .. "," .. v or v + elseif tv == "table" then + for k, vv in pairs( v) do + opt[k]= vv + end + elseif v == nil then + -- skip + else + error( "Bad parameter " .. i .. ": " .. tv .. " " .. string_format( "%q", tostring( v)), 2) + end + end - local func= select(n,...) - local functype = type(func) - if functype ~= "function" and functype ~= "string" then - error( "Last parameter not function or string: "..tostring(func)) - end + -- the last argument should be a function or a string + local func = select( n, ...) + local functype = type( func) + if functype ~= "function" and functype ~= "string" then + error( "Last parameter not function or string: " .. functype .. " " .. string_format( "%q", tostring( func)), 2) + end - -- Check 'libs' already here, so the error goes in the right place - -- (otherwise will be noticed only once the generator is called) - -- "*" is a special case that doesn't require individual checking - -- - if libs and libs ~= "*" then - local found = {} - -- check that the caller only provides reserved library names - for s in string_gmatch(libs, "[%a%d.]+") do - if not valid_libs[s] then - error( "Bad library name: " .. s) - else - found[s] = (found[s] or 0) + 1 - if found[s] > 1 then - error( "libs specification contains '" .. s .. "' more than once") - end - end - end - end - - local prio, cs, g_tbl, package_tbl, required, gc_cb - - for k,v in pairs(opt) do - if k == "priority" then - prio = (type( v) == "number") and v or error( "Bad 'prio' option: expecting number, got " .. type( v), lev) - elseif k=="cancelstep" then - cs = (v==true) and 100 or - (v==false) and 0 or - type(v)=="number" and v or - error( "Bad cancelstep: "..tostring(v), lev ) - elseif k=="globals" then g_tbl= v - elseif k=="package" then - package_tbl = (type( v) == "table") and v or error( "Bad package: " .. tostring( v), lev) - elseif k=="required" then - required= (type( v) == "table") and v or error( "Bad 'required' option: expecting table, got " .. type( v), lev) - elseif k == "gc_cb" then - gc_cb = (type( v) == "function") and v or error( "Bad 'gc_cb' option: expecting function, got " .. type( v), lev) - --.. - elseif k==1 then error( "unkeyed option: ".. tostring(v), lev ) - else error( "Bad option: ".. tostring(k), lev ) - end - end + -- check that the caller only provides reserved library names, and those only once + -- "*" is a special case that doesn't require individual checking + if libs and libs ~= "*" then + local found = {} + for s in string_gmatch(libs, "[%a%d.]+") do + if not valid_libs[s] then + error( "Bad library name: " .. s, 2) + else + found[s] = (found[s] or 0) + 1 + if found[s] > 1 then + error( "libs specification contains '" .. s .. "' more than once", 2) + end + end + end + end - if not package_tbl then package_tbl = package end - -- Lane generator - -- - return function(...) - return thread_new( func, libs, cs, prio, g_tbl, package_tbl, required, gc_cb, ...) -- args - end -end + -- validate that each option is known and properly valued + for k, v in pairs( opt) do + local validator = opt_validators[k] + if not validator then + error( (type( k) == "number" and "Unkeyed option: " .. type( v) .. " " .. string_format( "%q", tostring( v)) or "Bad '" .. tostring( k) .. "' option"), 2) + else + opt[k] = validator( v) + end + end + + local cancelstep, priority, globals, package, required, gc_cb = opt.cancelstep, opt.priority, opt.globals, opt.package or package, opt.required, opt.gc_cb + return function( ...) + -- must pass functions args last else they will be truncated to the first one + return core_lane_new( func, libs, cancelstep, priority, globals, package, required, gc_cb, ...) + end +end -- gen() ---=== Timers ===--- -- cgit v1.2.3-55-g6feb