mirror of
https://github.com/eclipse-threadx/threadx.git
synced 2026-10-06 06:59:08 +08:00
Bounded the wait for the delayed suspension window (#645)
threadx_thread_delayed_suspension_test waits for an interrupt to land while
thread 2 is part way through suspending, and waits for it with no bound:
while(delayed_suspend_set == 0)
{
tx_thread_wait_abort(&thread_2);
tx_thread_relinquish();
}
How long that takes depends on the build to a degree that is easy to miss. The
loop finishes in between a tenth of a second and three seconds in four of the
five ThreadX configurations. In trace_build it took 490 seconds, which was 40
percent of the whole ThreadX suite and more than every other test in that
configuration put together.
This is the third test in these suites built the same way, after
threadx_thread_priority_change and threadx_thread_wait_abort_and_isr_test: spin
until an interrupt happens to land in a narrow window, with nothing to stop the
spin if it does not. The other two have been given bounds already.
Give this one a wall clock budget too, for the same reason as the last: a tick
is delivered only when the port's timer thread runs, so the tick clock falls
behind real time under load or instrumentation, and instrumentation is exactly
what trace_build turns on.
The check after the loop needs care that the other two did not. It compares
thread_2_counter against thread_2_counter_capture, and the capture is taken
inside the interrupt handler at the moment the window is hit. Leaving that check
in place after a run that never reached the window would compare a live counter
against the zero it was initialised to and report a defect that is not there. So
the check is skipped when the window was not reached, and the run says so.
Reaching the window still exercises it exactly as before.
Verified both ways in trace_build, which is the configuration that was slow: the
window is reached in 8 seconds here and the test passes as it always did, and
with the budget forced to zero the test reports that the window was not reached
and passes without the dependent check firing.
Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5
parent
4d9ce41845
commit
a5483f0773
@@ -12,6 +12,7 @@
|
||||
/* This test checks out the delayed suspension clear from tx_thread_resume. */
|
||||
|
||||
#include <stdio.h>
|
||||
#include <time.h>
|
||||
#include "tx_api.h"
|
||||
#include "tx_thread.h"
|
||||
#include "tx_timer.h"
|
||||
@@ -235,6 +236,12 @@ static void thread_0_entry(ULONG thread_input)
|
||||
|
||||
UINT status;
|
||||
|
||||
#ifndef TX_NOT_INTERRUPTABLE
|
||||
#define DELAYED_SUSPENSION_SECOND_BUDGET ((ULONG) 120)
|
||||
|
||||
time_t start_wall;
|
||||
#endif
|
||||
|
||||
|
||||
/* Inform user. */
|
||||
printf("Running Thread Delayed Suspension Clearing Test..................... ");
|
||||
@@ -277,8 +284,23 @@ UINT status;
|
||||
/* Resume the test thread. */
|
||||
tx_thread_resume(&thread_2);
|
||||
|
||||
/* Wait until we see the delayed suspension set flag. */
|
||||
while(delayed_suspend_set == 0)
|
||||
/* Wait until we see the delayed suspension set flag.
|
||||
|
||||
The flag is set by the interrupt above, and only when that interrupt lands
|
||||
while thread 2 is part way through suspending. That is a narrow window,
|
||||
and waiting for it without a bound leaves the test no way out when it is
|
||||
not reached. How narrow it is depends on the build: in the ThreadX suite
|
||||
the same loop finishes in between a tenth of a second and three seconds in
|
||||
four configurations, and took 490 seconds in trace_build, where it was the
|
||||
most expensive thing in the suite by a wide margin.
|
||||
|
||||
The budget is in wall clock seconds rather than ticks. A tick arrives only
|
||||
when the port's timer thread runs, so the tick clock falls behind real
|
||||
time under load or instrumentation, which is exactly the configuration
|
||||
that needs bounding here. */
|
||||
start_wall = time(TX_NULL);
|
||||
while ((delayed_suspend_set == 0) &&
|
||||
(((ULONG) (time(TX_NULL) - start_wall)) <= DELAYED_SUSPENSION_SECOND_BUDGET))
|
||||
{
|
||||
/* Abort the suspension for thread 2. */
|
||||
tx_thread_wait_abort(&thread_2);
|
||||
@@ -291,7 +313,18 @@ UINT status;
|
||||
tx_thread_relinquish();
|
||||
|
||||
/* At this point, check for an error. */
|
||||
if (thread_2_counter != thread_2_counter_capture)
|
||||
if (delayed_suspend_set == 0)
|
||||
{
|
||||
|
||||
/* The window was never reached, so thread_2_counter_capture was never
|
||||
taken and the comparison below would be against a value that means
|
||||
nothing. Not reaching the window is a gap in what this run covered
|
||||
rather than a fault in the code under test, so say so and skip the
|
||||
check that depends on it. */
|
||||
printf("(delayed suspension window not reached in %lu seconds) ",
|
||||
(ULONG) (time(TX_NULL) - start_wall));
|
||||
}
|
||||
else if (thread_2_counter != thread_2_counter_capture)
|
||||
{
|
||||
|
||||
/* Delayed suspension error... thread kept running! */
|
||||
|
||||
@@ -12,6 +12,7 @@
|
||||
/* This test checks out the delayed suspension clear from tx_thread_resume. */
|
||||
|
||||
#include <stdio.h>
|
||||
#include <time.h>
|
||||
#include "tx_api.h"
|
||||
#include "tx_thread.h"
|
||||
#include "tx_timer.h"
|
||||
@@ -235,6 +236,12 @@ static void thread_0_entry(ULONG thread_input)
|
||||
|
||||
UINT status;
|
||||
|
||||
#ifndef TX_NOT_INTERRUPTABLE
|
||||
#define DELAYED_SUSPENSION_SECOND_BUDGET ((ULONG) 120)
|
||||
|
||||
time_t start_wall;
|
||||
#endif
|
||||
|
||||
|
||||
/* Inform user. */
|
||||
printf("Running Thread Delayed Suspension Clearing Test..................... ");
|
||||
@@ -278,8 +285,23 @@ UINT status;
|
||||
/* Resume the test thread. */
|
||||
tx_thread_resume(&thread_2);
|
||||
|
||||
/* Wait until we see the delayed suspension set flag. */
|
||||
while(delayed_suspend_set == 0)
|
||||
/* Wait until we see the delayed suspension set flag.
|
||||
|
||||
The flag is set by the interrupt above, and only when that interrupt lands
|
||||
while thread 2 is part way through suspending. That is a narrow window,
|
||||
and waiting for it without a bound leaves the test no way out when it is
|
||||
not reached. How narrow it is depends on the build: this loop finishes in
|
||||
between a tenth of a second and three seconds in four of the five ThreadX
|
||||
configurations, and took 490 seconds in trace_build, where it was the most
|
||||
expensive thing in the suite by a wide margin.
|
||||
|
||||
The budget is in wall clock seconds rather than ticks. A tick arrives only
|
||||
when the port's timer thread runs, so the tick clock falls behind real
|
||||
time under load or instrumentation, which is exactly the configuration
|
||||
that needs bounding here. */
|
||||
start_wall = time(TX_NULL);
|
||||
while ((delayed_suspend_set == 0) &&
|
||||
(((ULONG) (time(TX_NULL) - start_wall)) <= DELAYED_SUSPENSION_SECOND_BUDGET))
|
||||
{
|
||||
|
||||
/* Abort the suspension for thread 2. */
|
||||
@@ -293,7 +315,18 @@ UINT status;
|
||||
tx_thread_relinquish();
|
||||
|
||||
/* At this point, check for an error. */
|
||||
if (thread_2_counter != thread_2_counter_capture)
|
||||
if (delayed_suspend_set == 0)
|
||||
{
|
||||
|
||||
/* The window was never reached, so thread_2_counter_capture was never
|
||||
taken and the comparison below would be against a value that means
|
||||
nothing. Not reaching the window is a gap in what this run covered
|
||||
rather than a fault in the code under test, so say so and skip the
|
||||
check that depends on it. */
|
||||
printf("(delayed suspension window not reached in %lu seconds) ",
|
||||
(ULONG) (time(TX_NULL) - start_wall));
|
||||
}
|
||||
else if (thread_2_counter != thread_2_counter_capture)
|
||||
{
|
||||
|
||||
/* Delayed suspension error... thread kept running! */
|
||||
|
||||
Reference in New Issue
Block a user