From f741cac68de584f2e16507b2b84fc734ffcc3bb6 Mon Sep 17 00:00:00 2001
From: Benoit Germain <bnt.germain@gmail.com>
Date: Mon, 17 Dec 2012 11:29:34 +0100
Subject: Fixed lane tracking feature

---
 src/lanes.c | 192 ++++++++++++++++++++++++++++++++++++++++--------------------
 1 file changed, 128 insertions(+), 64 deletions(-)

(limited to 'src/lanes.c')

diff --git a/src/lanes.c b/src/lanes.c
index 161de59..b8f7eed 100644
--- a/src/lanes.c
+++ b/src/lanes.c
@@ -169,6 +169,12 @@ struct s_lane {
 	// M: sets to non-NULL if facing lane handle '__gc' cycle but the lane
 	//    is still running
 	// S: cleans up after itself if non-NULL at lane exit
+
+#if HAVE_LANE_TRACKING
+	struct s_lane * volatile tracking_next;
+#endif // HAVE_LANE_TRACKING
+	//
+	// For tracking only
 };
 
 static bool_t cancel_test( lua_State*L );
@@ -233,6 +239,91 @@ static bool_t push_registry_table( lua_State*L, void *key, bool_t create ) {
     return TRUE;    // table pushed
 }
 
+#if HAVE_LANE_TRACKING
+
+static MUTEX_T tracking_cs;
+struct s_lane* volatile tracking_first = NULL; // will change to TRACKING_END if we want to activate tracking
+
+// The chain is ended by '(struct s_lane*)(-1)', not NULL:
+// 'tracking_first -> ... -> ... -> (-1)'
+#define TRACKING_END ((struct s_lane *)(-1))
+
+/*
+ * Add the lane to tracking chain; the ones still running at the end of the
+ * whole process will be cancelled.
+ */
+static void tracking_add( struct s_lane *s)
+{
+
+	MUTEX_LOCK( &tracking_cs);
+	{
+		assert( s->tracking_next == NULL);
+
+		s->tracking_next = tracking_first;
+		tracking_first = s;
+	}
+	MUTEX_UNLOCK( &tracking_cs);
+}
+
+/*
+ * A free-running lane has ended; remove it from tracking chain
+ */
+static bool_t tracking_remove( struct s_lane *s )
+{
+	bool_t found = FALSE;
+	MUTEX_LOCK( &tracking_cs);
+	{
+		// Make sure (within the MUTEX) that we actually are in the chain
+		// still (at process exit they will remove us from chain and then
+		// cancel/kill).
+		//
+		if (s->tracking_next != NULL)
+		{
+			struct s_lane **ref= (struct s_lane **) &tracking_first;
+
+			while( *ref != TRACKING_END)
+			{
+				if( *ref == s)
+				{
+					*ref = s->tracking_next;
+					s->tracking_next = NULL;
+					found = TRUE;
+					break;
+				}
+				ref = (struct s_lane **) &((*ref)->tracking_next);
+			}
+			assert( found);
+		}
+	}
+	MUTEX_UNLOCK( &tracking_cs);
+	return found;
+}
+
+#endif // HAVE_LANE_TRACKING
+
+//---
+// low-level cleanup
+
+static void lane_cleanup( struct s_lane* s)
+{
+	// Clean up after a (finished) thread
+	//
+#if THREADWAIT_METHOD == THREADWAIT_CONDVAR
+	SIGNAL_FREE( &s->done_signal);
+	MUTEX_FREE( &s->done_lock);
+#endif // THREADWAIT_METHOD == THREADWAIT_CONDVAR
+
+#if HAVE_LANE_TRACKING
+	if( tracking_first)
+	{
+		// Lane was cleaned up, no need to handle at process termination
+		tracking_remove( s);
+	}
+#endif // HAVE_LANE_TRACKING
+
+	free( s);
+}
+
 /*
  * ###############################################################################################
  * ############################################ Linda ############################################
@@ -843,27 +934,27 @@ static void linda_id( lua_State*L, char const * const which)
 
         lua_pushlightuserdata( L, s );
     }
-    else if (strcmp( which, "delete" )==0)
+    else if( strcmp( which, "delete" ) == 0)
     {
-        struct s_Keeper *K;
-        struct s_Linda *s= lua_touserdata(L,1);
-        ASSERT_L(s);
+        struct s_Keeper* K;
+        struct s_Linda* l= lua_touserdata( L, 1);
+        ASSERT_L( l);
 
         /* Clean associated structures in the keeper state.
         */
-        K= keeper_acquire(s);
+        K = keeper_acquire( l);
         if( K && K->L) // can be NULL if this happens during main state shutdown (lanes is GC'ed -> no keepers -> no need to cleanup)
         {
-            keeper_call( K->L, KEEPER_API( clear), L, s, 0 );
+            keeper_call( K->L, KEEPER_API( clear), L, l, 0);
             keeper_release( K);
         }
 
         /* There aren't any lanes waiting on these lindas, since all proxies
         * have been gc'ed. Right?
         */
-        SIGNAL_FREE( &s->read_happened );
-        SIGNAL_FREE( &s->write_happened );
-        free(s);
+        SIGNAL_FREE( &l->read_happened);
+        SIGNAL_FREE( &l->write_happened);
+        free( l);
     }
     else if (strcmp( which, "metatable" )==0)
     {
@@ -1126,12 +1217,12 @@ static MUTEX_T selfdestruct_cs;
     // The chain is ended by '(struct s_lane*)(-1)', not NULL:
     //      'selfdestruct_first -> ... -> ... -> (-1)'
 
-struct s_lane * volatile selfdestruct_first= SELFDESTRUCT_END;
+struct s_lane* volatile selfdestruct_first = SELFDESTRUCT_END;
 
 /*
-* Add the lane to selfdestruct chain; the ones still running at the end of the
-* whole process will be cancelled.
-*/
+ * Add the lane to selfdestruct chain; the ones still running at the end of the
+ * whole process will be cancelled.
+ */
 static void selfdestruct_add( struct s_lane *s ) {
 
     MUTEX_LOCK( &selfdestruct_cs );
@@ -1145,8 +1236,8 @@ static void selfdestruct_add( struct s_lane *s ) {
 }
 
 /*
-* A free-running lane has ended; remove it from selfdestruct chain
-*/
+ * A free-running lane has ended; remove it from selfdestruct chain
+ */
 static bool_t selfdestruct_remove( struct s_lane *s )
 {
     bool_t found = FALSE;
@@ -1310,11 +1401,7 @@ static int selfdestruct_gc( lua_State*L)
 #endif // THREADAPI == THREADAPI_PTHREAD
                 }
                 // NO lua_close() in this case because we don't know where execution of the state was interrupted
-#if THREADWAIT_METHOD == THREADWAIT_CONDVAR
-                SIGNAL_FREE( &s->done_signal);
-                MUTEX_FREE( &s->done_lock);
-#endif // THREADWAIT_METHOD == THREADWAIT_CONDVAR
-                free( s);
+                lane_cleanup( s);
                 s = next_s;
                 n++;
             }
@@ -1567,10 +1654,6 @@ LUAG_FUNC( set_debug_threadname)
 	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)
 {
@@ -1579,11 +1662,9 @@ static THREAD_RETURN_T THREAD_CALLCONV lane_main( void *vs)
     lua_State*L= s->L;
 
 #if HAVE_LANE_TRACKING
-    if( GTrackLanes)
+    if( tracking_first)
     {
-        // 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);
+        tracking_add( s);
     }
 #endif // HAVE_LANE_TRACKING
 
@@ -1684,11 +1765,7 @@ static THREAD_RETURN_T THREAD_CALLCONV lane_main( void *vs)
         lua_close( s->L );
         s->L = L = 0;
 
-    #if THREADWAIT_METHOD == THREADWAIT_CONDVAR
-        SIGNAL_FREE( &s->done_signal);
-        MUTEX_FREE( &s->done_lock);
-    #endif // THREADWAIT_METHOD == THREADWAIT_CONDVAR
-        free(s);
+        lane_cleanup( s);
 
     }
     else
@@ -1955,6 +2032,9 @@ LUAG_FUNC( thread_new )
 #endif // THREADWAIT_METHOD == THREADWAIT_CONDVAR
 	s->mstatus= NORMAL;
 	s->selfdestruct_next= NULL;
+#if HAVE_LANE_TRACKING
+	s->tracking_next = NULL;
+#endif // HAVE_LANE_TRACKING
 
 	// Set metatable for the userdata
 	//
@@ -2024,14 +2104,9 @@ LUAG_FUNC( thread_gc)
 	}
 	else if( s->status < DONE)
 	{
-#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);
-		}
+		// still running: will have to be cleaned up later
+		selfdestruct_add( s);
+		assert( s->selfdestruct_next);
 		return 0;
 
 	}
@@ -2042,22 +2117,8 @@ 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
-	SIGNAL_FREE( &s->done_signal);
-	MUTEX_FREE( &s->done_lock);
-#endif // THREADWAIT_METHOD == THREADWAIT_CONDVAR
-
-	free( s);
+	lane_cleanup( s);
 	return 0;
 }
 
@@ -2381,12 +2442,12 @@ LUAG_FUNC( threads)
 	int const top = lua_gettop( L);
 	// List _all_ still running threads
 	//
-	MUTEX_LOCK( &selfdestruct_cs);
-	if( selfdestruct_first != SELFDESTRUCT_END)
+	MUTEX_LOCK( &tracking_cs);
+	if( tracking_first && tracking_first != TRACKING_END)
 	{
-		struct s_lane* s = selfdestruct_first;
+		struct s_lane* s = tracking_first;
 		lua_newtable( L);                                          // {}
-		while( s != SELFDESTRUCT_END)
+		while( s != TRACKING_END)
 		{
 			if( s->debug_name)
 				lua_pushstring( L, s->debug_name);                     // {} "name"
@@ -2394,10 +2455,10 @@ LUAG_FUNC( threads)
 				lua_pushfstring( L, "Lane %p", s);                     // {} "name"
 			push_thread_status( L, s);                               // {} "name" "status"
 			lua_rawset( L, -3);                                      // {}
-			s = s->selfdestruct_next;
+			s = s->tracking_next;
 		}
 	}
-	MUTEX_UNLOCK( &selfdestruct_cs);
+	MUTEX_UNLOCK( &tracking_cs);
 	return lua_gettop( L) - top;
 }
 #endif // HAVE_LANE_TRACKING
@@ -2499,7 +2560,7 @@ static void init_once_LOCKED( lua_State* L, volatile DEEP_PRELUDE** timer_deep_r
 #endif
 
 #if HAVE_LANE_TRACKING
-    GTrackLanes = _track_lanes;
+    tracking_first = _track_lanes ? TRACKING_END : NULL;
 #endif // HAVE_LANE_TRACKING
 
         // Locks for 'tools.c' inc/dec counters
@@ -2513,9 +2574,12 @@ static void init_once_LOCKED( lua_State* L, volatile DEEP_PRELUDE** timer_deep_r
 
         serialize_require( L );
 
-        // Selfdestruct chain handling
+        // Linked chains handling
         //
         MUTEX_INIT( &selfdestruct_cs );
+#if HAVE_LANE_TRACKING
+        MUTEX_INIT( &tracking_cs);
+#endif // HAVE_LANE_TRACKING
 
         //---
         // Linux needs SCHED_RR to change thread priorities, and that is only
-- 
cgit v1.2.3-55-g6feb