From 97eef73df2b3bc31dc6cc9cb13604d79c950d46f Mon Sep 17 00:00:00 2001 From: Victor Julien Date: Tue, 6 Oct 2026 04:30:06 +0000 Subject: [PATCH] threads: add unit tests for TmThreadsWaitForUnpause Covers the killed-during-initial-pause case (mgmt thread returns so the main loop runs once; non-mgmt returns false and skips it, THV_PAUSED cleared on both paths) and the unpause-while-spinning path, driven from a helper thread. Ticket: #9168. (cherry picked from commit 8998fbacc537f9b5756e28745925e62a12afef35) --- src/runmode-unittests.c | 2 ++ src/tm-threads.c | 70 +++++++++++++++++++++++++++++++++++++++++ 2 files changed, 72 insertions(+) diff --git a/src/runmode-unittests.c b/src/runmode-unittests.c index 4898f39a00..4401479b08 100644 --- a/src/runmode-unittests.c +++ b/src/runmode-unittests.c @@ -44,6 +44,7 @@ #include "flow-manager.h" #include "flow-var.h" #include "flow-bit.h" +#include "tm-threads.h" #include "pkt-var.h" #include "host.h" @@ -201,6 +202,7 @@ static void RegisterUnittests(void) MagicRegisterTests(); UtilMiscRegisterTests(); ThreadingAffinityRegisterTests(); + TmThreadsRegisterTests(); DetectAddressTests(); DetectProtoTests(); DetectPortTests(); diff --git a/src/tm-threads.c b/src/tm-threads.c index 0841c85cac..1651364144 100644 --- a/src/tm-threads.c +++ b/src/tm-threads.c @@ -2452,3 +2452,73 @@ void TmThreadsInjectFlowById(Flow *f, const int id) } BUG_ON(1); } + +#ifdef UNITTESTS +#include "util-unittest-helper.h" +#include + +static void *UnpauseHelper(void *arg) +{ + ThreadVars *tv = (ThreadVars *)arg; + /* only unpause once the caller is in the pause wait: THV_PAUSED is + * set by the caller on entry, so the fixed-delay race where the + * caller is descheduled past the unpause window cannot happen */ + while (!TmThreadsCheckFlag(tv, THV_PAUSED)) { + SleepUsec(100); + } + TmThreadsUnsetFlag(tv, THV_PAUSE); + return NULL; +} + +static int TmThreadsTestWaitForUnpause01(void) +{ + ThreadVars th_v; + memset(&th_v, 0, sizeof(th_v)); + th_v.type = TVT_MGMT; + + /* killed while still in the initial pause: mgmt threads must still + * run their main loop once to do final work */ + TmThreadsSetFlag(&th_v, THV_PAUSE); + TmThreadsSetFlag(&th_v, THV_KILL); + FAIL_IF(!TmThreadsWaitForUnpause(&th_v)); + FAIL_IF(TmThreadsCheckFlag(&th_v, THV_PAUSED)); + + memset(&th_v, 0, sizeof(th_v)); + th_v.type = TVT_PPT; + + /* non-mgmt threads keep skipping the main loop */ + TmThreadsSetFlag(&th_v, THV_PAUSE); + TmThreadsSetFlag(&th_v, THV_KILL); + FAIL_IF(TmThreadsWaitForUnpause(&th_v)); + FAIL_IF(TmThreadsCheckFlag(&th_v, THV_PAUSED)); + + PASS; +} + +static int TmThreadsTestWaitForUnpause02(void) +{ + /* unpause arrives while the thread spins in the pause wait */ + ThreadVars th_v; + memset(&th_v, 0, sizeof(th_v)); + + TmThreadsSetFlag(&th_v, THV_PAUSE); + pthread_t th; + /* a failed create would leave THV_PAUSE set forever: fail the test + * instead of spinning in the pause wait */ + int rc = pthread_create(&th, NULL, UnpauseHelper, &th_v); + FAIL_IF(rc != 0); + + FAIL_IF(!TmThreadsWaitForUnpause(&th_v)); + pthread_join(th, NULL); + FAIL_IF(TmThreadsCheckFlag(&th_v, THV_PAUSED)); + + PASS; +} + +void TmThreadsRegisterTests(void) +{ + UtRegisterTest("TmThreadsTestWaitForUnpause01", TmThreadsTestWaitForUnpause01); + UtRegisterTest("TmThreadsTestWaitForUnpause02", TmThreadsTestWaitForUnpause02); +} + +#endif /* UNITTESTS */