From 11b19bf4d7dfc883e18ea579d978c620865d843f Mon Sep 17 00:00:00 2001
From: Benoit Germain <bnt.germain@gmail.com>
Date: Fri, 14 Dec 2012 15:35:26 +0100
Subject: lanes.threads(): core implementation
---
src/lanes.c | 143 +++++++++++++++++++++++++++++++++++++++++++++++-------------
1 file changed, 112 insertions(+), 31 deletions(-)
(limited to 'src/lanes.c')
diff --git a/src/lanes.c b/src/lanes.c
index 6789e4e..161de59 100644
--- a/src/lanes.c
+++ b/src/lanes.c
@@ -103,6 +103,11 @@ THE SOFTWARE.
# include <sys/types.h>
#endif
+/*
+ * Do we want to activate full lane tracking feature? (EXPERIMENTAL)
+ */
+#define HAVE_LANE_TRACKING 1
+
/* 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').
@@ -118,7 +123,9 @@ struct s_lane {
// M: sub-thread OS thread
// S: not used
- lua_State *L;
+ char const* debug_name;
+
+ lua_State* L;
//
// M: prepares the state, and reads results
// S: while S is running, M must keep out of modifying the state
@@ -164,8 +171,8 @@ struct s_lane {
// S: cleans up after itself if non-NULL at lane exit
};
-static bool_t cancel_test( lua_State *L );
-static void cancel_error( lua_State *L );
+static bool_t cancel_test( lua_State*L );
+static void cancel_error( lua_State*L );
#define CANCEL_TEST_KEY ((void*)cancel_test) // used as registry key
#define CANCEL_ERROR ((void*)cancel_error) // 'cancel_error' sentinel
@@ -204,7 +211,7 @@ 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 ) {
+static bool_t push_registry_table( lua_State*L, void *key, bool_t create ) {
STACK_GROW(L,3);
@@ -247,7 +254,7 @@ static void linda_id( lua_State*, char const * const which);
#define lua_toLinda(L,n) ((struct s_Linda *)luaG_todeep( L, linda_id, n ))
-static void check_key_types( lua_State *L, int _start, int _end)
+static void check_key_types( lua_State*L, int _start, int _end)
{
int i;
for( i = _start; i <= _end; ++ i)
@@ -305,7 +312,7 @@ LUAG_FUNC( linda_send)
STACK_GROW(L, 1);
{
struct s_Keeper *K = keeper_acquire( linda);
- lua_State *KL = K->L; // need to do this for 'STACK_CHECK'
+ lua_State*KL = K->L; // need to do this for 'STACK_CHECK'
STACK_CHECK( KL)
for( ;;)
{
@@ -720,8 +727,6 @@ LUAG_FUNC( linda_deep ) {
static int linda_tostring( lua_State* L, int _idx, bool_t _opt)
{
- char text[32];
- int len;
struct s_Linda* linda = lua_toLinda( L, _idx);
if( !_opt)
{
@@ -729,6 +734,8 @@ static int linda_tostring( lua_State* L, int _idx, bool_t _opt)
}
if( linda)
{
+ char text[32];
+ int len;
if( linda->name[0])
len = sprintf( text, "Linda: %.*s", (int)sizeof(text) - 8, linda->name);
else
@@ -808,7 +815,7 @@ LUAG_FUNC( linda_dump)
* For any other strings, the ID function must not react at all. This allows
* future extensions of the system.
*/
-static void linda_id( lua_State *L, char const * const which)
+static void linda_id( lua_State*L, char const * const which)
{
if (strcmp( which, "new" )==0)
{
@@ -981,7 +988,7 @@ LUAG_FUNC( set_finalizer )
//
// TBD: should we add stack trace on failing finalizer, wouldn't be hard..
//
-static int run_finalizers( lua_State *L, int lua_rc )
+static int run_finalizers( lua_State*L, int lua_rc )
{
unsigned error_index, tbl_index;
unsigned n;
@@ -1176,7 +1183,7 @@ volatile DEEP_PRELUDE *timer_deep; // = NULL
/*
* Process end; cancel any still free-running threads
*/
-static int selfdestruct_gc( lua_State *L)
+static int selfdestruct_gc( lua_State*L)
{
(void)L; // unused
if (selfdestruct_first == SELFDESTRUCT_END) return 0; // no free-running threads
@@ -1341,7 +1348,7 @@ static int selfdestruct_gc( lua_State *L)
* Returns TRUE if any locks are to be exited, and 'cancel_error()' called,
* to make execution of the lane end.
*/
-static bool_t cancel_test( lua_State *L ) {
+static bool_t cancel_test( lua_State*L ) {
struct s_lane *s;
STACK_GROW(L,1);
@@ -1358,13 +1365,13 @@ static bool_t cancel_test( lua_State *L ) {
return s && s->cancel_request;
}
-static void cancel_error( lua_State *L ) {
+static void cancel_error( lua_State*L ) {
STACK_GROW(L,1);
lua_pushlightuserdata( L, CANCEL_ERROR ); // special error value
lua_error(L); // no return
}
-static void cancel_hook( lua_State *L, lua_Debug *ar ) {
+static void cancel_hook( lua_State*L, lua_Debug *ar ) {
(void)ar;
if (cancel_test(L)) cancel_error(L);
}
@@ -1546,19 +1553,39 @@ static int lane_error( lua_State* L)
LUAG_FUNC( set_debug_threadname)
{
- luaL_checktype( L, -1, LUA_TSTRING);
- THREAD_SETNAME( lua_tostring( L, -1));
+ struct s_lane* s = lua_touserdata( L, lua_upvalueindex( 1));
+ luaL_checktype( L, -1, LUA_TSTRING); // "name"
+ // store a hidden reference in the registry to make sure the string is kept around even if a lane decides to manually change the "decoda_name" global...
+ lua_pushlightuserdata( L, LG_set_debug_threadname); // "name" lud
+ lua_pushvalue( L, -2); // "name" lud "name"
+ lua_rawset( L, LUA_REGISTRYINDEX); // "name"
+ s->debug_name = lua_tostring( L, -1);
+ // keep a direct pointer on the string
+ THREAD_SETNAME( s->debug_name);
// to see VM name in Decoda debugger Virtual Machine window
- lua_setglobal( L, "decoda_name");
+ lua_setglobal( L, "decoda_name"); //
return 0;
}
+#if HAVE_LANE_TRACKING
+static bool_t GTrackLanes = FALSE;
+#endif // HAVE_LANE_TRACKING
+
//---
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;
+ lua_State*L= s->L;
+
+#if HAVE_LANE_TRACKING
+ if( GTrackLanes)
+ {
+ // If we track lanes, we add them right now to the list so that its traversal hits all known lanes
+ // (else we get only the still running lanes for which GC was called, IOW not accessible anymore from a script)
+ selfdestruct_add( s);
+ }
+#endif // HAVE_LANE_TRACKING
s->status= RUNNING; // PENDING -> RUNNING
@@ -1569,7 +1596,8 @@ static THREAD_RETURN_T THREAD_CALLCONV lane_main( void *vs)
// Tie "set_debug_threadname()" to the state
//
- lua_pushcfunction( L, LG_set_debug_threadname);
+ lua_pushlightuserdata( L, s);
+ lua_pushcclosure( L, LG_set_debug_threadname, 1);
lua_setglobal( L, "set_debug_threadname" );
// Tie "cancel_test()" to the state
@@ -1704,7 +1732,7 @@ 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);
@@ -1728,7 +1756,7 @@ static void require_one_module( lua_State *L, lua_State *L2, bool_t _fatal)
LUAG_FUNC( thread_new )
{
- lua_State *L2;
+ lua_State*L2;
struct s_lane *s;
struct s_lane **ud;
@@ -1918,6 +1946,7 @@ LUAG_FUNC( thread_new )
s->L= L2;
s->status= PENDING;
s->waiting_on = NULL;
+ s->debug_name = NULL;
s->cancel_request= FALSE;
#if THREADWAIT_METHOD == THREADWAIT_CONDVAR
@@ -1995,9 +2024,14 @@ LUAG_FUNC( thread_gc)
}
else if( s->status < DONE)
{
- // still running: will have to be cleaned up later
- selfdestruct_add( s);
- assert( s->selfdestruct_next);
+#if HAVE_LANE_TRACKING
+ if( !GTrackLanes)
+#endif // HAVE_LANE_TRACKING
+ {
+ // still running: will have to be cleaned up later
+ selfdestruct_add( s);
+ assert( s->selfdestruct_next);
+ }
return 0;
}
@@ -2008,6 +2042,14 @@ LUAG_FUNC( thread_gc)
s->L = 0;
}
+#if HAVE_LANE_TRACKING
+ if( GTrackLanes)
+ {
+ // Lane was cleaned up, no need to handle at process termination
+ selfdestruct_remove( s);
+ }
+#endif // HAVE_LANE_TRACKING
+
// Clean up after a (finished) thread
//
#if THREADWAIT_METHOD == THREADWAIT_CONDVAR
@@ -2091,7 +2133,7 @@ static char const * thread_status_string( struct s_lane *s)
return str;
}
-static int push_thread_status( lua_State *L, struct s_lane *s)
+static int push_thread_status( lua_State*L, struct s_lane *s)
{
char const * const str = thread_status_string( s);
ASSERT_L( str);
@@ -2113,7 +2155,7 @@ LUAG_FUNC( thread_join)
{
struct s_lane* const s = lua_toLane( L, 1);
double wait_secs= luaL_optnumber(L,2,-1.0);
- lua_State *L2= s->L;
+ lua_State*L2= s->L;
int ret;
bool_t done;
@@ -2329,6 +2371,37 @@ LUAG_FUNC( thread_index)
return 0;
}
+#if HAVE_LANE_TRACKING
+//---
+// threads() -> {}|nil
+//
+// Return a list of all known lanes
+LUAG_FUNC( threads)
+{
+ int const top = lua_gettop( L);
+ // List _all_ still running threads
+ //
+ MUTEX_LOCK( &selfdestruct_cs);
+ if( selfdestruct_first != SELFDESTRUCT_END)
+ {
+ struct s_lane* s = selfdestruct_first;
+ lua_newtable( L); // {}
+ while( s != SELFDESTRUCT_END)
+ {
+ if( s->debug_name)
+ lua_pushstring( L, s->debug_name); // {} "name"
+ else
+ lua_pushfstring( L, "Lane %p", s); // {} "name"
+ push_thread_status( L, s); // {} "name" "status"
+ lua_rawset( L, -3); // {}
+ s = s->selfdestruct_next;
+ }
+ }
+ MUTEX_UNLOCK( &selfdestruct_cs);
+ return lua_gettop( L) - top;
+}
+#endif // HAVE_LANE_TRACKING
+
/*
* ###############################################################################################
* ######################################## Timer support ########################################
@@ -2401,6 +2474,9 @@ LUAG_FUNC( wakeup_conv )
static const struct luaL_Reg lanes_functions [] = {
{"linda", LG_linda},
{"now_secs", LG_now_secs},
+#if HAVE_LANE_TRACKING
+ {"threads", LG_threads},
+#endif // HAVE_LANE_TRACKING
{"wakeup_conv", LG_wakeup_conv},
{"nameof", luaG_nameof},
{"set_singlethreaded", LG_set_singlethreaded},
@@ -2410,7 +2486,7 @@ 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)
+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)
{
const char *err;
@@ -2421,7 +2497,11 @@ static void init_once_LOCKED( lua_State* L, volatile DEEP_PRELUDE** timer_deep_r
#if (defined PLATFORM_OSX) && (defined _UTILBINDTHREADTOCPU)
chudInitialize();
#endif
-
+
+#if HAVE_LANE_TRACKING
+ GTrackLanes = _track_lanes;
+#endif // HAVE_LANE_TRACKING
+
// Locks for 'tools.c' inc/dec counters
//
MUTEX_INIT( &deep_lock );
@@ -2516,6 +2596,7 @@ LUAG_FUNC( configure )
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.
*
@@ -2528,7 +2609,7 @@ LUAG_FUNC( configure )
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);
+ init_once_LOCKED( L, &timer_deep, nbKeepers, on_state_create, shutdown_timeout, track_lanes);
go_ahead= 1; // let others pass
}
else
@@ -2546,7 +2627,7 @@ LUAG_FUNC( configure )
//
if( s_initCount == 0)
{
- init_once_LOCKED( L, &timer_deep, nbKeepers, on_state_create, shutdown_timeout);
+ init_once_LOCKED( L, &timer_deep, nbKeepers, on_state_create, shutdown_timeout, track_lanes);
s_initCount = 1;
}
}
@@ -2569,7 +2650,7 @@ LUAG_FUNC( configure )
lua_newtable( L);
lua_pushcfunction( L, LG_thread_gc);
lua_setfield( L, -2, "__gc");
- lua_pushcfunction( L, LG_thread_index);
+ lua_pushcfunction( L, LG_thread_index);
lua_setfield( L, -2, "__index");
lua_getglobal( L, "error");
ASSERT_L( lua_isfunction( L, -1));
--
cgit v1.2.3-55-g6feb