Skip to content

cpu/native: fix lockup on libucontext#21915

Merged
maribu merged 1 commit intoRIOT-OS:masterfrom
maribu:cpu/native/libucontext/fix-lockup
Nov 26, 2025
Merged

cpu/native: fix lockup on libucontext#21915
maribu merged 1 commit intoRIOT-OS:masterfrom
maribu:cpu/native/libucontext/fix-lockup

Conversation

@maribu
Copy link
Member

@maribu maribu commented Nov 25, 2025

Contribution description

The setcontext() implementation of glibc does restore the signal mask to the target thread during the switch, libucontext does not

Instead, we just manually enable signals again just before the call to setcontext().

With this, tests like tests/core/mutex_canel or tests/core/irq now pass on native64 when using libucontext.

Testing procedure

Run

make BOARD=native64 -C tests/core/irq
make BOARD=native64 -C tests/core/mutex_cancel 

On master e.g. using Alpine Linux

git:(master) ~/Repos/software/RIOT/master ➜ make BOARD=native64 flash test -j -C tests/core/irq         
make: Entering directory '/home/maribu/Repos/software/RIOT/master/tests/core/irq'
Building application "tests_irq" for "native64" with CPU "native".
[...]
RIOT native interrupts/signals initialized.
TZ not set, setting UTC
RIOT native64 board initialized.
RIOT native hardware initialization complete.

main(): This is RIOT! (Version: 2026.01-devel-164-gb8c553)
START
busy_thread created
xtimer_wait()
busy_thread starting
Timeout in expect script at "child.expect('SUCCESS')" (tests/core/irq/tests/01-run.py:9)

make: *** [/home/maribu/Repos/software/RIOT/master/makefiles/tests/tests.inc.mk:32: test] Error 1
make: Leaving directory '/home/maribu/Repos/software/RIOT/master/tests/core/irq'
git:(master) ~/Repos/software/RIOT/master ➜ make BOARD=native64 flash test -j -C tests/core/mutex_cancel
make: Entering directory '/home/maribu/Repos/software/RIOT/master/tests/core/mutex_cancel'
Building application "tests_mutex_cancel" for "native64" with CPU "native".
[...]
RIOT native interrupts/signals initialized.
TZ not set, setting UTC
RIOT native64 board initialized.
RIOT native hardware initialization complete.

main(): This is RIOT! (Version: 2026.01-devel-164-gb8c553)
Test Application for mutex_cancel / mutex_lock_cancelable
=========================================================

Test without cancellation: OK
Test early cancellation: OK
Verify no side effects on subsequent calls: Timeout in expect script at "child.expect("TEST PASSED")" (tests/core/mutex_cancel/tests/01-run.py:16)

make: *** [/home/maribu/Repos/software/RIOT/master/makefiles/tests/tests.inc.mk:32: test] Error 1
make: Leaving directory '/home/maribu/Repos/software/RIOT/master/tests/core/mutex_cancel'

This PR

git:(cpu/native/libucontext/fix-lockup) ~/Repos/software/RIOT/master ➜ make BOARD=native64 flash test -j -C tests/core/irq
make: Entering directory '/home/maribu/Repos/software/RIOT/master/tests/core/irq'
Building application "tests_irq" for "native64" with CPU "native".
[...]
RIOT native interrupts/signals initialized.
TZ not set, setting UTC
RIOT native64 board initialized.
RIOT native hardware initialization complete.

main(): This is RIOT! (Version: 2026.01-devel-165-g8c44f-cpu/native/libucontext/fix-lockup)
START
busy_thread created
xtimer_wait()
busy_thread starting
main: return
{ "threads": [{ "name": "idle", "stack_size": 16384, "stack_used": 1048 }]}
{ "threads": [{ "name": "main", "stack_size": 20480, "stack_used": 2600 }]}
i: 119908691
j: 119908691
k: 119908691
SUCCESS
{ "threads": [{ "name": "busy_thread", "stack_size": 20480, "stack_used": 2552 }]}

Process already stopped
make: Leaving directory '/home/maribu/Repos/software/RIOT/master/tests/core/irq'
git:(cpu/native/libucontext/fix-lockup) ~/Repos/software/RIOT/master ➜ make BOARD=native64 flash test -j -C tests/core/mutex_cancel 
make: Entering directory '/home/maribu/Repos/software/RIOT/master/tests/core/mutex_cancel'
Building application "tests_mutex_cancel" for "native64" with CPU "native".
[...]
RIOT native interrupts/signals initialized.
TZ not set, setting UTC
RIOT native64 board initialized.
RIOT native hardware initialization complete.

main(): This is RIOT! (Version: 2026.01-devel-165-g8c44f-cpu/native/libucontext/fix-lockup)
Test Application for mutex_cancel / mutex_lock_cancelable
=========================================================

Test without cancellation: OK
Test early cancellation: OK
Verify no side effects on subsequent calls: OK
Test late cancellation: OK
TEST PASSED
{ "threads": [{ "name": "idle", "stack_size": 16384, "stack_used": 1296 }]}
{ "threads": [{ "name": "main", "stack_size": 20480, "stack_used": 2728 }]}

Process already stopped
make: Leaving directory '/home/maribu/Repos/software/RIOT/master/tests/core/mutex_cancel'

Issues/PRs references

None

@github-actions github-actions bot added Platform: native Platform: This PR/issue effects the native platform Area: cpu Area: CPU/MCU ports labels Nov 25, 2025
@maribu maribu added Type: bug The issue reports a bug / The PR fixes a bug (including spelling errors) CI: ready for build If set, CI server will compile all applications for all available boards for the labeled PR and removed Area: cpu Area: CPU/MCU ports labels Nov 25, 2025
@crasbe crasbe added the Area: cpu Area: CPU/MCU ports label Nov 25, 2025
@riot-ci
Copy link

riot-ci commented Nov 25, 2025

Murdock results

✔️ PASSED

87af1a4 cpu/native: fix lockup on libucontext

Success Failures Total Runtime
10932 0 10932 13m:14s

Artifacts

Copy link
Contributor

@mguetschow mguetschow left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I have reproduced the bug with the patched tinybuild-native64 from RIOT-OS/riotdocker#259 (comment) using BUILD_IN_DOCKER=1 DOCKER_IMAGE=local/tinybuild-native64:latest make -C tests/core/irq all test.

With the changes from this PR applied, both this one and tests/core/mutex_cancel successfully complete.

Thanks for finding and fixing! I haven't looked much into the changes themselves though, being quite unfamiliar with native context switching, trusting your expertise here.

The `setcontext()` implementation of glibc does restore the signal
mask to the target thread during the switch, libucontext [does not][1]

[1]: https://man.archlinux.org/man/libucontext.3.en#CAVEATS

Instead, we just manually enable signals again just before the call
to `setcontext()`.

With this, tests like `tests/core/mutex_canel` or `tests/core/irq` now
pass on `native64` when using libucontext.

Co-authored-by: crasbe <crasbe@gmail.com>
@maribu maribu force-pushed the cpu/native/libucontext/fix-lockup branch from 1aa87c4 to 87af1a4 Compare November 26, 2025 11:43
@maribu maribu enabled auto-merge November 26, 2025 11:43
@maribu maribu added this pull request to the merge queue Nov 26, 2025
Merged via the queue into RIOT-OS:master with commit ed50187 Nov 26, 2025
25 checks passed
@maribu maribu deleted the cpu/native/libucontext/fix-lockup branch November 26, 2025 15:09
@maribu
Copy link
Member Author

maribu commented Nov 26, 2025

Thx ❤️

@leandrolanzieri leandrolanzieri added this to the Release 2026.01 milestone Jan 13, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Area: cpu Area: CPU/MCU ports CI: ready for build If set, CI server will compile all applications for all available boards for the labeled PR Platform: native Platform: This PR/issue effects the native platform Type: bug The issue reports a bug / The PR fixes a bug (including spelling errors)

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants