[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, ¶m);
@@ -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