[LTP] [PATCH] sched_football: synchronize start with barrier and add proper game stop

Li Wang liwang@redhat.com
Fri Aug 29 11:56:35 CEST 2025


The original code synchronized thread startup only with a `players_ready`
counter and busy-wait loops like:

    while (tst_atomic_load(&players_ready) < players_per_team * 4)
        usleep(100);

This ensured only that the right *number* of threads had started, but not
that they all reached the same point before execution. In practice, offense
threads could already move the ball while defense threads were not yet
scheduled, especially on debug/RT kernels. This resulted in unstable test
behavior and spurious failures.

To fix this, introduce a `pthread_barrier` that all players (offense,
defense, fans) and the referee must reach before the game starts. This
provides strict synchronization and removes the fragile busy-wait loops.

Additionally, player threads previously ran infinite loops (`while (1)`),
causing the test process to never terminate after the referee finished,
leading to harness timeouts. A global `game_over` flag is introduced so
threads can exit cleanly when the referee ends the game.

With these changes:
 - Startup is deterministic (barrier-based instead of busy-wait).
 - Threads terminate gracefully after the game (via `game_over`).
 - Test stability and reproducibility are significantly improved.

And to get rid of false postive:
  ==== sched_football ====
  command: sched_football
  tst_test.c:1882: TINFO: LTP version: 20240930
  tst_test.c:1886: TINFO: Tested kernel: 5.14.0-528.5693_1539899881.el9.x86_64+rt-debug
  tst_test.c:1719: TINFO: Timeout per run is 0h 05m 24s
  sched_football.c:147: TINFO: players_per_team: 256 game_length: 5
  sched_football.c:159: TINFO: Starting 256 offense threads at priority 15
  sched_football.c:170: TINFO: Starting 256 defense threads at priority 30
  sched_football.c:181: TINFO: Starting 256 fan threads at priority 50
  sched_football.c:108: TINFO: Starting referee thread
  sched_football.c:111: TINFO: Starting the game (5 sec)
  sched_football.c:131: TINFO: Final ball position: 6977
  sched_football.c:133: TFAIL: Expect: final_ball == 0

Signed-off-by: Li Wang <liwang@redhat.com>
Cc: Chunyu Hu <chuhu@redhat.com>
---
 .../func/sched_football/sched_football.c      | 48 ++++++++-----------
 1 file changed, 20 insertions(+), 28 deletions(-)

diff --git a/testcases/realtime/func/sched_football/sched_football.c b/testcases/realtime/func/sched_football/sched_football.c
index 1d761d43c..0617bdb87 100644
--- a/testcases/realtime/func/sched_football/sched_football.c
+++ b/testcases/realtime/func/sched_football/sched_football.c
@@ -44,18 +44,19 @@
 static tst_atomic_t the_ball;
 static int players_per_team = 0;
 static int game_length = DEF_GAME_LENGTH;
-static tst_atomic_t players_ready;
+static tst_atomic_t game_over;
 
 static char *str_game_length;
 static char *str_players_per_team;
+static pthread_barrier_t start_barrier;
 
 /* These are fans running across the field. They're trying to interrupt/distract everyone */
 void *thread_fan(void *arg LTP_ATTRIBUTE_UNUSED)
 {
 	prctl(PR_SET_NAME, "crazy_fan", 0, 0, 0);
-	tst_atomic_add_return(1, &players_ready);
+	pthread_barrier_wait(&start_barrier);
 	/*occasionally wake up and run across the field */
-	while (1) {
+	while (!tst_atomic_load(&game_over)) {
 		struct timespec start, stop;
 		nsec_t nsec;
 
@@ -78,9 +79,9 @@ void *thread_fan(void *arg LTP_ATTRIBUTE_UNUSED)
 void *thread_defense(void *arg LTP_ATTRIBUTE_UNUSED)
 {
 	prctl(PR_SET_NAME, "defense", 0, 0, 0);
-	tst_atomic_add_return(1, &players_ready);
+	pthread_barrier_wait(&start_barrier);
 	/*keep the ball from being moved */
-	while (1) {
+	while (!tst_atomic_load(&game_over)) {
 	}
 
 	return NULL;
@@ -90,8 +91,8 @@ void *thread_defense(void *arg LTP_ATTRIBUTE_UNUSED)
 void *thread_offense(void *arg LTP_ATTRIBUTE_UNUSED)
 {
 	prctl(PR_SET_NAME, "offense", 0, 0, 0);
-	tst_atomic_add_return(1, &players_ready);
-	while (1) {
+	pthread_barrier_wait(&start_barrier);
+	while (!tst_atomic_load(&game_over)) {
 		tst_atomic_add_return(1, &the_ball); /* move the ball ahead one yard */
 	}
 
@@ -115,6 +116,7 @@ void referee(int game_length)
 
 	/* Start the game! */
 	tst_atomic_store(0, &the_ball);
+	pthread_barrier_wait(&start_barrier);
 	atrace_marker_write("sched_football", "Game_started!");
 
 	/* Watch the game */
@@ -122,10 +124,13 @@ void referee(int game_length)
 		sleep(1);
 		gettimeofday(&now, NULL);
 	}
+
+	/* Stop the game! */
+	tst_atomic_store(1, &game_over);
 	atrace_marker_write("sched_football", "Game_Over!");
-	final_ball = tst_atomic_load(&the_ball);
 
 	/* Blow the whistle */
+	final_ball = tst_atomic_load(&the_ball);
 	tst_res(TINFO, "Final ball position: %d", final_ball);
 
 	TST_EXP_EXPR(final_ball == 0);
@@ -140,11 +145,12 @@ static void do_test(void)
 	if (players_per_team == 0)
 		players_per_team = get_numcpus();
 
-	tst_atomic_store(0, &players_ready);
-
 	tst_res(TINFO, "players_per_team: %d game_length: %d",
 	       players_per_team, game_length);
 
+	/* total = offense + defense + fans + referee */
+	pthread_barrier_init(&start_barrier, NULL, players_per_team * 4 + 1);
+
 	/* We're the ref, so set our priority right */
 	param.sched_priority = sched_get_priority_min(SCHED_FIFO) + 80;
 	sched_setscheduler(0, SCHED_FIFO, &param);
@@ -159,10 +165,6 @@ static void do_test(void)
 	for (i = 0; i < players_per_team; i++)
 		create_fifo_thread(thread_offense, NULL, priority);
 
-	/* Wait for the offense threads to start */
-	while (tst_atomic_load(&players_ready) < players_per_team)
-		usleep(100);
-
 	/* Start the defense */
 	priority = 30;
 	tst_res(TINFO, "Starting %d defense threads at priority %d",
@@ -170,26 +172,16 @@ static void do_test(void)
 	for (i = 0; i < players_per_team; i++)
 		create_fifo_thread(thread_defense, NULL, priority);
 
-	/* Wait for the defense threads to start */
-	while (tst_atomic_load(&players_ready) < players_per_team * 2)
-		usleep(100);
-
 	/* Start the crazy fans*/
 	priority = 50;
-	tst_res(TINFO, "Starting %d fan threads at priority %d",
-	       players_per_team, priority);
+	tst_res(TINFO, "Starting %d crazy-fan threads at priority %d",
+	       players_per_team*2, priority);
 	for (i = 0; i < players_per_team*2; i++)
 		create_fifo_thread(thread_fan, NULL, priority);
 
-	/* Wait for the crazy fan threads to start */
-	while (tst_atomic_load(&players_ready) < players_per_team * 4)
-		usleep(100);
-
-	/* let things get into steady state */
-	sleep(2);
-	/* Ok, everyone is on the field, bring out the ref */
-
 	referee(game_length);
+
+	pthread_barrier_destroy(&start_barrier);
 }
 
 static void do_setup(void)
-- 
2.51.0



More information about the ltp mailing list