Skip to content

Stopped the SMP Linux port leaking a critical section on unprotect - #759

Open
fdesbiens wants to merge 1 commit into
eclipse-threadx:devfrom
fdesbiens:fix-smp-unprotect-critical-section
Open

fdesbiens wants to merge 1 commit into
eclipse-threadx:devfrom
fdesbiens:fix-smp-unprotect-critical-section

Conversation

@fdesbiens

Copy link
Copy Markdown
Contributor

Fixes #757

_tx_thread_smp_unprotect takes _tx_linux_mutex on entry and releases it twice when the protection structure names this core, but only once when it does not, while the matching _tx_thread_smp_protect took it once either way. _tx_thread_system_return clears the protection outright, so an unprotect can find it no longer naming its core and return one release short.

_tx_linux_mutex_release_all drains such a level, but the timer interrupt thread reaches one on its tick path only when a thread was preempted on core 0. A level leaked there while core 0 is idle is never recovered: the count never reaches zero, pthread_mutex_unlock is never called again, and every other thread waits on the mutex for the life of the process.

The release now happens whether or not the protection still names this core. Bookkeeping and scheduling are unchanged, and over-releasing was already a no-op.

Two hung processes captured untraced, in different tests, show the timer thread owning the mutex with a nesting count of one while parked in its tick wait and the scheduler blocked in pthread_mutex_timedlock, with the clock still advancing and every other value byte-identical across a ten second sample. On a healthy run that path is taken 0 times in 26,546 unprotect calls, and forcing it leaks one level before this change and balances after.

No regression test ships with this: the trigger is a window inside the port's own critical-section primitive, which no ThreadX API can construct.

_tx_thread_smp_unprotect takes the Linux mutex on entry and releases it twice when the
protection structure names this core, but only once when it does not, while the matching
_tx_thread_smp_protect took it once either way. _tx_thread_system_return clears the
protection outright, so an unprotect can find it no longer naming its core and return
having released one level fewer than were taken.

A thread that later reaches _tx_linux_mutex_release_all drains that level. The timer
interrupt thread has none on its tick path unless a thread was preempted on core 0, so a
level leaked there while core 0 is idle is never recovered: the nesting count never
reaches zero, the mutex is never handed back to Linux, and every other thread waits on
it for the life of the process while the tick keeps arriving.

The release of the level the matching protect took now happens whether or not the
protection still names this core. Protection bookkeeping and scheduling are unchanged,
and releasing beyond what a thread holds was already a no-op.

Two hung processes captured untraced, in different tests, show the timer thread owning
the mutex with a nesting count of one while parked in its tick wait, the scheduler
blocked in pthread_mutex_timedlock, and the clock advancing 99 ticks a second across a
ten second sample while nothing else changes. On a healthy run that path is taken 0
times in 26,546 unprotect calls. Forcing it on the timer thread leaks one level before
this change and balances after it.

Assisted-by: Claude Code (Opus 5) <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant