From 5db999e79a1dd28365231c449840236b204a1ca9 Mon Sep 17 00:00:00 2001
From: Benoit Germain <bnt period germain arrobase gmail period com>
Date: Thu, 20 Mar 2014 16:49:15 +0100
Subject: Fixed error handling when handler isn't called

* bumped version to 3.9.4
* set_finalizer throws an error if provided finalizer isn't a function
* fix error handling when the error doesn't generate an error handler
call (IOW, all errors but LUA_ERRRUN)
* provide callstack if LUA_ERRRUN occurs inside a finalizer
---
 CHANGES         |   6 +
 docs/index.html |   6 +-
 src/lanes.c     | 413 ++++++++++++++++++++++++++++----------------------------
 3 files changed, 218 insertions(+), 207 deletions(-)

diff --git a/CHANGES b/CHANGES
index 7bda6f3..3a7ff89 100644
--- a/CHANGES
+++ b/CHANGES
@@ -1,5 +1,11 @@
 CHANGES:
 
+CHANGE 108: BGe 20-Mar-14
+   * bumped version to 3.9.4
+   * set_finalizer throws an error if provided finalizer isn't a function
+   * fix error handling when the error doesn't generate an error handler call (IOW, all errors but LUA_ERRRUN)
+   * provide callstack if LUA_ERRRUN occurs inside a finalizer
+
 CHANGE 107: BGe 19-Mar-14
    * Make sure we don't mutex-wrap require() more than once, just in case
 
diff --git a/docs/index.html b/docs/index.html
index c4a6441..da5da71 100644
--- a/docs/index.html
+++ b/docs/index.html
@@ -70,7 +70,7 @@
 			</p>
 
 			<p>
-				This document was revised on 27-Feb-14, and applies to version <tt>3.9.3</tt>.
+				This document was revised on 20-Mar-14, and applies to version <tt>3.9.4</tt>.
 			</p>
 		</font>
 	</center>
@@ -252,6 +252,10 @@
 <p>
 	At the same time, <tt>configure()</tt> itself will be replaced by another function that raises an error if called again with differing arguments, if any.
 </p>
+<p>
+	Also, once Lanes is initialized, <tt>require()</tt> is replaced by another one that wraps it inside a mutex, both in the main state and in all created lanes. This prevents multiple thread-unsafe module initializations from several lanes to occur simultaneously.
+	It remains to be seen whether this is actually useful or not: If a module is already threadsafe, protecting its initialization isn't useful. And if it is not, any parallel operation may crash without Lanes being able to do anything about it.
+</p>
 <p>
 	<b>IMPORTANT NOTE:</b> Starting with version 3.7.0, only the first occurence of <tt>require "lanes"</tt> must be followed by a call to <tt>.configure()</tt>. From this point, a simple <tt>require "lanes"</tt> will do wherever you need to require lanes again.
 </p>
diff --git a/src/lanes.c b/src/lanes.c
index 6b3264a..9e79f64 100644
--- a/src/lanes.c
+++ b/src/lanes.c
@@ -52,7 +52,7 @@
  *      ...
  */
 
-char const* VERSION = "3.9.3";
+char const* VERSION = "3.9.4";
 
 /*
 ===============================================================================
@@ -293,26 +293,29 @@ struct s_Linda;
 * Returns: TRUE if a table was pushed
 *          FALSE if no table found, not created, and nothing pushed
 */
-static bool_t push_registry_table( lua_State*L, void *key, bool_t create ) {
-
-    STACK_GROW(L,3);
-    
-    lua_pushlightuserdata( L, key );
-    lua_gettable( L, LUA_REGISTRYINDEX );
-    
-    if (lua_isnil(L,-1)) {
-        lua_pop(L,1);
-
-        if (!create) return FALSE;  // nothing pushed
-
-        lua_newtable(L);
-        lua_pushlightuserdata( L, key );
-        lua_pushvalue(L,-2);    // duplicate of the table
-        lua_settable( L, LUA_REGISTRYINDEX );
-        
-        // [-1]: table that's also bound in registry
-    }
-    return TRUE;    // table pushed
+static bool_t push_registry_table( lua_State* L, void* key, bool_t create)
+{
+	STACK_GROW( L, 3);
+	STACK_CHECK( L);
+	lua_pushlightuserdata( L, key);                                              // key
+	lua_gettable( L, LUA_REGISTRYINDEX);                                         // t?
+
+	if( lua_isnil( L, -1))                                                       // nil?
+	{
+		lua_pop( L, 1);                                                            //
+
+		if( !create)
+		{
+			return FALSE;
+		}
+
+		lua_newtable(L);                                                           // t
+		lua_pushlightuserdata( L, key);                                            // t key
+		lua_pushvalue( L, -2);                                                     // t key t
+		lua_rawset( L, LUA_REGISTRYINDEX);                                         // t
+	}
+	STACK_END( L, 1);
+	return TRUE;    // table pushed
 }
 
 #if HAVE_LANE_TRACKING
@@ -1281,20 +1284,18 @@ LUAG_FUNC( linda)
 // Add a function that will be called when exiting the lane, either via
 // normal return or an error.
 //
-LUAG_FUNC( set_finalizer )
+LUAG_FUNC( set_finalizer)
 {
-    STACK_GROW(L,3);
-    
-    // Get the current finalizer table (if any)
-    //
-    push_registry_table( L, FINALIZER_REG_KEY, TRUE /*do create if none*/ );
-
-    lua_pushinteger( L, lua_rawlen(L,-1)+1 );
-    lua_pushvalue( L, 1 );  // copy of the function
-    lua_settable( L, -3 );
-    
-    lua_pop(L,1);
-    return 0;
+	luaL_argcheck( L, lua_isfunction( L, 1), 1, "finalizer should be a function");
+	luaL_argcheck( L, lua_gettop( L) == 1, 1, "too many arguments");
+	// Get the current finalizer table (if any)
+	push_registry_table( L, FINALIZER_REG_KEY, TRUE /*do create if none*/);      // finalizer {finalisers}
+	STACK_GROW( L, 2);
+	lua_pushinteger( L, lua_rawlen( L, -1) + 1);                                 // finalizer {finalisers} idx
+	lua_pushvalue( L, 1);                                                        // finalizer {finalisers} idx finalizer
+	lua_rawset( L, -3);                                                          // finalizer {finalisers}
+	lua_pop( L, 2);                                                              //
+	return 0;
 }
 
 
@@ -1311,14 +1312,15 @@ LUAG_FUNC( set_finalizer )
 //
 // TBD: should we add stack trace on failing finalizer, wouldn't be hard..
 //
+static void push_stack_trace( lua_State* L, int rc_, int stk_base_);
+
 static int run_finalizers( lua_State* L, int lua_rc)
 {
-	int error_index, finalizers_index;
+	int finalizers_index;
 	int n;
 	int err_handler_index = 0;
-	int rc = 0;                                                                     // [err_msg {stack_trace}]?
-
-	if( !push_registry_table( L, FINALIZER_REG_KEY, FALSE))                         // [err_msg {stack_trace}]? {func [, ...]}?
+	int rc = LUA_OK;                                                                // ...
+	if( !push_registry_table( L, FINALIZER_REG_KEY, FALSE))                         // ... finalizers?
 	{
 		return 0;   // no finalizers
 	}
@@ -1328,51 +1330,58 @@ static int run_finalizers( lua_State* L, int lua_rc)
 	finalizers_index = lua_gettop( L);
 
 #if ERROR_FULL_STACK
-	lua_pushcfunction( L, lane_error);                                              // [err_msg {stack_trace}]? {func [, ...]}? lane_error
+	lua_pushcfunction( L, lane_error);                                              // ... finalizers lane_error
 	err_handler_index = lua_gettop( L);
 #endif // ERROR_FULL_STACK
-	error_index = (lua_rc != LUA_OK) ? finalizers_index - (1 + ERROR_FULL_STACK) : 0;
 
 	for( n = (int) lua_rawlen( L, finalizers_index); n > 0; -- n)
 	{
 		int args = 0;
-		lua_pushinteger( L, n);                                                       // [err_msg {stack_trace}]? {func [, ...]}? lane_error n
-		lua_gettable( L, finalizers_index);                                           // [err_msg {stack_trace}]? {func [, ...]}? lane_error finalizer
+		lua_pushinteger( L, n);                                                       // ... finalizers lane_error n
+		lua_rawget( L, finalizers_index);                                             // ... finalizers lane_error finalizer
 		ASSERT_L( lua_isfunction( L, -1));
-		if( error_index)
+		if( lua_rc != LUA_OK) // we have an error message and an optional stack trace at the bottom of the stack
 		{
-			//char const* err_msg = lua_tostring( L, error_index);
-			lua_pushvalue( L, error_index);                                             // [err_msg {stack_trace}]? {func [, ...]}? lane_error finalizer err_msg
-#if ERROR_FULL_STACK
-			lua_pushvalue( L, error_index + 1);                                         // [err_msg {stack_trace}]? {func [, ...]}? lane_error finalizer err_msg {stack_trace}
-#endif // ERROR_FULL_STACK
-			args = 1 + ERROR_FULL_STACK;
+			ASSERT_L( finalizers_index == 2 || finalizers_index == 3);
+			//char const* err_msg = lua_tostring( L, 1);
+			lua_pushvalue( L, 1);                                                       // ... finalizers lane_error finalizer err_msg
+			// note we don't always have a stack trace for example when CANCEL_ERROR, or when we got an error that doesn't call our handler, such as LUA_ERRMEM
+			if( finalizers_index == 3)
+			{
+				lua_pushvalue( L, 2);                                                     // ... finalizers lane_error finalizer err_msg stack_trace
+			}
+			args = finalizers_index - 1;
 		}
 
-		rc = lua_pcall( L, args, 0, err_handler_index);                               // [err_msg {stack_trace}]? {func [, ...]}? lane_error err_msg2?
-		//
-		// LUA_ERRRUN / LUA_ERRMEM
-
+		// if no error from the main body, finlizer doesn't receive any argument, else it gets the error message and optional stack trace
+		rc = lua_pcall( L, args, 0, err_handler_index);                               // ... finalizers lane_error err_msg2?
 		if( rc != LUA_OK)
 		{
-#if ERROR_FULL_STACK
-			lua_pushlightuserdata( L, STACK_TRACE_KEY);                                 // [err_msg {stack_trace}]? {func [, ...]}? lane_error err_msg2 STACK_TRACE_KEY
-			lua_gettable( L, LUA_REGISTRYINDEX);                                        // [err_msg {stack_trace}]? {func [, ...]}? lane_error err_msg2 {stack_trace2}
-#endif // ERROR_FULL_STACK
-
+			push_stack_trace( L, rc, lua_gettop( L));
 			// If one finalizer fails, don't run the others. Return this
 			// as the 'real' error, replacing what we could have had (or not)
 			// from the actual code.
-			//
 			break;
 		}
+		// no error, proceed to next finalizer                                        // ... finalizers lane_error
 	}
 
-	// remove error handler function (if any) and finalizers table from the stack
-#if ERROR_FULL_STACK
-	lua_remove( L, err_handler_index);                                              // [err_msg {stack_trace}]? {func [, ...]}? err_msg2 {stack_trace2}
-#endif // ERROR_FULL_STACK
-	lua_remove( L, finalizers_index);                                               // [err_msg {stack_trace}]? err_msg2 {stack_trace2}
+	if( rc != LUA_OK)
+	{
+		// ERROR_FULL_STACK accounts for the presence of lane_error on the stack
+		int nb_err_slots = lua_gettop( L) - finalizers_index - ERROR_FULL_STACK;
+		// a finalizer generated an error, this is what we leave of the stack
+		for( n = nb_err_slots; n > 0; -- n)
+		{
+			lua_replace( L, n);
+		}
+		// leave on the stack only the error and optional stack trace produced by the error in the finalizer
+		lua_settop( L, nb_err_slots);
+	}
+	else // no error from the finalizers, make sure only the original return values from the lane body remain on the stack
+	{
+		lua_settop( L, finalizers_index - 1);
+	}
 
 	return rc;
 }
@@ -1896,6 +1905,40 @@ static int lane_error( lua_State* L)
 }
 #endif // ERROR_FULL_STACK
 
+static void push_stack_trace( lua_State* L, int rc_, int stk_base_)
+{
+	// Lua 5.1 error handler is limited to one return value; it stored the stack trace in the registry
+	switch( rc_)
+	{
+		case LUA_OK: // no error, body return values are on the stack
+		break;
+
+		case LUA_ERRRUN: // cancellation or a runtime error
+#if ERROR_FULL_STACK // when ERROR_FULL_STACK, we installed a handler
+		{
+			// fetch the call stack table from the registry where the handler stored it
+			STACK_GROW( L, 1);
+			lua_pushlightuserdata( L, STACK_TRACE_KEY);                              // err STACK_TRACE_KEY
+			// yields nil if no stack was generated (in case of cancellation for example)
+			lua_gettable( L, LUA_REGISTRYINDEX);                                     // err trace|nil
+
+			// For cancellation the error message is CANCEL_ERROR, and a stack trace isn't placed
+			// For other errors, the message should be a string, and we should have a stack trace table
+			ASSERT_L( (lua_istable( L, 1 + stk_base_) && lua_type( L, stk_base_) == LUA_TSTRING) || (lua_touserdata( L, stk_base_) == CANCEL_ERROR));
+			// Just leaving the stack trace table on the stack is enough to get it through to the master.
+			break;
+		}
+#endif // fall through if not ERROR_FULL_STACK
+
+		case LUA_ERRMEM: // memory allocation error (handler not called)
+		case LUA_ERRERR: // error while running the error handler (if any, for example an out-of-memory condition)
+		default:
+		// we should have a single value which is either a string (the error message) or CANCEL_ERROR
+		ASSERT_L( (lua_gettop( L) == stk_base_) && ((lua_type( L, stk_base_) == LUA_TSTRING) || (lua_touserdata( L, stk_base_) == CANCEL_ERROR)));
+		break;
+	}
+}
+
 LUAG_FUNC( set_debug_threadname)
 {
 	// C s_lane structure is a light userdata upvalue
@@ -1979,152 +2022,107 @@ static void thread_cleanup_handler( void* opaque)
 }
 #endif // THREADWAIT_METHOD == THREADWAIT_CONDVAR
 
-//---
 static THREAD_RETURN_T THREAD_CALLCONV lane_main( void* vs)
 {
-    struct s_lane* s = (struct s_lane*) vs;
-    int rc, rc2;
-    lua_State* L = s->L;
-    DEBUGSPEW_CODE( struct s_Universe* U = get_universe( L));
+	struct s_lane* s = (struct s_lane*) vs;
+	int rc, rc2;
+	lua_State* L = s->L;
+	// 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);
-    }
+	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
-
-    // Tie "set_finalizer()" to the state
-    //
-    lua_pushcfunction( L, LG_set_finalizer );
-    populate_func_lookup_table( L, -1, "set_finalizer");
-    lua_setglobal( L, "set_finalizer");
-
-    // Tie "set_debug_threadname()" to the state
-    // But don't register it in the lookup database because of the s_lane pointer upvalue
-    lua_pushlightuserdata( L, s);
-    lua_pushcclosure( L, LG_set_debug_threadname, 1);
-    lua_setglobal( L, "set_debug_threadname" );
-
-    // Tie "cancel_test()" to the state
-    //
-    lua_pushcfunction( L, LG_cancel_test);
-    populate_func_lookup_table( L, -1, "cancel_test");
-    lua_setglobal( L, "cancel_test");
+	THREAD_MAKE_ASYNCH_CANCELLABLE();
+	THREAD_CLEANUP_PUSH( thread_cleanup_handler, s);
+	s->status = RUNNING;  // PENDING -> RUNNING
+
+	// Tie "set_finalizer()" to the state
+	lua_pushcfunction( L, LG_set_finalizer);
+	populate_func_lookup_table( L, -1, "set_finalizer");
+	lua_setglobal( L, "set_finalizer");
+
+	// Tie "set_debug_threadname()" to the state
+	// But don't register it in the lookup database because of the s_lane pointer upvalue
+	lua_pushlightuserdata( L, s);
+	lua_pushcclosure( L, LG_set_debug_threadname, 1);
+	lua_setglobal( L, "set_debug_threadname" );
+
+	// Tie "cancel_test()" to the state
+	lua_pushcfunction( L, LG_cancel_test);
+	populate_func_lookup_table( L, -1, "cancel_test");
+	lua_setglobal( L, "cancel_test");
 
 #if ERROR_FULL_STACK
-    // Tie "set_error_reporting()" to the state
-    //
-    lua_pushcfunction( L, LG_set_error_reporting);
-    populate_func_lookup_table( L, -1, "set_error_reporting");
-    lua_setglobal( L, "set_error_reporting");
-
-    STACK_GROW( L, 1 );
-    lua_pushcfunction( L, lane_error);
-    lua_insert( L, 1 );
-
-    // [1]: error handler
-    // [2]: function to run
-    // [3..top]: parameters
-    //
-    rc= lua_pcall( L, lua_gettop(L)-2, LUA_MULTRET, 1 /*error handler*/ );
-        // 0: no error, body return values are on the stack
-        // LUA_ERRRUN: cancellation or a runtime error (error pushed on stack)
-        // LUA_ERRMEM: memory allocation error
-        // LUA_ERRERR: error while running the error handler (if any)
+	// Tie "set_error_reporting()" to the state
+	lua_pushcfunction( L, LG_set_error_reporting);
+	populate_func_lookup_table( L, -1, "set_error_reporting");
+	lua_setglobal( L, "set_error_reporting");
 
-    assert( rc!=LUA_ERRERR );   // since we've authored it
-
-    lua_remove(L,1);    // remove error handler
+	STACK_GROW( L, 1);
+	lua_pushcfunction( L, lane_error);                                           // func args handler
+	lua_insert( L, 1);                                                           // handler func args
+#endif // ERROR_FULL_STACK
 
-    // Lua 5.1 error handler is limited to one return value; taking stack trace via registry
-    if( rc != LUA_OK)
-    {
-        STACK_GROW(L,1);
-        lua_pushlightuserdata( L, STACK_TRACE_KEY );
-        lua_gettable(L, LUA_REGISTRYINDEX); // yields nil if no stack was generated (in case of cancellation for example)
+	rc = lua_pcall( L, nargs, LUA_MULTRET, ERROR_FULL_STACK);                    // retvals|err
 
-        // For cancellation, a stack trace isn't placed
-        //
-        assert( lua_istable(L,2) || (lua_touserdata(L,1)==CANCEL_ERROR) );
-        
-        // Just leaving the stack trace table on the stack is enough to get
-        // it through to the master.
-    }
+#if ERROR_FULL_STACK
+	lua_remove( L, 1);                                                           // retvals|error
+#	endif // ERROR_FULL_STACK
 
-#else // ERROR_FULL_STACK == 0
-    // This code does not use 'lane_error'
-    //
-    // [1]: function to run
-    // [2..top]: parameters
-    //
-    rc = lua_pcall( L, lua_gettop( L) - 1, LUA_MULTRET, 0); // no error handler
-        // LUA_OK(0): no error
-        // LUA_ERRRUN(2): a runtime error (error pushed on stack)
-        // LUA_ERRMEM(4): memory allocation error
-#endif // ERROR_FULL_STACK
+	// in case of error and if it exists, fetch stack trace from registry and push it
+	push_stack_trace( L, rc, 1);
 
-    DEBUGSPEW_CODE( fprintf( stderr, INDENT_BEGIN "Lane %p body: %s (%s)\n" INDENT_END, L, get_errcode_name( rc), (lua_touserdata(L,1)==CANCEL_ERROR) ? "cancelled" : lua_typename( L, lua_type( L, 1))));
-    //STACK_DUMP(L);
-    // Call finalizers, if the script has set them up.
-    //
-    rc2 = run_finalizers( L, rc);
-    DEBUGSPEW_CODE( fprintf( stderr, INDENT_BEGIN "Lane %p finalizer: %s\n" INDENT_END, L, get_errcode_name( rc2)));
-    if( rc2 != LUA_OK) // Error within a finalizer!
-    {
-        rc = rc2;    // we're overruling the earlier script error or normal return
-        // the finalizer generated an error, the error message [and stack trace] are pushed on the stack
-        // remove the rest so that only the error message [and stack trace] remain on the stack
-#if ERROR_FULL_STACK
-        lua_insert( L, 1);
-        lua_insert( L, 1);
-        lua_settop( L, 2);
-#else // ERROR_FULL_STACK == 0
-        lua_insert( L, 1);
-        lua_settop( L, 1);
-#endif // ERROR_FULL_STACK
-    }
-    s->waiting_on = NULL; // just in case
-    if( selfdestruct_remove( s)) // check and remove (under lock!)
-    {
-        // We're a free-running thread and no-one's there to clean us up.
-        //
-        lua_close( s->L);
+	DEBUGSPEW_CODE( fprintf( stderr, INDENT_BEGIN "Lane %p body: %s (%s)\n" INDENT_END, L, get_errcode_name( rc), (lua_touserdata( L, 1)==CANCEL_ERROR) ? "cancelled" : lua_typename( L, lua_type( L, 1))));
+	//STACK_DUMP(L);
+	// Call finalizers, if the script has set them up.
+	//
+	rc2 = run_finalizers( L, rc);
+	DEBUGSPEW_CODE( fprintf( stderr, INDENT_BEGIN "Lane %p finalizer: %s\n" INDENT_END, L, get_errcode_name( rc2)));
+	if( rc2 != LUA_OK) // Error within a finalizer!
+	{
+		// the finalizer generated an error, and left its own error message [and stack trace] on the stack
+		rc = rc2;    // we're overruling the earlier script error or normal return
+	}
+	s->waiting_on = NULL; // just in case
+	if( selfdestruct_remove( s)) // check and remove (under lock!)
+	{
+		// We're a free-running thread and no-one's there to clean us up.
+		//
+		lua_close( s->L);
 
-        MUTEX_LOCK( &s->U->selfdestruct_cs);
-        // done with lua_close(), terminal shutdown sequence may proceed
-        -- s->U->selfdestructing_count;
-        MUTEX_UNLOCK( &s->U->selfdestruct_cs);
+		MUTEX_LOCK( &s->U->selfdestruct_cs);
+		// done with lua_close(), terminal shutdown sequence may proceed
+		-- s->U->selfdestructing_count;
+		MUTEX_UNLOCK( &s->U->selfdestruct_cs);
 
-        lane_cleanup( s); // s is freed at this point
-    }
-    else
-    {
-        // leave results (1..top) or error message + stack trace (1..2) on the stack - master will copy them
+		lane_cleanup( s); // s is freed at this point
+	}
+	else
+	{
+		// leave results (1..top) or error message + stack trace (1..2) on the stack - master will copy them
 
-        enum e_status st= 
-            (rc==0) ? DONE 
-                    : (lua_touserdata(L,1)==CANCEL_ERROR) ? CANCELLED 
-                    : ERROR_ST;
+		enum e_status st = (rc == 0) ? DONE : (lua_touserdata( L, 1) == CANCEL_ERROR) ? CANCELLED : ERROR_ST;
 
-        // Posix no PTHREAD_TIMEDJOIN:
-        // 'done_lock' protects the -> DONE|ERROR_ST|CANCELLED state change
-        //
+		// Posix no PTHREAD_TIMEDJOIN:
+		// 'done_lock' protects the -> DONE|ERROR_ST|CANCELLED state change
+		//
 #if THREADWAIT_METHOD == THREADWAIT_CONDVAR
-        MUTEX_LOCK( &s->done_lock);
-        {
+		MUTEX_LOCK( &s->done_lock);
+		{
 #endif // THREADWAIT_METHOD == THREADWAIT_CONDVAR
-            s->status = st;
+			s->status = st;
 #if THREADWAIT_METHOD == THREADWAIT_CONDVAR
-            SIGNAL_ONE( &s->done_signal);   // wake up master (while 's->done_lock' is on)
-        }
-        MUTEX_UNLOCK( &s->done_lock);
+			SIGNAL_ONE( &s->done_signal);   // wake up master (while 's->done_lock' is on)
+		}
+		MUTEX_UNLOCK( &s->done_lock);
 #endif // THREADWAIT_METHOD == THREADWAIT_CONDVAR
-    }
-    THREAD_CLEANUP_POP( FALSE);
-    return 0;   // ignored
+	}
+	THREAD_CLEANUP_POP( FALSE);
+	return 0;   // ignored
 }
 
 // --- If a client wants to transfer stuff of a given module from the current state to another Lane, the module must be required
@@ -2312,11 +2310,12 @@ LUAG_FUNC( thread_new)
 		DEBUGSPEW_CODE( -- U->debugspew_indent_depth);
 	}
 
+
+	STACK_CHECK( L);
+	STACK_CHECK( L2);
 	ASSERT_L( lua_gettop( L2) == 0);
 
 	// Lane main function
-	//
-	STACK_CHECK( L);
 	if( lua_type( L, 1) == LUA_TFUNCTION)
 	{
 		int res;
@@ -2340,7 +2339,7 @@ LUAG_FUNC( thread_new)
 		}
 	}
 
-	ASSERT_L( lua_gettop( L2) == 1);
+	STACK_MID( L2, 1);
 	ASSERT_L( lua_isfunction( L2, 1));
 
 	// revive arguments
@@ -2359,8 +2358,7 @@ LUAG_FUNC( thread_new)
 	}
 	STACK_MID( L, 0);
 
-	ASSERT_L( (uint_t)lua_gettop( L2) == 1 + args);
-	ASSERT_L( lua_isfunction( L2, 1));
+	STACK_END( L2, 1 + args);
 
 	// 's' is allocated from heap, not Lua, since its life span may surpass 
 	// the handle's (if free running thread)
@@ -2616,20 +2614,19 @@ LUAG_FUNC( thread_join)
 	double wait_secs = luaL_optnumber( L, 2, -1.0);
 	lua_State* L2 = s->L;
 	int ret;
-	bool_t done;
-
-	done = THREAD_ISNULL( s->thread) || THREAD_WAIT( &s->thread, wait_secs, &s->done_signal, &s->done_lock, &s->status);
+	bool_t done = THREAD_ISNULL( s->thread) || THREAD_WAIT( &s->thread, wait_secs, &s->done_signal, &s->done_lock, &s->status);
 	if( !done || !L2)
 	{
 		return 0;      // timeout: pushes none, leaves 'L2' alive
 	}
 
+	STACK_CHECK( L);
 	// Thread is DONE/ERROR_ST/CANCELLED; all ours now
 
 	if( s->mstatus == KILLED) // OS thread was killed if thread_cancel was forced
 	{
 		// in that case, even if the thread was killed while DONE/ERROR_ST/CANCELLED, ignore regular return values
-		STACK_GROW( L, 1);
+		STACK_GROW( L, 2);
 		lua_pushnil( L);
 		lua_pushliteral( L, "killed");
 		ret = 2;
@@ -2654,13 +2651,17 @@ LUAG_FUNC( thread_join)
 			break;
 
 			case ERROR_ST:
-			STACK_GROW( L, 1);
-			lua_pushnil( L);
-			if( luaG_inter_move( U, L2, L, 1 + ERROR_FULL_STACK, eLM_LaneBody) != 0)    // error message at [-2], stack trace at [-1]
 			{
-				return luaL_error( L, "tried to copy unsupported types");
+				int const n = lua_gettop( L2);
+				STACK_GROW( L, 3);
+				lua_pushnil( L);
+				// even when ERROR_FULL_STACK, if the error is not LUA_ERRRUN, the handler wasn't called, and we only have 1 error message on the stack ...
+				if( luaG_inter_move( U, L2, L, n, eLM_LaneBody) != 0)  // nil "err" [trace]
+				{
+					return luaL_error( L, "tried to copy unsupported types: %s", lua_tostring( L, -n));
+				}
+				ret = 1 + n;
 			}
-			ret = 2 + ERROR_FULL_STACK;
 			break;
 
 			case CANCELLED:
@@ -2675,7 +2676,7 @@ LUAG_FUNC( thread_join)
 		lua_close( L2);
 	}
 	s->L = 0;
-
+	STACK_END( L, ret);
 	return ret;
 }
 
-- 
cgit v1.2.3-55-g6feb