From 68d8db431ec2b739dc53233d6b4d8aeee9324e48 Mon Sep 17 00:00:00 2001
From: Benoit Germain
Date: Thu, 24 Jan 2013 22:46:21 +0100
Subject: version 3.4.3 * raise an error if lane generator libs
specification contains a lib more than once * bit32 is a valid lib name in
the libs specification (silently ignored by the Lua 5.1 build) * improved
lanes.nameof to search inside table- and userdata- metatables for an object's
name * fixed an unwarranted error when trying to discover a function name
upon a failed transfer * contents of
package.[path,cpath,preload,loaders|searchers] are pulled *only once* inside
keeper states at initialisation * Lua function upvalues equal to the
global environment aren't copied by value, but bound to the destination's
global environment especially useful for Lua 5.2 _ENV * fixed loading
of base libraries that didn't create the global tables when built for Lua 5.2
---
CHANGES | 13 +-
docs/index.html | 27 ++--
src/keeper.c | 57 ++++----
src/keeper.h | 2 +-
src/lanes.c | 371 +++++++++++++++++++++++++++++----------------------
src/lanes.lua | 18 ++-
src/tools.c | 249 ++++++++++++++++++++++------------
src/tools.h | 20 ++-
tests/basic.lua | 38 ++++--
tests/keeper.lua | 3 +-
tests/linda_perf.lua | 5 +-
11 files changed, 489 insertions(+), 314 deletions(-)
diff --git a/CHANGES b/CHANGES
index 6d44716..52ec52d 100644
--- a/CHANGES
+++ b/CHANGES
@@ -1,5 +1,16 @@
CHANGES:
+CHANGE 55: BGe 24-Jan-13
+ * version 3.4.3
+ * raise an error if lane generator libs specification contains a lib more than once
+ * bit32 is a valid lib name in the libs specification (silently ignored by the Lua 5.1 build)
+ * improved lanes.nameof to search inside table- and userdata- metatables for an object's name
+ * fixed an unwarranted error when trying to discover a function name upon a failed transfer
+ * contents of package.[path,cpath,preload,loaders|searchers] are pulled *only once* inside keeper states at initialisation
+ * Lua function upvalues equal to the global environment aren't copied by value, but bound to the destination's global environment
+ especially useful for Lua 5.2 _ENV
+ * fixed loading of base libraries that didn't create the global tables when built for Lua 5.2
+
CHANGE 54: BGe 10-Jan-13
* version 3.4.2
* Don't pull "package" settings in the timer lane
@@ -92,7 +103,7 @@ CHANGE 35 BGe 17-Feb-2012
CHANGE 34 BGe 14-Nov-2011
* removed packagepath and packagecpath options, replaced by a package table, whose fields path, cpath, loaders, preload are transfered
- * code cleanup to facilitate transition between WIN32 and PTHREAD impleentations
+ * code cleanup to facilitate transition between WIN32 and PTHREAD implementations
* tentative fix for desinit crashes when free running lanes are killed at process shutdown
CHANGE 33 BGe 5-Nov-2011: Lanes version 3.0-beta
diff --git a/docs/index.html b/docs/index.html
index 4d11371..8188fe9 100644
--- a/docs/index.html
+++ b/docs/index.html
@@ -63,13 +63,13 @@
- Copyright © 2007-12 Asko Kauppi, Benoit Germain. All rights reserved.
+ Copyright © 2007-13 Asko Kauppi, Benoit Germain. All rights reserved.
Lua Lanes is published under the same MIT license as Lua 5.1 and 5.2.
- This document was revised on 10-Jan-13, and applies to version 3.4.2.
+ This document was revised on 24-Jan-13, and applies to version 3.4.3.
@@ -246,7 +246,7 @@
- .on_create_state
+ .on_state_create
|
C function/nil
@@ -415,7 +415,7 @@
|
|
- all standard libraries (including those specific to LuaJIT and not listed above)
+ All standard libraries (including those specific to LuaJIT and not listed above). This must be used alone.
|
@@ -487,9 +487,13 @@
table |
+ Introduced at version 3.0.
+
Specifying it when libs_str doesn't cause the package library to be loaded will generate an error.
If not specified, the created lane will receive the current values of package. Only path, cpath, preload and loaders (Lua 5.1)/searchers (Lua 5.2) are transfered.
+
+ IMPORTANT: The contents of whatever package table is actually provided to the lane won't be known to the keeper states: they will stick to whatever was found in the main state's package table when Lane was required.
|
@@ -1118,13 +1122,18 @@ events to a common Linda, but... :).
Rather completely re-initialize a module with require in the target lane.
+
+ Lua 5.2 functions may have a special _ENV upvalue if they perform 'global namespace' lookups. Unless special care is taken, this upvalue defaults to the table found at LUA_RIDX_GLOBALS.
+ Obviously, we don't want to transfer the whole global table along with each Lua function. Therefore, any upvalue equal to the global table is not transfered by value, but simply bound
+ to the global table in the destination state. Note that this also applies when Lanes is built for Lua 5.1, as it doesn't hurt.
+
Full userdata can be passed only if it's prepared using the deep userdata system, which handles its lifespan management
- In particular, lane handles cannot be passed between lanes.
- Coroutines cannot be passed. A coroutine's Lua state is tied to the Lua state that created it, and there is no way the mixed C/Lua stack of a couroutine can be transfered from one Lua state to another.
+ Coroutines cannot be passed. A coroutine's Lua state is tied to the Lua state that created it, and there is no way the mixed C/Lua stack of a coroutine can be transfered from one Lua state to another.
@@ -1158,7 +1167,7 @@ events to a common Linda, but... :).
// expects a C function on top of the source Lua stack
copy_func( lua_State *dest, lua_State* source)
{
- // extract C function pointer from source
+ // fetch function 'name' from source lookup database
char const* funcname = lookup_func_name( source, -1);
// lookup a function bound to this name in the destination state, and push it on the stack
push_resolved_func( dest, funcname);
@@ -1200,14 +1209,14 @@ events to a common Linda, but... :).
Lua 5.2 introduced a hash randomizer seed which causes table iteration to yield a different key order on different VMs even when the tables are populated the exact same way.
When Lua is built with compatibility options (such as LUA_COMPAT_ALL), this causes several base libraries to register functions under multiple names.
- This, with the randomizer, can cause the first name of a function to be different on different VMs, which breaks function transfer.
+ This, with the randomizer, can cause the first encountered name of a function to be different on different VMs, which breaks function transfer.
This means that Lua 5.2 must be built WITHOUT compatibility options to be able to use Lanes.
Even under Lua 5.1, this may cause trouble (even if this would be much less frequent).
- Unfortunately, this fails with string.gfind/string.gmatch when Lua 5.1 is built with LUA_COMPAT_GFIND (which is the case of LuaBinaries),
+ Unfortunately, we get bitten by string.gfind/string.gmatch when Lua 5.1 is built with LUA_COMPAT_GFIND (which is the case of LuaBinaries),
so for the time being, Lanes fails only for Lua 5.2 as the randomizer is the real show breaker here.
- Another more direct reason of failed transfer is that the destination state doesn't know about the C function that has to be transferred. This occurs if a function is transferred in a lane before it had a chance to scan the module.
+ Another more immediate reason of failed transfer is when the destination state doesn't know about the C function that has to be transferred. This occurs if a function is transferred in a lane before it had a chance to scan the module.
If the C function is sent through a linda, it is enough for the destination lane body to have required the module before the function is sent.
But if the lane body provided to the generator has a C function as upvalue, the transfer itself must succeed, therefore the module that imported that C function must be required in the destination lane before the lane body starts executing. This is where the .required options play their role.
diff --git a/src/keeper.c b/src/keeper.c
index 0be4fd3..a7c8647 100644
--- a/src/keeper.c
+++ b/src/keeper.c
@@ -557,7 +557,7 @@ void close_keepers( void)
* unclosed, because it does not really matter. In production code, this
* function never fails.
*/
-char const* init_keepers( int const _nbKeepers, lua_CFunction _on_state_create)
+char const* init_keepers( lua_State* L, int const _nbKeepers, lua_CFunction _on_state_create)
{
int i;
assert( _nbKeepers >= 1);
@@ -565,15 +565,16 @@ char const* init_keepers( int const _nbKeepers, lua_CFunction _on_state_create)
GKeepers = malloc( _nbKeepers * sizeof( struct s_Keeper));
for( i = 0; i < _nbKeepers; ++ i)
{
-
// We need to load all base libraries in the keeper states so that the transfer databases are populated properly
//
// 'io' for debugging messages, 'package' because we need to require modules exporting idfuncs
// the others because they export functions that we may store in a keeper for transfer between lanes
lua_State* K = luaG_newstate( "*", _on_state_create);
- if (!K)
+ if( !K)
return "out of memory";
+ DEBUGSPEW_CODE( fprintf( stderr, "init_keepers %d\n", i));
+
STACK_CHECK( K)
// to see VM name in Decoda debugger
lua_pushliteral( K, "Keeper #");
@@ -581,6 +582,11 @@ char const* init_keepers( int const _nbKeepers, lua_CFunction _on_state_create)
lua_concat( K, 2);
lua_setglobal( K, "decoda_name");
+ // replace default 'package' contents with stuff gotten from the master state
+ lua_getglobal( L, "package");
+ luaG_inter_copy_package( L, K, -1);
+ lua_pop( L, 1);
+
#if KEEPER_MODEL == KEEPER_MODEL_C
// create the fifos table in the keeper state
lua_pushlightuserdata( K, fifos_key);
@@ -589,7 +595,7 @@ char const* init_keepers( int const _nbKeepers, lua_CFunction _on_state_create)
#endif // KEEPER_MODEL == KEEPER_MODEL_C
#if KEEPER_MODEL == KEEPER_MODEL_LUA
- // use package.loaders[2] to find keeper microcode
+ // use package.loaders[2] to find keeper microcode (NOTE: this works only if nobody tampered with the loaders table...)
lua_getglobal( K, "package"); // package
lua_getfield( K, -1, "loaders"); // package package.loaders
lua_rawgeti( K, -1, 2); // package package.loaders package.loaders[2]
@@ -619,55 +625,38 @@ char const* init_keepers( int const _nbKeepers, lua_CFunction _on_state_create)
}
// cause each keeper state to populate its database of transferable functions with those from the specified module
-void populate_keepers( lua_State *L)
+// do do this we simply require the module inside the keeper state, then populate the lookup database
+void populate_keepers( lua_State* L)
{
size_t name_len;
- char const *name = luaL_checklstring( L, -1, &name_len);
- size_t package_path_len;
- char const *package_path;
- size_t package_cpath_len;
- char const *package_cpath;
+ char const* name = luaL_checklstring( L, -1, &name_len);
int i;
- // we need to make sure that package.path & package.cpath are the same in the keepers
-// than what is currently in use when the module is required in the caller's Lua state
- STACK_CHECK(L)
+ STACK_CHECK( L)
STACK_GROW( L, 3);
- lua_getglobal( L, "package");
- lua_getfield( L, -1, "path");
- package_path = luaL_checklstring( L, -1, &package_path_len);
- lua_getfield( L, -2, "cpath");
- package_cpath = luaL_checklstring( L, -1, &package_cpath_len);
for( i = 0; i < GNbKeepers; ++ i)
{
- lua_State *K = GKeepers[i].L;
+ lua_State* K = GKeepers[i].L;
int res;
MUTEX_LOCK( &GKeepers[i].lock_);
- STACK_CHECK(K)
+ STACK_CHECK( K)
STACK_GROW( K, 2);
- lua_getglobal( K, "package");
- lua_pushlstring( K, package_path, package_path_len);
- lua_setfield( K, -2, "path");
- lua_pushlstring( K, package_cpath, package_cpath_len);
- lua_setfield( K, -2, "cpath");
- lua_pop( K, 1);
lua_getglobal( K, "require");
lua_pushlstring( K, name, name_len);
res = lua_pcall( K, 1, 0, 0);
if( res != 0)
{
- char const *err = luaL_checkstring( K, -1);
+ char const* err = luaL_checkstring( K, -1);
luaL_error( L, "error requiring '%s' in keeper state: %s", name, err);
}
- STACK_END(K, 0)
+ STACK_END( K, 0)
MUTEX_UNLOCK( &GKeepers[i].lock_);
}
- lua_pop( L, 3);
- STACK_END(L, 0)
+ STACK_END( L, 0)
}
-struct s_Keeper *keeper_acquire( const void *ptr)
+struct s_Keeper* keeper_acquire( void const* ptr)
{
// can be 0 if this happens during main state shutdown (lanes is being GC'ed -> no keepers)
if( GNbKeepers == 0)
@@ -691,19 +680,19 @@ struct s_Keeper *keeper_acquire( const void *ptr)
}
}
-void keeper_release( struct s_Keeper *K)
+void keeper_release( struct s_Keeper* K)
{
//-- K->count;
if( K) MUTEX_UNLOCK( &K->lock_);
}
-void keeper_toggle_nil_sentinels( lua_State *L, int _val_i, int _nil_to_sentinel)
+void keeper_toggle_nil_sentinels( lua_State* L, int _val_i, int _nil_to_sentinel)
{
int i, n = lua_gettop( L);
/* We could use an empty table in 'keeper.lua' as the sentinel, but maybe
* checking for a lightuserdata is faster. (any unique value will do -> take the address of some global of ours)
*/
- void *nil_sentinel = &GNbKeepers;
+ void* nil_sentinel = &GNbKeepers;
for( i = _val_i; i <= n; ++ i)
{
if( _nil_to_sentinel)
diff --git a/src/keeper.h b/src/keeper.h
index decae4a..15a5a41 100644
--- a/src/keeper.h
+++ b/src/keeper.h
@@ -13,7 +13,7 @@ struct s_Keeper
// problem: maybe on some platforms (linux) atexit() is called after DLL/so are unloaded...
#define HAVE_KEEPER_ATEXIT_DESINIT 0
-char const* init_keepers( int const _nbKeepers, lua_CFunction _on_state_create);
+char const* init_keepers( lua_State* L, int const _nbKeepers, lua_CFunction _on_state_create);
#if !HAVE_KEEPER_ATEXIT_DESINIT
void close_keepers( void);
#endif // HAVE_KEEPER_ATEXIT_DESINIT
diff --git a/src/lanes.c b/src/lanes.c
index 8a67621..6fec951 100644
--- a/src/lanes.c
+++ b/src/lanes.c
@@ -52,13 +52,13 @@
* ...
*/
-char const* VERSION = "3.4.2";
+char const* VERSION = "3.4.3";
/*
===============================================================================
Copyright (C) 2007-10 Asko Kauppi
- 2011-12 Benoit Germain
+ 2011-13 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
@@ -1114,7 +1114,8 @@ static int run_finalizers( lua_State*L, int lua_rc )
//
// LUA_ERRRUN / LUA_ERRMEM
- if (rc!=0) {
+ if( rc != LUA_OK)
+ {
// [-1]: error message
//
// If one finalizer fails, don't run the others. Return this
@@ -1269,7 +1270,7 @@ static bool_t selfdestruct_remove( struct s_lane *s )
// Initialized by 'init_once_LOCKED()': the deep userdata Linda object
// used for timers (each lane will get a proxy to this)
//
-volatile DEEP_PRELUDE *timer_deep; // = NULL
+volatile DEEP_PRELUDE* timer_deep; // = NULL
/*
* Process end; cancel any still free-running threads
@@ -1349,7 +1350,7 @@ static int selfdestruct_gc( lua_State*L)
t_now = now_secs();
if( n == 0 || ( t_now >= t_until))
{
- DEBUGEXEC(fprintf( stderr, "%d uncancelled lane(s) remain after waiting %fs at process end.\n", n, shutdown_timeout - (t_until - t_now)));
+ DEBUGSPEW_CODE( fprintf( stderr, "%d uncancelled lane(s) remain after waiting %fs at process end.\n", n, shutdown_timeout - (t_until - t_now)));
break;
}
}
@@ -1379,7 +1380,7 @@ static int selfdestruct_gc( lua_State*L)
//we want to free memory and such when we exit.
// 2.0.2: at least timer lane is still here
//
- DEBUGEXEC(fprintf( stderr, "Left %d lane(s) with cancel request at process end.\n", n ));
+ DEBUGSPEW_CODE( fprintf( stderr, "Left %d lane(s) with cancel request at process end.\n", n ));
n=0;
#else
// first thing we did was to raise the linda signals the threads were waiting on (if any)
@@ -1409,7 +1410,7 @@ static int selfdestruct_gc( lua_State*L)
}
MUTEX_UNLOCK( &selfdestruct_cs );
- DEBUGEXEC(fprintf( stderr, "Killed %d lane(s) at process end.\n", n ));
+ DEBUGSPEW_CODE( fprintf( stderr, "Killed %d lane(s) at process end.\n", n));
#endif
}
#if !HAVE_KEEPER_ATEXIT_DESINIT
@@ -1654,6 +1655,39 @@ LUAG_FUNC( set_debug_threadname)
return 0;
}
+#if USE_DEBUG_SPEW
+// can't use direct LUA_x errcode indexing because the sequence is not the same between Lua 5.1 and 5.2 :-(
+// LUA_ERRERR doesn't have the same value
+struct errcode_name
+{
+ int code;
+ char const* name;
+};
+
+static struct errcode_name s_errcodes[] =
+{
+ { LUA_OK, "LUA_OK"},
+ { LUA_YIELD, "LUA_YIELD"},
+ { LUA_ERRRUN, "LUA_ERRRUN"},
+ { LUA_ERRSYNTAX, "LUA_ERRSYNTAX"},
+ { LUA_ERRMEM, "LUA_ERRMEM"},
+ { LUA_ERRGCMM, "LUA_ERRGCMM"},
+ { LUA_ERRERR, "LUA_ERRERR"},
+};
+static char const* get_errcode_name( int _code)
+{
+ int i;
+ for( i = 0; i < 7; ++ i)
+ {
+ if( s_errcodes[i].code == _code)
+ {
+ return s_errcodes[i].name;
+ }
+ }
+ return "";
+}
+#endif // USE_DEBUG_SPEW
+
//---
static THREAD_RETURN_T THREAD_CALLCONV lane_main( void *vs)
{
@@ -1713,7 +1747,8 @@ static THREAD_RETURN_T THREAD_CALLCONV lane_main( void *vs)
// Lua 5.1 error handler is limited to one return value; taking stack trace
// via registry
//
- if (rc!=0) {
+ if( rc != 0)
+ {
STACK_GROW(L,1);
lua_pushlightuserdata( L, STACK_TRACE_KEY );
lua_gettable(L, LUA_REGISTRYINDEX);
@@ -1733,16 +1768,19 @@ static THREAD_RETURN_T THREAD_CALLCONV lane_main( void *vs)
// [2..top]: parameters
//
rc= lua_pcall( L, lua_gettop(L)-1, LUA_MULTRET, 0 /*no error handler*/ );
- // 0: no error
- // LUA_ERRRUN: a runtime error (error pushed on stack)
- // LUA_ERRMEM: memory allocation error
+ // LUA_OK(0): no error
+ // LUA_ERRRUN(2): a runtime error (error pushed on stack)
+ // LUA_ERRMEM(4): memory allocation error
#endif
-//STACK_DUMP(L);
+ DEBUGSPEW_CODE( fprintf( stderr, "Lane %p body: %s\n", L, get_errcode_name( rc)));
+ //STACK_DUMP(L);
// Call finalizers, if the script has set them up.
//
- rc2= run_finalizers(L,rc);
- if (rc2!=0) {
+ rc2 = run_finalizers( L, rc);
+ DEBUGSPEW_CODE( fprintf( stderr, "Lane %p finalizer: %s\n", L, get_errcode_name( rc2)));
+ if( rc2 != LUA_OK)
+ {
// Error within a finalizer!
//
// [-1]: error message
@@ -1766,7 +1804,6 @@ static THREAD_RETURN_T THREAD_CALLCONV lane_main( void *vs)
s->L = L = 0;
lane_cleanup( s);
-
}
else
{
@@ -1809,10 +1846,10 @@ static THREAD_RETURN_T THREAD_CALLCONV lane_main( void *vs)
// helper function to require a module in the keeper states and in the target state
// source state contains module name at the top of the stack
-static void require_one_module( lua_State*L, lua_State*L2, bool_t _fatal)
+static void require_one_module( lua_State* L, lua_State* L2, bool_t _fatal)
{
size_t len;
- char const *name = lua_tolstring( L, -1, &len);
+ char const* name = lua_tolstring( L, -1, &len);
// require the module in the target lane
STACK_GROW( L2, 2);
lua_getglobal( L2, "require");
@@ -1872,39 +1909,11 @@ LUAG_FUNC( thread_new )
ASSERT_L( lua_gettop(L2) == 0);
- // package.path
- STACK_CHECK(L)
- STACK_CHECK(L2)
+ // package
if( package)
{
- if( lua_type( L, package) != LUA_TTABLE)
- {
- return luaL_error( L, "expected package as table, got %s", luaL_typename( L, package));
- }
- lua_getglobal( L2, "package");
- if( !lua_isnil( L2, -1)) // package library not loaded: do nothing
- {
- int i;
- // package.loaders is renamed package.searchers in Lua 5.2
- char const* entries[] = { "path", "cpath", "preload", (LUA_VERSION_NUM == 501) ? "loaders" : "searchers", NULL};
- for( i = 0; entries[i]; ++ i)
- {
- lua_getfield( L, package, entries[i]);
- if( lua_isnil( L, -1))
- {
- lua_pop( L, 1);
- }
- else
- {
- luaG_inter_move( L, L2, 1); // moves the entry to L2
- lua_setfield( L2, -2, entries[i]); // set package[entries[i]]
- }
- }
- }
- lua_pop( L2, 1);
+ luaG_inter_copy_package( L, L2, package);
}
- STACK_END(L2,0)
- STACK_END(L,0)
// modules to require in the target lane *before* the function is transfered!
@@ -1924,14 +1933,14 @@ LUAG_FUNC( thread_new )
{
int nbRequired = 1;
// should not happen, was checked in lanes.lua before calling thread_new()
- if (lua_type(L, required) != LUA_TTABLE)
+ if( lua_type( L, required) != LUA_TTABLE)
{
return luaL_error( L, "expected required module list as a table, got %s", luaL_typename( L, required));
}
lua_pushnil( L);
while( lua_next( L, required) != 0)
{
- if (lua_type(L,-1) != LUA_TSTRING || lua_type(L,-2) != LUA_TNUMBER || lua_tonumber( L, -2) != nbRequired)
+ if( lua_type( L, -1) != LUA_TSTRING || lua_type( L, -2) != LUA_TNUMBER || lua_tonumber( L, -2) != nbRequired)
{
return luaL_error( L, "required module list should be a list of strings");
}
@@ -1949,13 +1958,13 @@ LUAG_FUNC( thread_new )
// 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( glob != 0)
{
STACK_CHECK(L)
STACK_CHECK(L2)
if( !lua_istable( L, glob))
{
- return luaL_error( L, "Expected table, got %s", luaL_typename(L,glob));
+ return luaL_error( L, "Expected table, got %s", luaL_typename( L, glob));
}
lua_pushnil( L);
@@ -1987,7 +1996,7 @@ LUAG_FUNC( thread_new )
}
STACK_MID(L,0)
}
- else if( lua_type(L, 1) == LUA_TSTRING)
+ else if( lua_type( L, 1) == LUA_TSTRING)
{
// compile the string
if( luaL_loadstring( L2, lua_tostring( L, 1)) != 0)
@@ -2089,7 +2098,7 @@ LUAG_FUNC( thread_gc)
// Make sure a kill has proceeded, before cleaning up the data structure.
//
// NO lua_close() in this case because we don't know where execution of the state was interrupted
- DEBUGEXEC(fprintf( stderr, "** Joining with a killed thread (needs testing) **" ));
+ DEBUGSPEW_CODE( fprintf( stderr, "** Joining with a killed thread (needs testing) **"));
// make sure the thread is no longer running, just like thread_join()
if(! THREAD_ISNULL( s->thread))
THREAD_WAIT( &s->thread, -1, &s->done_signal, &s->done_lock, &s->status);
@@ -2100,7 +2109,7 @@ LUAG_FUNC( thread_gc)
lua_close( s->L);
s->L = 0;
}
- DEBUGEXEC(fprintf( stderr, "** Joined ok **" ));
+ DEBUGSPEW_CODE( fprintf( stderr, "** Joined ok **"));
}
else if( s->status < DONE)
{
@@ -2265,7 +2274,7 @@ LUAG_FUNC( thread_join)
break;
default:
- DEBUGEXEC(fprintf( stderr, "Status: %d\n", s->status));
+ DEBUGSPEW_CODE( fprintf( stderr, "Status: %d\n", s->status));
ASSERT_L( FALSE ); ret= 0;
}
lua_close( L2);
@@ -2547,9 +2556,9 @@ static const struct luaL_Reg lanes_functions [] = {
/*
* One-time initializations
*/
-static void init_once_LOCKED( lua_State* L, volatile DEEP_PRELUDE** timer_deep_ref, int const nbKeepers, lua_CFunction _on_state_create, lua_Number _shutdown_timeout, bool_t _track_lanes)
+static void init_once_LOCKED( lua_State* L, int const nbKeepers, lua_CFunction _on_state_create, lua_Number _shutdown_timeout, bool_t _track_lanes)
{
- const char *err;
+ char const* err;
#if (defined PLATFORM_WIN32) || (defined PLATFORM_POCKETPC)
now_secs(); // initialize 'now_secs()' internal offset
@@ -2604,7 +2613,7 @@ static void init_once_LOCKED( lua_State* L, volatile DEEP_PRELUDE** timer_deep_r
}
#endif
#endif
- err = init_keepers( nbKeepers, _on_state_create);
+ err = init_keepers( L, nbKeepers, _on_state_create);
if (err)
{
(void) luaL_error( L, "Unable to initialize: %s", err );
@@ -2612,7 +2621,7 @@ static void init_once_LOCKED( lua_State* L, volatile DEEP_PRELUDE** timer_deep_r
// Initialize 'timer_deep'; a common Linda object shared by all states
//
- ASSERT_L( timer_deep_ref && (!(*timer_deep_ref)) );
+ ASSERT_L( timer_deep == NULL);
STACK_CHECK(L)
{
@@ -2627,8 +2636,8 @@ static void init_once_LOCKED( lua_State* L, volatile DEEP_PRELUDE** timer_deep_r
// Proxy userdata contents is only a 'DEEP_PRELUDE*' pointer
//
- *timer_deep_ref= * (DEEP_PRELUDE**) lua_touserdata( L, -1 );
- ASSERT_L( (*timer_deep_ref) && (*timer_deep_ref)->refcount==1 && (*timer_deep_ref)->deep );
+ timer_deep = * (DEEP_PRELUDE**) lua_touserdata( L, -1);
+ ASSERT_L( timer_deep && (timer_deep->refcount == 1) && timer_deep->deep);
// The host Lua state must always have a reference to this Linda object in order for our 'timer_deep_ref' to be valid.
// So store a reference that we will never actually use.
@@ -2644,8 +2653,8 @@ static void init_once_LOCKED( lua_State* L, volatile DEEP_PRELUDE** timer_deep_r
lua_setfield( L, -2, "__metatable");
lua_setmetatable( L, -2);
}
- lua_insert(L, -2); // Swap key with the Linda object
- lua_rawset(L, LUA_REGISTRYINDEX);
+ lua_insert( L, -2); // Swap key with the Linda object
+ lua_rawset( L, LUA_REGISTRYINDEX);
}
STACK_END(L,0)
@@ -2653,107 +2662,147 @@ static void init_once_LOCKED( lua_State* L, volatile DEEP_PRELUDE** timer_deep_r
static volatile long s_initCount = 0;
-LUAG_FUNC( configure )
+// upvalue 1: module name
+// upvalue 2: module table
+LUAG_FUNC( configure)
{
- char const* name = luaL_checkstring( L, lua_upvalueindex( 1));
- // all parameter checks are done lua-side
- int const nbKeepers = (int)lua_tointeger( L, 1);
- lua_CFunction on_state_create = lua_iscfunction( L, 2) ? lua_tocfunction( L, 2) : NULL;
- lua_Number shutdown_timeout = lua_tonumber( L, 3);
- bool_t track_lanes = lua_toboolean( L, 4);
- /*
- * Making one-time initializations.
- *
- * When the host application is single-threaded (and all threading happens via Lanes)
- * there is no problem. But if the host is multithreaded, we need to lock around the
- * initializations.
- */
+ char const* name = luaL_checkstring( L, lua_upvalueindex( 1));
+ // all parameter checks are done lua-side
+ int const nbKeepers = (int)lua_tointeger( L, 1);
+ lua_CFunction on_state_create = lua_iscfunction( L, 2) ? lua_tocfunction( L, 2) : NULL;
+ lua_Number shutdown_timeout = lua_tonumber( L, 3);
+ bool_t track_lanes = lua_toboolean( L, 4);
+
+ STACK_CHECK( L)
+ // Create main module interface table
+ lua_pushvalue( L, lua_upvalueindex( 2)); // ... M
+ // remove configure() (this function) from the module interface
+ lua_pushnil( L); // ... M nil
+ lua_setfield( L, -2, "configure"); // ... M
+ // add functions to the module's table
+ luaG_registerlibfuncs( L, lanes_functions);
+ STACK_MID( L, 1)
+
+ // metatable for threads
+ // contains keys: { __gc, __index, cached_error, cached_tostring, cancel, join }
+ //
+ lua_newtable( L); // ... M mt
+ lua_pushcfunction( L, LG_thread_gc); // ... M mt LG_thread_gc
+ lua_setfield( L, -2, "__gc"); // ... M mt
+ lua_pushcfunction( L, LG_thread_index); // ... M mt LG_thread_index
+ lua_setfield( L, -2, "__index"); // ... M mt
+ lua_getglobal( L, "error"); // ... M mt error
+ ASSERT_L( lua_isfunction( L, -1));
+ lua_setfield( L, -2, "cached_error"); // ... M mt
+ lua_getglobal( L, "tostring"); // ... M mt tostring
+ ASSERT_L( lua_isfunction( L, -1));
+ lua_setfield( L, -2, "cached_tostring"); // ... M mt
+ lua_pushcfunction( L, LG_thread_join); // ... M mt LG_thread_join
+ lua_setfield( L, -2, "join"); // ... M mt
+ lua_pushcfunction( L, LG_thread_cancel); // ... M mt LG_thread_cancel
+ lua_setfield( L, -2, "cancel"); // ... M mt
+ lua_pushliteral( L, "Lane"); // ... M mt "Lane"
+ lua_setfield( L, -2, "__metatable"); // ... M mt
+
+ lua_pushcclosure( L, LG_thread_new, 1); // ... M LG_thread_new
+ lua_setfield(L, -2, "thread_new"); // ... M
+
+ lua_pushstring(L, VERSION); // ... M VERSION
+ lua_setfield(L, -2, "version"); // ... M
+
+ lua_pushinteger(L, THREAD_PRIO_MAX); // ... M THREAD_PRIO_MAX
+ lua_setfield(L, -2, "max_prio"); // ... M
+
+ lua_pushlightuserdata( L, CANCEL_ERROR); // ... M CANCEL_ERROR
+ lua_setfield(L, -2, "cancel_error"); // ... M
+
+ // register all native functions found in that module in the transferable functions database
+ // we process it before _G because we don't want to find the module when scanning _G (this would generate longer names)
+ // for example in package.loaded.lanes.core.*
+ populate_func_lookup_table( L, -1, name);
+
+ // record all existing C/JIT-fast functions
+ // Lua 5.2 no longer has LUA_GLOBALSINDEX: we must push globals table on the stack
+ lua_pushglobaltable( L); // ... M _G
+ populate_func_lookup_table( L, -1, NULL);
+ lua_pop( L, 1); // ... M
+
+ STACK_MID( L, 1)
+ /*
+ * Making one-time initializations.
+ *
+ * When the host application is single-threaded (and all threading happens via Lanes)
+ * there is no problem. But if the host is multithreaded, we need to lock around the
+ * initializations.
+ *
+ * we must do this after the populate_func_lookup_table is called, else populating the keepers will fail
+ * because this makes a copy of packages.loaders, which requires the lookup tables to exist!
+ */
#if THREADAPI == THREADAPI_WINDOWS
- {
- static volatile int /*bool*/ go_ahead; // = 0
- if( InterlockedCompareExchange( &s_initCount, 1, 0) == 0)
- {
- init_once_LOCKED( L, &timer_deep, nbKeepers, on_state_create, shutdown_timeout, track_lanes);
- go_ahead= 1; // let others pass
- }
- else
- {
- while( !go_ahead ) { Sleep(1); } // changes threads
- }
- }
+ {
+ static volatile int /*bool*/ go_ahead; // = 0
+ if( InterlockedCompareExchange( &s_initCount, 1, 0) == 0)
+ {
+ init_once_LOCKED( L, nbKeepers, on_state_create, shutdown_timeout, track_lanes);
+ go_ahead = 1; // let others pass
+ }
+ else
+ {
+ while( !go_ahead ) { Sleep(1); } // changes threads
+ }
+ }
#else // THREADAPI == THREADAPI_PTHREAD
- if( s_initCount == 0)
- {
- static pthread_mutex_t my_lock= PTHREAD_MUTEX_INITIALIZER;
- pthread_mutex_lock( &my_lock);
- {
- // Recheck now that we're within the lock
- //
- if( s_initCount == 0)
- {
- init_once_LOCKED( L, &timer_deep, nbKeepers, on_state_create, shutdown_timeout, track_lanes);
- s_initCount = 1;
- }
- }
- pthread_mutex_unlock(&my_lock);
- }
+ if( s_initCount == 0)
+ {
+ static pthread_mutex_t my_lock = PTHREAD_MUTEX_INITIALIZER;
+ pthread_mutex_lock( &my_lock);
+ {
+ // Recheck now that we're within the lock
+ //
+ if( s_initCount == 0)
+ {
+ init_once_LOCKED( L, nbKeepers, on_state_create, shutdown_timeout, track_lanes);
+ s_initCount = 1;
+ }
+ }
+ pthread_mutex_unlock( &my_lock);
+ }
#endif // THREADAPI == THREADAPI_PTHREAD
- assert( timer_deep != 0 );
-
- // Create main module interface table
- lua_pushvalue( L, lua_upvalueindex( 2));
- // remove configure() (this function) from the module interface
- lua_pushnil( L);
- lua_setfield( L, -2, "configure");
- // add functions to the module's table
- luaG_registerlibfuncs(L, lanes_functions);
-
- // metatable for threads
- // contains keys: { __gc, __index, cached_error, cached_tostring, cancel, join }
- //
- lua_newtable( L);
- lua_pushcfunction( L, LG_thread_gc);
- lua_setfield( L, -2, "__gc");
- lua_pushcfunction( L, LG_thread_index);
- lua_setfield( L, -2, "__index");
- lua_getglobal( L, "error");
- ASSERT_L( lua_isfunction( L, -1));
- lua_setfield( L, -2, "cached_error");
- lua_getglobal( L, "tostring");
- ASSERT_L( lua_isfunction( L, -1));
- lua_setfield( L, -2, "cached_tostring");
- lua_pushcfunction( L, LG_thread_join);
- lua_setfield( L, -2, "join");
- lua_pushcfunction( L, LG_thread_cancel);
- lua_setfield( L, -2, "cancel");
- lua_pushliteral( L, "Lane");
- lua_setfield( L, -2, "__metatable");
-
- lua_pushcclosure( L, LG_thread_new, 1 ); // metatable as closure param
- lua_setfield(L, -2, "thread_new");
-
- luaG_push_proxy( L, linda_id, (DEEP_PRELUDE *) timer_deep );
- lua_setfield(L, -2, "timer_gateway");
-
- lua_pushstring(L, VERSION);
- lua_setfield(L, -2, "version");
-
- lua_pushinteger(L, THREAD_PRIO_MAX);
- lua_setfield(L, -2, "max_prio");
-
- lua_pushlightuserdata( L, CANCEL_ERROR );
- lua_setfield(L, -2, "cancel_error");
-
- // register all native functions found in that module in the transferable functions database
- // we process it before _G because we don't want to find the module when scanning _G (this would generate longer names)
- populate_func_lookup_table( L, -1, name);
- lua_pop( L, 1);
- // record all existing C/JIT-fast functions
- lua_pushglobaltable( L); // Lua 5.2 no longer has LUA_GLOBALSINDEX: we must push globals table on the stack
- populate_func_lookup_table( L, -1, NULL);
- lua_pop( L, 1); // done with globals table, pop it
- // Return nothing
- return 0;
+ assert( timer_deep != NULL);
+ STACK_MID( L, 1)
+
+ // init_once_LOCKED initializes timer_deep, so we must do this after, of course
+ luaG_push_proxy( L, linda_id, (DEEP_PRELUDE*) timer_deep); // ... M timer_deep
+ lua_setfield( L, -2, "timer_gateway"); // ... M
+
+ lua_pop( L, 1); // ...
+ STACK_END( L, 0)
+ // Return nothing
+ return 0;
+}
+
+// helper to have correct callstacks when crashing a Win32 running on 64 bits Windows
+// don't forget to toggle Debug/Exceptions/Win32 in visual Studio too!
+void EnableCrashingOnCrashes()
+{
+#if 0 && defined PLATFORM_WIN32
+ typedef BOOL (WINAPI *tGetPolicy)(LPDWORD lpFlags);
+ typedef BOOL (WINAPI *tSetPolicy)(DWORD dwFlags);
+ const DWORD EXCEPTION_SWALLOWING = 0x1;
+
+ HMODULE kernel32 = LoadLibraryA("kernel32.dll");
+ tGetPolicy pGetPolicy = (tGetPolicy)GetProcAddress(kernel32, "GetProcessUserModeExceptionPolicy");
+ tSetPolicy pSetPolicy = (tSetPolicy)GetProcAddress(kernel32, "SetProcessUserModeExceptionPolicy");
+ if (pGetPolicy && pSetPolicy)
+ {
+ DWORD dwFlags;
+ if (pGetPolicy(&dwFlags))
+ {
+ // Turn off the filter
+ pSetPolicy(dwFlags & ~EXCEPTION_SWALLOWING);
+ }
+ }
+#endif // PLATFORM_WIN32
}
int
@@ -2762,6 +2811,8 @@ __declspec(dllexport)
#endif // (defined PLATFORM_WIN32) || (defined PLATFORM_POCKETPC)
luaopen_lanes_core( lua_State* L)
{
+ EnableCrashingOnCrashes();
+
STACK_GROW( L, 3);
STACK_CHECK( L)
diff --git a/src/lanes.lua b/src/lanes.lua
index 8c135c2..6bd9e44 100644
--- a/src/lanes.lua
+++ b/src/lanes.lua
@@ -210,10 +210,10 @@ local valid_libs= {
["string"]= true,
["math"]= true,
["debug"]= true,
+ ["bit32"]= true, -- Lua 5.2 only, ignored silently under 5.1
--
["base"]= true,
- ["coroutine"]= true,
- ["*"]= true
+ ["coroutine"]= true
}
-- PUBLIC LANES API
@@ -251,11 +251,19 @@ local function gen( ... )
-- 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 then
- for s in string_gmatch(libs, "[%a*]+") do
+ 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 )
+ 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
diff --git a/src/tools.c b/src/tools.c
index 2629fd3..fe1728d 100644
--- a/src/tools.c
+++ b/src/tools.c
@@ -42,6 +42,8 @@ THE SOFTWARE.
#include
#include
+DEBUGSPEW_CODE( char const* debugspew_indent = "----+----!----+----!----+----!----+----!----+----!----+----!----+----!----+");
+
MUTEX_T deep_lock;
MUTEX_T mtid_lock;
@@ -94,45 +96,52 @@ void luaG_dump( lua_State* L ) {
/*---=== luaG_newstate ===---*/
-static const luaL_Reg libs[] = {
- { LUA_LOADLIBNAME, luaopen_package },
- { LUA_TABLIBNAME, luaopen_table },
- { LUA_IOLIBNAME, luaopen_io },
- { LUA_OSLIBNAME, luaopen_os },
- { LUA_STRLIBNAME, luaopen_string },
- { LUA_MATHLIBNAME, luaopen_math },
- { LUA_DBLIBNAME, luaopen_debug },
- //
- { "base", NULL }, // ignore "base" (already acquired it)
- { "coroutine", NULL }, // part of Lua 5.1 base package
- { NULL, NULL }
+static const luaL_Reg libs[] =
+{
+ { LUA_LOADLIBNAME, luaopen_package},
+ { LUA_TABLIBNAME, luaopen_table},
+ { LUA_IOLIBNAME, luaopen_io},
+ { LUA_OSLIBNAME, luaopen_os},
+ { LUA_STRLIBNAME, luaopen_string},
+ { LUA_MATHLIBNAME, luaopen_math},
+#if LUA_VERSION_NUM >= 502
+ { LUA_BITLIBNAME, luaopen_bit32},
+#endif // LUA_VERSION_NUM
+ { LUA_DBLIBNAME, luaopen_debug},
+ //
+ { "base", NULL }, // ignore "base" (already acquired it)
+ { LUA_COLIBNAME, NULL }, // part of Lua 5.[1|2] base package
+ { NULL, NULL }
};
-static bool_t openlib( lua_State *L, const char *name, size_t len ) {
-
- unsigned i;
- bool_t all= strncmp( name, "*", len ) == 0;
-
- for( i=0; libs[i].name; i++ )
+static void open1lib( lua_State* L, char const* name, size_t len)
+{
+ int i;
+ for( i = 0; libs[i].name; ++ i)
{
- if (all || (strncmp(name, libs[i].name, len) ==0))
+ if( strncmp( name, libs[i].name, len) == 0)
{
- if (libs[i].func)
+ if( libs[i].func)
{
- STACK_GROW(L,1);
- STACK_CHECK(L)
+ DEBUGSPEW_CODE( fprintf( stderr, "opening %.*s library\n", len, name));
+ STACK_GROW( L, 1);
+ STACK_CHECK( L)
lua_pushcfunction( L, libs[i].func);
// pushes the module table on the stack
lua_call( L, 0, 1);
populate_func_lookup_table( L, -1, libs[i].name);
- // remove the module when we are done
+#if LUA_VERSION_NUM >= 502
+ // Lua 5.2: luaopen_x doesn't create the global, we have to do it ourselves!
+ lua_setglobal( L, libs[i].name);
+#else // LUA_VERSION_NUM
+ // Lua 5.1: remove the module when we are done
lua_pop( L, 1);
- STACK_END(L, 0)
+#endif // LUA_VERSION_NUM
+ STACK_END( L, 0)
}
- if (!all) return TRUE;
+ break;
}
}
- return all;
}
static int dummy_writer(lua_State *L, const void* p, size_t sz, void* ud)
@@ -323,6 +332,7 @@ static void populate_func_lookup_table_recur( lua_State* L, int _ctx_base, int _
// remove table name from fqn stack
lua_pushnil( L); // ... {_i} {bfc} k nil
lua_rawseti( L, fqn, _depth); // ... {_i} {bfc} k
+ DEBUGSPEW_CODE( fprintf( stderr, "%.*spopulating: %s\n", _i, debugspew_indent, newName));
-- _depth;
}
else
@@ -372,12 +382,12 @@ static void populate_func_lookup_table_recur( lua_State* L, int _ctx_base, int _
/*
* create a "fully.qualified.name" <-> function equivalence database
*/
-void populate_func_lookup_table( lua_State *L, int _i, char const *_name)
+void populate_func_lookup_table( lua_State* L, int _i, char const* _name)
{
int const ctx_base = lua_gettop( L) + 1;
int const in_base = lua_absindex( L, _i);
int const start_depth = _name ? 1 : 0;
- //printf( "%p: populate_func_lookup_table('%s')\n", L, _name ? _name : "NULL");
+ DEBUGSPEW_CODE( fprintf( stderr, "%p: populate_func_lookup_table('%s')\n", L, _name ? _name : "NULL"));
STACK_GROW( L, 3);
STACK_CHECK( L)
lua_getfield( L, LUA_REGISTRYINDEX, LOOKUP_KEY); // {}?
@@ -423,8 +433,6 @@ void populate_func_lookup_table( lua_State *L, int _i, char const *_name)
lua_State* luaG_newstate( char const* libs, lua_CFunction _on_state_create)
{
- char const* p;
- unsigned int len;
lua_State* const L = luaL_newstate();
// no libs, or special init func (not even 'base')
@@ -447,31 +455,43 @@ lua_State* luaG_newstate( char const* libs, lua_CFunction _on_state_create)
{
if( libs[0] == '*' && libs[1] == 0) // special "*" case (mainly to help with LuaJIT compatibility)
{
+ DEBUGSPEW_CODE( fprintf( stderr, "opening ALL base libraries\n"));
luaL_openlibs( L);
libs = NULL; // done with libs
}
else
{
+ DEBUGSPEW_CODE( fprintf( stderr, "opening base library\n"));
lua_pushcfunction( L, luaopen_base);
lua_call( L, 0, 0);
}
}
+
// after opening base, register the functions it exported in our name<->function database
lua_pushglobaltable( L); // Lua 5.2 no longer has LUA_GLOBALSINDEX: we must push globals table on the stack
populate_func_lookup_table( L, -1, NULL);
lua_pop( L, 1);
+
STACK_MID( L, 0);
- if( libs)
{
- for( p = libs; *p; p += len)
+ char const* p;
+ unsigned int len = 0;
+ if( libs)
{
- len=0;
- while (*p && !is_name_char(*p)) p++; // bypass delimiters
- while (is_name_char(p[len])) len++; // bypass name
- if (len && (!openlib( L, p, len )))
- break;
+ for( p = libs; *p; p += len)
+ {
+ len = 0;
+ // skip delimiters
+ while( *p && !is_name_char( *p))
+ ++ p;
+ // skip name
+ while( is_name_char( p[len]))
+ ++ len;
+ // open library
+ open1lib( L, p, len);
+ }
+ serialize_require( L);
}
- serialize_require( L);
}
STACK_END(L,0)
lua_gc( L, LUA_GCRESTART, 0);
@@ -1126,9 +1146,9 @@ static bool_t push_cached_table( lua_State *L2, uint_t L2_cache_i, lua_State *L,
*
* Always pushes a function to 'L2'.
*/
-static void inter_copy_func( lua_State *L2, uint_t L2_cache_i, lua_State *L, uint_t i );
+static void inter_copy_func( lua_State* L2, uint_t L2_cache_i, lua_State* L, uint_t i);
-static void push_cached_func( lua_State *L2, uint_t L2_cache_i, lua_State *L, uint_t i )
+static void push_cached_func( lua_State* L2, uint_t L2_cache_i, lua_State* L, uint_t i)
{
void * const aspointer = (void*)lua_topointer( L, i);
// TBD: Merge this and same code for tables
@@ -1228,6 +1248,27 @@ int discover_object_name_recur( lua_State* L, int _shortest, int _length)
else if( lua_istable( L, -1))
{
_shortest = discover_object_name_recur( L, _shortest, _length);
+ // search in the table's metatable too
+ if( lua_getmetatable( L, -1))
+ {
+ if( lua_istable( L, -1))
+ {
+ _shortest = discover_object_name_recur( L, _shortest, _length);
+ }
+ lua_pop( L, 1);
+ }
+ }
+ else if( lua_isuserdata( L, -1))
+ {
+ // search in the object's metatable (some modules are built that way)
+ if( lua_getmetatable( L, -1))
+ {
+ if( lua_istable( L, -1))
+ {
+ _shortest = discover_object_name_recur( L, _shortest, _length);
+ }
+ lua_pop( L, 1);
+ }
}
// make ready for next iteration
lua_pop( L, 1); // o "r" {c} {fqn} ... {?} k
@@ -1298,9 +1339,10 @@ static void lookup_native_func( lua_State* L2, lua_State* L, uint_t i)
if( !fqn)
{
char const* from;
- lua_pushvalue( L, i); // ... f ... f
// try to discover the name of the function we want to send
- luaG_nameof( L); // ... f ... "type" "name"
+ lua_pushcfunction( L, luaG_nameof); // ... f ...luaG_nameof
+ lua_pushvalue( L, i); // ... f ... luaG_nameof f
+ lua_call( L, 1, 2); // ... f ... "type" "name"
lua_getglobal( L, "decoda_name"); // ... f ... "type" "name" decoda_name
from = lua_tostring( L, -1);
(void) luaL_error( L, "%s '%s' not found in %s origin transfer database.", lua_tostring( L, -3), lua_tostring( L, -2), from ? from : "main");
@@ -1327,15 +1369,6 @@ static void lookup_native_func( lua_State* L2, lua_State* L, uint_t i)
STACK_END( L2, 1)
}
-#define LOG_FUNC_INFO 0
-#if LOG_FUNC_INFO
-#define LOG_FUNC_INFO_CODE(_code) _code
-#else // LOG_FUNC_INFO
-#define LOG_FUNC_INFO_CODE(_code)
-#endif // LOG_FUNC_INFO
-
-LOG_FUNC_INFO_CODE( static char const* s_indent = "----+----!----+----!----+----!----+----!----+----!----+----!----+----!----+");
-
/*
* Copy a function over, which has not been found in the cache.
* L2 has the cache key for this function at the top of the stack
@@ -1350,7 +1383,7 @@ static void inter_copy_func( lua_State* L2, uint_t L2_cache_i, lua_State* L, uin
FuncSubType funcSubType;
lua_CFunction cfunc = luaG_tocfunction( L, i, &funcSubType); // NULL for LuaJIT-fast && bytecode functions
- ASSERT_L( L2_cache_i != 0); // ... {cache} ... p
+ ASSERT_L( L2_cache_i != 0); // ... {cache} ... p
STACK_GROW(L,2);
STACK_CHECK(L)
@@ -1362,7 +1395,7 @@ static void inter_copy_func( lua_State* L2, uint_t L2_cache_i, lua_State* L, uin
// if already on top of the stack, no need to push again
int needToPush = (i != (uint_t)lua_gettop( L));
if( needToPush)
- lua_pushvalue( L, i);
+ lua_pushvalue( L, i); // ... f
luaL_buffinit( L, &b);
//
@@ -1374,12 +1407,13 @@ static void inter_copy_func( lua_State* L2, uint_t L2_cache_i, lua_State* L, uin
luaL_error( L, "internal error: function dump failed.");
}
- luaL_pushresult( &b); // pushes dumped string on 'L'
+ // pushes dumped string on 'L'
+ luaL_pushresult( &b); // ... f b
// if not pushed, no need to pop
if( needToPush)
{
- lua_remove( L, -2);
+ lua_remove( L, -2); // ... b
}
// transfer the bytecode, then the upvalues, to create a similar closure
@@ -1392,15 +1426,16 @@ static void inter_copy_func( lua_State* L2, uint_t L2_cache_i, lua_State* L, uin
//
{
lua_Debug ar;
- lua_pushvalue( L, i);
- lua_getinfo(L, ">nS", &ar); // fills 'name' 'namewhat' and 'linedefined', pops function
+ lua_pushvalue( L, i); // ... b f
+ // fills 'name' 'namewhat' and 'linedefined', pops function
+ lua_getinfo(L, ">nS", &ar); // ... b
name = ar.namewhat;
fprintf( stderr, "%.*sFNAME: %s @ %d\n", i, s_indent, ar.short_src, ar.linedefined); // just gives NULL
}
#endif // LOG_FUNC_INFO
{
size_t sz;
- char const* s = lua_tolstring( L, -1, &sz);
+ char const* s = lua_tolstring( L, -1, &sz); // ... b
ASSERT_L( s && sz);
STACK_GROW( L2, 2);
// Note: Line numbers seem to be taken precisely from the
@@ -1409,7 +1444,7 @@ static void inter_copy_func( lua_State* L2, uint_t L2_cache_i, lua_State* L, uin
//
// TBD: Can we get the function's original name through, as well?
//
- if( luaL_loadbuffer( L2, s, sz, name) != 0) // ... {cache} ... p function
+ if( luaL_loadbuffer( L2, s, sz, name) != 0) // ... {cache} ... p function
{
// chunk is precompiled so only LUA_ERRMEM can happen
// "Otherwise, it pushes an error message"
@@ -1417,38 +1452,50 @@ static void inter_copy_func( lua_State* L2, uint_t L2_cache_i, lua_State* L, uin
STACK_GROW( L, 1);
luaL_error( L, "%s", lua_tostring( L2, -1));
}
- lua_pop( L, 1); // remove the dumped string
+ // remove the dumped string
+ lua_pop( L, 1); // ...
// now set the cache as soon as we can.
// this is necessary if one of the function's upvalues references it indirectly
// we need to find it in the cache even if it isn't fully transfered yet
- lua_insert( L2, -2); // ... {cache} ... function p
- lua_pushvalue( L2, -2); // ... {cache} ... function p function
+ lua_insert( L2, -2); // ... {cache} ... function p
+ lua_pushvalue( L2, -2); // ... {cache} ... function p function
// cache[p] = function
- lua_rawset( L2, L2_cache_i); // ... {cache} ... function
+ lua_rawset( L2, L2_cache_i); // ... {cache} ... function
}
STACK_MID( L, 0)
/* push over any upvalues; references to this function will come from
* cache so we don't end up in eternal loop.
+ * Lua5.2: one of the upvalues is _ENV, which we don't want to copy!
+ * instead, the function shall have LUA_RIDX_GLOBALS taken in the destination state!
*/
{
- LOG_FUNC_INFO_CODE( char const* upname);
- for( n = 0; (LOG_FUNC_INFO_CODE( upname =) lua_getupvalue( L, i, 1 + n)) != NULL; ++ n)
- {
- LOG_FUNC_INFO_CODE( fprintf( stderr, "%.*sUPNAME[%d]: %s\n", i, s_indent, n, upname));
- // v 3.4.2: we now longer need to handle this special case, because the general mechanism can take care of it just fine
- /*if( (!cfunc) && lua_equal( L, i, -1))
+ DEBUGSPEW_CODE( char const* upname);
+#if LUA_VERSION_NUM == 502
+ // With Lua 5.2, each Lua function gets its environment as one of its upvalues (named LUA_ENV, aka "_ENV" by default)
+ // Generally this is LUA_RIDX_GLOBALS, which we don't want to copy from the source to the destination state...
+ // -> if we encounter an upvalue equal to the global table in the source, bind it to the destination's global table
+ lua_pushglobaltable( L); // ... _G
+#endif // LUA_VERSION_NUM
+ for( n = 0; (DEBUGSPEW_CODE( upname =) lua_getupvalue( L, i, 1 + n)) != NULL; ++ n)
+ { // ... _G up[n]
+ DEBUGSPEW_CODE( fprintf( stderr, "%.*sUPNAME[%d]: %s\n", i, debugspew_indent, n, upname));
+#if LUA_VERSION_NUM == 502
+ if( lua_rawequal( L, -1, -2)) // is the upvalue equal to the global table?
{
- // Lua closure that has a (recursive) upvalue to itself
- lua_pushvalue( L2, -n - 1); // ... {cache} ... function upvalues...
+ lua_pushglobaltable( L2); // ... {cache} ... function
}
- else*/
+ else
+#endif // LUA_VERSION_NUM
{
- if( !inter_copy_one_( L2, L2_cache_i, L, lua_gettop( L), VT_NORMAL))
+ if( !inter_copy_one_( L2, L2_cache_i, L, lua_gettop( L), VT_NORMAL)) // ... {cache} ... function
luaL_error( L, "Cannot copy upvalue type '%s'", luaL_typename( L, -1));
}
- lua_pop( L, 1);
+ lua_pop( L, 1); // ... _G
}
+#if LUA_VERSION_NUM == 502
+ lua_pop( L, 1); // ...
+#endif // LUA_VERSION_NUM
}
// L2: function + 'n' upvalues (>=0)
@@ -1459,7 +1506,7 @@ static void inter_copy_func( lua_State* L2, uint_t L2_cache_i, lua_State* L, uin
int func_index = lua_gettop( L2) - n;
for( ; n > 0; -- n)
{
- char const* rc = lua_setupvalue( L2, func_index, n); // ... {cache} ... function
+ char const* rc = lua_setupvalue( L2, func_index, n); // ... {cache} ... function
//
// "assigns the value at the top of the stack to the upvalue and returns its name.
// It also pops the value from the stack."
@@ -1467,16 +1514,16 @@ static void inter_copy_func( lua_State* L2, uint_t L2_cache_i, lua_State* L, uin
ASSERT_L( rc); // not having enough slots?
}
// once all upvalues have been set we are left
- // with the function at the top of the stack // ... {cache} ... function
+ // with the function at the top of the stack // ... {cache} ... function
}
}
}
else // C function OR LuaJIT fast function!!!
{
- lua_pop( L2, 1); // ... {cache} ...
- LOG_FUNC_INFO_CODE( fprintf( stderr, "%.*sFNAME: [C] function %p \n", i, s_indent, cfunc));
+ lua_pop( L2, 1); // ... {cache} ...
+ DEBUGSPEW_CODE( fprintf( stderr, "%.*sFNAME: [C] function %p \n", i, debugspew_indent, cfunc));
// No need to transfer upvalues for C/JIT functions since they weren't actually copied, only looked up
- lookup_native_func( L2, L, i); // ... {cache} ... function
+ lookup_native_func( L2, L, i); // ... {cache} ... function
}
STACK_END( L, 0)
}
@@ -1518,8 +1565,8 @@ static bool_t inter_copy_one_( lua_State *L2, uint_t L2_cache_i, lua_State *L, u
break;
case LUA_TSTRING: {
- size_t len; const char *s = lua_tolstring( L, i, &len );
- LOG_FUNC_INFO_CODE( if( vt == VT_KEY) fprintf( stderr, "%.*sKEY: %s\n", i, s_indent, s));
+ size_t len; const char* s = lua_tolstring( L, i, &len);
+ DEBUGSPEW_CODE( if( vt == VT_KEY) fprintf( stderr, "%.*sKEY: %s\n", i, debugspew_indent, s));
lua_pushlstring( L2, s, len );
} break;
@@ -1735,7 +1782,7 @@ int luaG_inter_copy( lua_State* L, lua_State *L2, uint_t n)
}
/*
- * Remove the cache table. Persistant caching would cause i.e. multiple
+ * Remove the cache table. Persistent caching would cause i.e. multiple
* messages passed in the same table to use the same table also in receiving
* end.
*/
@@ -1755,13 +1802,49 @@ int luaG_inter_copy( lua_State* L, lua_State *L2, uint_t n)
}
-int luaG_inter_move( lua_State* L, lua_State *L2, uint_t n )
+int luaG_inter_move( lua_State* L, lua_State* L2, uint_t n)
{
int ret = luaG_inter_copy( L, L2, n);
lua_pop( L, (int) n);
return ret;
}
+void luaG_inter_copy_package( lua_State* L, lua_State* L2, int _idx)
+{
+ // package
+ STACK_CHECK( L)
+ STACK_CHECK( L2)
+ _idx = lua_absindex( L, _idx);
+ if( lua_type( L, _idx) != LUA_TTABLE)
+ {
+ (void) luaL_error( L, "expected package as table, got %s", luaL_typename( L, _idx));
+ }
+ lua_getglobal( L2, "package");
+ if( !lua_isnil( L2, -1)) // package library not loaded: do nothing
+ {
+ int i;
+ // package.loaders is renamed package.searchers in Lua 5.2
+ char const* entries[] = { "path", "cpath", "preload", (LUA_VERSION_NUM == 501) ? "loaders" : "searchers", NULL};
+ for( i = 0; entries[i]; ++ i)
+ {
+ lua_getfield( L, _idx, entries[i]);
+ if( lua_isnil( L, -1))
+ {
+ lua_pop( L, 1);
+ }
+ else
+ {
+ luaG_inter_move( L, L2, 1); // moves the entry to L2
+ lua_setfield( L2, -2, entries[i]); // set package[entries[i]]
+ }
+ }
+ }
+ lua_pop( L2, 1);
+ STACK_END( L2, 0)
+ STACK_END( L, 0)
+}
+
+
/*---=== Serialize require ===---
*/
diff --git a/src/tools.h b/src/tools.h
index 4a77a6a..d0169cf 100644
--- a/src/tools.h
+++ b/src/tools.h
@@ -25,6 +25,8 @@
#define lua_getuservalue lua_getfenv
#define lua_rawlen lua_objlen
#define luaG_registerlibfuncs( L, _funcs) luaL_register( L, NULL, _funcs)
+#define LUA_OK 0
+#define LUA_ERRGCMM 666 // doesn't exist in Lua 5.1, we don't care about the actual value
#endif // LUA_VERSION_NUM == 501
// wrap Lua 5.2 calls under Lua 5.1 API when it is simpler that way
@@ -33,14 +35,21 @@
#define luaG_registerlibfuncs( L, _funcs) luaL_setfuncs( L, _funcs, 0)
#endif // LUA_VERSION_NUM == 502
+#define USE_DEBUG_SPEW 0
+#if USE_DEBUG_SPEW
+extern char const* debugspew_indent;
+#define DEBUGSPEW_CODE(_code) _code
+#else // USE_DEBUG_SPEW
+#define DEBUGSPEW_CODE(_code)
+#endif // USE_DEBUG_SPEW
+
+
#ifdef NDEBUG
#define _ASSERT_L(lua,c) /*nothing*/
#define STACK_CHECK(L) /*nothing*/
#define STACK_MID(L,c) /*nothing*/
#define STACK_END(L,c) /*nothing*/
#define STACK_DUMP(L) /*nothing*/
- #define DEBUG() /*nothing*/
- #define DEBUGEXEC(_code) {} /*nothing*/
#else
#define _ASSERT_L(lua,c) do { if (!(c)) luaL_error( lua, "ASSERT failed: %s:%d '%s'", __FILE__, __LINE__, #c ); } while( 0)
//
@@ -50,8 +59,6 @@
#define STACK_END(L,change) STACK_MID(L,change) }
#define STACK_DUMP(L) luaG_dump(L);
- #define DEBUG() fprintf( stderr, "<<%s %d>>\n", __FILE__, __LINE__ );
- #define DEBUGEXEC(_code) {_code;} /*nothing*/
#endif
#define ASSERT_L(c) _ASSERT_L(L,c)
@@ -73,7 +80,8 @@ typedef struct {
void *deep;
} DEEP_PRELUDE;
-void luaG_push_proxy( lua_State *L, luaG_IdFunction idfunc, DEEP_PRELUDE *deep_userdata );
+void luaG_push_proxy( lua_State *L, luaG_IdFunction idfunc, DEEP_PRELUDE *deep_userdata);
+void luaG_inter_copy_package( lua_State* L, lua_State* L2, int _idx);
int luaG_inter_copy( lua_State *L, lua_State *L2, uint_t n);
int luaG_inter_move( lua_State *L, lua_State *L2, uint_t n);
@@ -85,7 +93,7 @@ int luaG_nameof( lua_State* L);
extern MUTEX_T deep_lock;
extern MUTEX_T mtid_lock;
-void populate_func_lookup_table( lua_State *L, int _i, char const *_name);
+void populate_func_lookup_table( lua_State* L, int _i, char const* _name);
void serialize_require( lua_State *L);
extern MUTEX_T require_cs;
diff --git a/tests/basic.lua b/tests/basic.lua
index e913906..c35f16a 100644
--- a/tests/basic.lua
+++ b/tests/basic.lua
@@ -7,8 +7,7 @@
-- - ...
--
-local lanes = require "lanes"
-lanes.configure()
+local lanes = require "lanes".configure{ with_timers = false}
require "assert" -- assert.fails()
local lanes_gen= assert( lanes.gen )
@@ -49,7 +48,7 @@ local function subtable( a, b )
return true -- is a subtable
end
--- true when contents of 'a' and 'b' are identific
+-- true when contents of 'a' and 'b' are identical
--
tables_match= function( a, b )
return subtable( a, b ) and subtable( b, a )
@@ -215,7 +214,7 @@ assert( type(linda) == "userdata" )
local function PEEK() return linda:get("<-") end
local function SEND(...) linda:send( "->", ... ) end
-local function RECEIVE() local k,v = linda:receive( "<-" ) return v end
+local function RECEIVE() local k,v = linda:receive( 1, "<-" ) return v end
local t= lanes_gen("io",chunk)(linda) -- prepare & launch
@@ -228,7 +227,14 @@ end
SEND(3); WR( "3 sent\n" )
local a,b,c= RECEIVE(), RECEIVE(), RECEIVE()
- WR( a..", "..b..", "..c.." received\n" )
+
+print( "lane status: " .. t.status)
+if t.status == "error" then
+ print( t:join())
+else
+ WR( a..", "..b..", "..c.." received\n" )
+end
+
assert( a==1 and b==2 and c==3 )
local a= RECEIVE(); WR( a.." received\n" )
@@ -299,7 +305,7 @@ local function chunk2( linda )
--
for k,v in pairs(info) do PRINT(k,v) end
- assert( info.nups == 2 ) -- one upvalue + PRINT
+ assert( info.nups == (_VERSION == "Lua 5.1" and 2 or 3) ) -- one upvalue + PRINT + _ENV (Lua 5.2 only)
assert( info.what == "Lua" )
--assert( info.name == "chunk2" ) -- name does not seem to come through
assert( string.match( info.source, "^@.*basic.lua$" ) )
@@ -327,7 +333,10 @@ linda:send( "down", function(linda) linda:send( "up", "ready!" ) end,
"ok" )
-- wait to see if the tiny function gets executed
--
-local k,s= linda:receive( "up" )
+local k,s= linda:receive( 1, "up" )
+if t2.status == "error" then
+ print( "t2 error: " , t2:join())
+end
PRINT(s)
assert( s=="ready!" )
@@ -355,16 +364,21 @@ local S= lanes_gen( "table",
for i, v in ipairs(arg) do
table.insert (aux, 1, v)
end
- return unpack(aux)
+ -- unpack was renamed table.unpack in Lua 5.2: cater for both!
+ return (unpack or table.unpack)(aux)
end )
h= S { 12, 13, 14 } -- execution starts, h[1..3] will get the return values
local a,b,c,d= h:join()
-assert(a==14)
-assert(b==13)
-assert(c==12)
-assert(d==nil)
+if h.status == "error" then
+ print( "h error: " , a, b, c, d)
+else
+ assert(a==14)
+ assert(b==13)
+ assert(c==12)
+ assert(d==nil)
+end
--
io.stderr:write "Done! :)\n"
diff --git a/tests/keeper.lua b/tests/keeper.lua
index 40c9e11..73ed3cf 100644
--- a/tests/keeper.lua
+++ b/tests/keeper.lua
@@ -4,8 +4,7 @@
-- Test program for Lua Lanes
--
-local lanes = require "lanes"
-lanes.configure()
+local lanes = require "lanes".configure{ with_timers = false}
local function keeper(linda)
local mt= {
diff --git a/tests/linda_perf.lua b/tests/linda_perf.lua
index 87e0da7..be582ce 100644
--- a/tests/linda_perf.lua
+++ b/tests/linda_perf.lua
@@ -1,6 +1,9 @@
local lanes = require "lanes"
lanes.configure()
+-- Lua 5.1/5.2 compatibility
+local table_unpack = unpack or table.unpack
+
-- this lane eats items in the linda one by one
local eater = function( l, loop)
local key, val = l:receive( "go")
@@ -131,7 +134,7 @@ local function ziva2( preloop, loop, batch)
end
-- create a function that can send several values in one shot
batch_send = function()
- l:send( "key", unpack( batch_values))
+ l:send( "key", table_unpack( batch_values))
end
batch_read = function()
l:receive( l.batched, "key", batch)
--
cgit v1.2.3-55-g6feb