From 1760eafa1d2ebce8f07e11414a53d4a251af5b8e Mon Sep 17 00:00:00 2001 From: Benoit Germain Date: Fri, 18 Feb 2011 20:33:43 +0100 Subject: * keeper.lua is now embedded in text form instead of bytecode to improve LuaJIT2-compatibility (but this is not enough). * moved keeper-related code in a separate source file --- CHANGES | 4 ++ CMakeLists.txt | 8 +-- docs/index.html | 27 ++++++- make-vc.cmd | 6 +- src/Makefile | 12 ++-- src/keeper.c | 199 +++++++++++++++++++++++++++++++++++++++++++++++++++ src/keeper.h | 18 +++++ src/keeper.lua | 28 ++++---- src/lanes.c | 215 +------------------------------------------------------- src/tools.c | 69 ++++++++++++++++++ src/tools.h | 3 + 11 files changed, 347 insertions(+), 242 deletions(-) create mode 100644 src/keeper.c create mode 100644 src/keeper.h diff --git a/CHANGES b/CHANGES index 1e84397..1850415 100644 --- a/CHANGES +++ b/CHANGES @@ -3,6 +3,10 @@ CHANGES: CHANGE X: +CHANGE 28 BGe 18-Feb-2011 + - moved keeper-related code in a separate source file + - keeper.lua is now embedded in text form instead of bytecode to improve LuaJIT2-compatibility + CHANGE 27 BGe 17-Feb-2011 - we know Lanes is loaded in the master state, so we don't force it to be required in every lane too when a linda deep userdata is copied diff --git a/CMakeLists.txt b/CMakeLists.txt index be6534c..31883fa 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -25,13 +25,11 @@ IF(CYGWIN) ENDIF(CYGWIN) #2DO - use provided bin2c -# Compile Lua bytecode to C +# Embed keeper.lua in text form in C (embedding bytecode is not LuaJIT2-compatible) ADD_CUSTOM_COMMAND(OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/keeper.lch DEPENDS src/keeper.lua - COMMAND "${LUAC}" "-o" "${CMAKE_CURRENT_BINARY_DIR}/keeper.lo" - "${CMAKE_CURRENT_SOURCE_DIR}/src/keeper.lua" COMMAND "${LUA}" "${CMAKE_CURRENT_SOURCE_DIR}/tools/bin2c.lua" - "${CMAKE_CURRENT_BINARY_DIR}/keeper.lo" + "${CMAKE_CURRENT_SOURCE_DIR}/src/keeper.lua" "-o" "${CMAKE_CURRENT_BINARY_DIR}/keeper.lch") SET_SOURCE_FILES_PROPERTIES(src/lanes.c PROPERTIES OBJECT_DEPENDS ${CMAKE_CURRENT_BINARY_DIR}/keeper.lch) @@ -40,7 +38,7 @@ INCLUDE_DIRECTORIES(${CMAKE_CURRENT_BINARY_DIR}) # Build INCLUDE_DIRECTORIES(src) -ADD_LIBRARY(lua51-lanes MODULE src/lanes.c src/threading.c src/tools.c) +ADD_LIBRARY(lua51-lanes MODULE src/lanes.c src/threading.c src/tools.c src/keeper.c) IF(UNIX AND NOT CYGWIN) SET(LIBS pthread) diff --git a/docs/index.html b/docs/index.html index 03c91f7..a28dd41 100644 --- a/docs/index.html +++ b/docs/index.html @@ -799,8 +799,31 @@ can be used for custom userdata as well. Here's what to do.

  1. Provide an identity function for your userdata, in C. This function is -used for creation and deletion of your deep userdata (the shared resource), -and for making metatables for the state-specific proxies for accessing it. + used for creation and deletion of your deep userdata (the shared resource), + and for making metatables for the state-specific proxies for accessing it. The + prototype is + + +
    + void idfunc( lua_State *L, char const * const which); +
    +which can be one of: +
      +
    • + "new": requests the creation of a new object, whose pointer is pushed on the stack + as a light userdata. +
    • +
    • + "delete": receives this same pointer on the stack, and should cleanup the object.
    • +
    • + "metatable": should build a metatable for the object. Don't cache the metatable +yourself, Lanes takes care of it ("metatable" should only be invoked once).
    • +
    • + "module": is the name of the module that exports the idfunc, +to be pushed on the stack as a string. It is necessary so that Lanes can require it in +any Lane and keeper state that receives a userdata. This is to prevent crashes in situations +where the module could be unloaded while the idfunc pointer is still held.
    • +
    Take a look at linda_id in lanes.c.
  2. Instanciate your userdata using luaG_deep_userdata(), diff --git a/make-vc.cmd b/make-vc.cmd index 2b4a7f6..7711a44 100644 --- a/make-vc.cmd +++ b/make-vc.cmd @@ -114,13 +114,11 @@ goto ERR_NOLUA :LUA_LIB_OK @REM -@REM Precompile src/.lua -> .lch +@REM Embed src/keeper.lua -> .lch @REM @REM Note: we cannot use piping in Windows since we need binary output. @REM -"%LUAC_EXE%" -o delme src/keeper.lua -"%LUA_EXE%" tools/bin2c.lua -o src/keeper.lch delme -@del delme +"%LUA_EXE%" tools/bin2c.lua -o src/keeper.lch src/keeper.lua @if "%VCINSTALLDIR%"=="" goto ERR_NOVC diff --git a/src/Makefile b/src/Makefile index a17e9cd..df65926 100644 --- a/src/Makefile +++ b/src/Makefile @@ -7,7 +7,7 @@ MODULE=lanes -SRC=lanes.c threading.c tools.c +SRC=lanes.c threading.c tools.c keeper.c OBJ=$(SRC:.c=.o) @@ -134,10 +134,14 @@ lanes.o: keeper.lch # Note: 'luac -o -' could be used on systems other than Windows (where pipes # are binary). We need to support MinGW as well, so a temporary file. # +# mode 1: embed bytecode version +#%.lch: %.lua +# $(LUAC) -o $@.tmp $< +# $(LUA) ../tools/bin2c.lua $@.tmp -o $@ +# -rm $@.tmp +# mode 2: embed text version (LuaJIT2-compatible) %.lch: %.lua - $(LUAC) -o $@.tmp $< - $(LUA) ../tools/bin2c.lua $@.tmp -o $@ - -rm $@.tmp + $(LUA) ../tools/bin2c.lua $< -o $@ #--- # NSLU2 "slug" Linux ARM diff --git a/src/keeper.c b/src/keeper.c new file mode 100644 index 0000000..f19beed --- /dev/null +++ b/src/keeper.c @@ -0,0 +1,199 @@ +/* + -- + -- KEEPER.C + -- + -- Keeper state logic + -- + -- This code is read in for each "keeper state", which are the hidden, inter- + -- mediate data stores used by Lanes inter-state communication objects. + -- + -- Author: Benoit Germain + -- + -- C implementation replacement of the original keeper.lua + -- + --[[ + =============================================================================== + + Copyright (C) 2011 Benoit Germain + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + THE SOFTWARE. + + =============================================================================== + ]]-- + */ + +#include +#include +#include +#include + +#include "lua.h" +#include "lauxlib.h" + +#include "threading.h" +#include "tools.h" +#include "keeper.h" + +/*---=== Keeper states ===--- +*/ + +/* The selected number is not optimal; needs to be tested. Even using just +* one keeper state may be good enough (depends on the number of Lindas used +* in the applications). +*/ +#define KEEPER_STATES_N 1 // 6 + +/* +* Pool of keeper states +* +* Access to keeper states is locked (only one OS thread at a time) so the +* bigger the pool, the less chances of unnecessary waits. Lindas map to the +* keepers randomly, by a hash. +*/ +static struct s_Keeper GKeepers[KEEPER_STATES_N]; + +/* We could use an empty table in 'keeper.lua' as the sentinel, but maybe +* checking for a lightuserdata is faster. +*/ +static bool_t nil_sentinel = 0; + +/* +* Lua code for the keeper states (baked in) +*/ +static char const keeper_chunk[]= +#include "keeper.lch" + +/* +* Initialize keeper states +* +* If there is a problem, return an error message (NULL for okay). +* +* Note: Any problems would be design flaws; the created Lua state is left +* unclosed, because it does not really matter. In production code, this +* function never fails. +*/ +const char *init_keepers(void) +{ + unsigned int i; + for( i=0; i> 3) % KEEPER_STATES_N; + struct s_Keeper *K= &GKeepers[i]; + + MUTEX_LOCK( &K->lock_); + //++ K->count; + return K; +} + +void keeper_release( struct s_Keeper *K) +{ + //-- K->count; + MUTEX_UNLOCK( &K->lock_); +} + +/* +* Call a function ('func_name') in the keeper state, and pass on the returned +* values to 'L'. +* +* 'linda': deep Linda pointer (used only as a unique table key, first parameter) +* 'starting_index': first of the rest of parameters (none if 0) +* +* Returns: number of return values (pushed to 'L') or -1 in case of error +*/ +int keeper_call( lua_State *K, char const *func_name, lua_State *L, void *linda, uint_t starting_index) +{ + int const args = starting_index ? (lua_gettop(L) - starting_index +1) : 0; + int const Ktos = lua_gettop(K); + int retvals = -1; + + STACK_GROW( K, 2); + + lua_getglobal( K, func_name); + ASSERT_L( lua_isfunction(K, -1)); + + lua_pushlightuserdata( K, linda); + + if( (args == 0) || luaG_inter_copy( L, K, args) == 0) // L->K + { + lua_call( K, 1 + args, LUA_MULTRET); + + retvals = lua_gettop( K) - Ktos; + if( (retvals > 0) && luaG_inter_move( K, L, retvals) != 0) // K->L + { + retvals = -1; + } + } + // whatever happens, restore the stack to where it was at the origin + lua_settop( K, Ktos); + return retvals; +} + +void close_keepers(void) +{ + int i; + for(i=0;i @@ -113,12 +114,6 @@ THE SOFTWARE. # include #endif -/* The selected number is not optimal; needs to be tested. Even using just -* one keeper state may be good enough (depends on the number of Lindas used -* in the applications). -*/ -#define KEEPER_STATES_N 1 // 6 - /* Do you want full call stacks, or just the line where the error happened? * * TBD: The full stack feature does not seem to work (try 'make error'). @@ -129,12 +124,6 @@ THE SOFTWARE. # define STACK_TRACE_KEY ((void*)lane_error) // used as registry key #endif -/* -* Lua code for the keeper states (baked in) -*/ -static char keeper_chunk[]= -#include "keeper.lch" - // NOTE: values to be changed by either thread, during execution, without // locking, are marked "volatile" // @@ -254,199 +243,6 @@ static bool_t push_registry_table( lua_State *L, void *key, bool_t create ) { } -/*---=== Serialize require ===--- -*/ - -static MUTEX_T require_cs; - -//--- -// [val]= new_require( ... ) -// -// Call 'old_require' but only one lane at a time. -// -// Upvalues: [1]: original 'require' function -// -static int new_require( lua_State *L ) { - int rc; - int args= lua_gettop(L); - - STACK_GROW(L,1); - STACK_CHECK(L) - - // Using 'lua_pcall()' to catch errors; otherwise a failing 'require' would - // leave us locked, blocking any future 'require' calls from other lanes. - // - MUTEX_LOCK( &require_cs ); - { - lua_pushvalue( L, lua_upvalueindex(1) ); - lua_insert( L, 1 ); - - rc= lua_pcall( L, args, 1 /*retvals*/, 0 /*errfunc*/ ); - // - // LUA_ERRRUN / LUA_ERRMEM - } - MUTEX_UNLOCK( &require_cs ); - - if (rc) lua_error(L); // error message already at [-1] - - STACK_END(L,0) - return 1; -} - -/* -* Serialize calls to 'require', if it exists -*/ -static -void serialize_require( lua_State *L ) { - - STACK_GROW(L,1); - STACK_CHECK(L) - - // Check 'require' is there; if not, do nothing - // - lua_getglobal( L, "require" ); - if (lua_isfunction( L, -1 )) { - // [-1]: original 'require' function - - lua_pushcclosure( L, new_require, 1 /*upvalues*/ ); - lua_setglobal( L, "require" ); - - } else { - // [-1]: nil - lua_pop(L,1); - } - - STACK_END(L,0) -} - - -/*---=== Keeper states ===--- -*/ - -/* -* Pool of keeper states -* -* Access to keeper states is locked (only one OS thread at a time) so the -* bigger the pool, the less chances of unnecessary waits. Lindas map to the -* keepers randomly, by a hash. -*/ -struct s_Keeper -{ - MUTEX_T lock_; - lua_State *L; - //int count; -} GKeepers[KEEPER_STATES_N]; - -/* We could use an empty table in 'keeper.lua' as the sentinel, but maybe -* checking for a lightuserdata is faster. -*/ -static bool_t nil_sentinel; - -/* -* Initialize keeper states -* -* If there is a problem, return an error message (NULL for okay). -* -* Note: Any problems would be design flaws; the created Lua state is left -* unclosed, because it does not really matter. In production code, this -* function never fails. -*/ -static const char *init_keepers(void) { - unsigned int i; - for( i=0; i> 3) % KEEPER_STATES_N; - struct s_Keeper *K= &GKeepers[i]; - - MUTEX_LOCK( &K->lock_); - //++ K->count; - return K; -} - -static void keeper_release( struct s_Keeper *K) -{ - //-- K->count; - MUTEX_UNLOCK( &K->lock_); -} - -/* -* Call a function ('func_name') in the keeper state, and pass on the returned -* values to 'L'. -* -* 'linda': deep Linda pointer (used only as a unique table key, first parameter) -* 'starting_index': first of the rest of parameters (none if 0) -* -* Returns: number of return values (pushed to 'L') or -1 in case of error -*/ -static int keeper_call( lua_State *K, char const *func_name, lua_State *L, struct s_Linda *linda, uint_t starting_index) -{ - int const args = starting_index ? (lua_gettop(L) - starting_index +1) : 0; - int const Ktos = lua_gettop(K); - int retvals = -1; - - STACK_GROW( K, 2); - - lua_getglobal( K, func_name); - ASSERT_L( lua_isfunction(K, -1)); - - lua_pushlightuserdata( K, linda); - - if( (args == 0) || luaG_inter_copy( L, K, args) == 0) // L->K - { - lua_call( K, 1 + args, LUA_MULTRET); - - retvals = lua_gettop( K) - Ktos; - if( (retvals > 0) && luaG_inter_move( K, L, retvals) != 0) // K->L - { - retvals = -1; - } - } - // whatever happens, restore the stack to where it was at the origin - lua_settop( K, Ktos); - return retvals; -} - - /*---=== Linda ===--- */ @@ -1189,14 +985,7 @@ static void selfdestruct_atexit( void ) { DEBUGEXEC(fprintf( stderr, "Killed %d lane(s) at process end.\n", n )); #endif } - { - int i; - for(i=0;i