|WARNING| [v2] Revert "examples/symmetric_mp: do not print or exit in handler"
dpdklab at iol.unh.edu
dpdklab at iol.unh.edu
Tue Oct 27 23:25:59 CET 2026
Test-Label: ai-code-review
Test-Status: WARNING
https://dpdk.org/patch/172195
_AI Code Review_
=== Patch Review: series.patch (via Claude) ===
Review date: 2026-10-27
# Review of DPDK Patch: Revert "examples/symmetric_mp: do not print or exit in handler"
## ERRORS
**1. Calling non-async-signal-safe functions in signal handler**
The `print_stats()` function, now used as a signal handler, calls `printf()` and `exit()`.
`printf()` is not async-signal-safe and can deadlock if the signal interrupts a malloc/free
or another stdio operation. This violates POSIX signal safety requirements.
```c
static void
print_stats(int signum)
{
unsigned i;
printf("\n"); /* ERROR: printf() not async-signal-safe */
for (i = 0; i < num_ports; i++) {
printf("Port %u: RX - %u, TX - %u, Drop - %u\n", ...); /* ERROR */
}
exit(0); /* ERROR: exit() not async-signal-safe */
}
```
**Suggested fix:** Use the atomic flag pattern that the reverted commit introduced.
Set a `volatile sig_atomic_t quit` flag in the handler, check it in the main loop,
and call `print_stats()` from normal context after the loop exits:
```c
static volatile sig_atomic_t quit;
static void
signal_handler(int signum)
{
(void)signum; /* unused parameter */
quit = 1;
}
/* In main(): */
signal(SIGINT, signal_handler);
signal(SIGTERM, signal_handler);
/* In lcore_main(): */
while (quit == 0) {
/* forwarding loop */
}
/* After rte_eal_mp_wait_lcore(): */
print_stats();
rte_eal_cleanup();
```
The commit message acknowledges the reverted approach was correct but claims it "crashes the secondaries"
when the primary calls `rte_eal_cleanup()`. That is a separate bug in the shutdown sequence,
not a reason to reintroduce unsafe signal handling.
The correct fix is to make the shutdown sequence safe (e.g., stop secondaries first, or use process monitoring),
not to avoid `rte_eal_cleanup()`.
---
**2. Unreachable code after infinite loop**
The `lcore_main()` function now has an infinite `for (;;)` loop with no exit condition,
making the `return 0;` removal correct but also ensuring the function never returns.
Combined with the unsafe signal handler that calls `exit(0)`, this means `rte_eal_cleanup()`
in `main()` is unreachable.
```c
static void
lcore_main(void *arg __rte_unused)
{
/* ... */
for (;;) { /* infinite loop, never exits */
/* forwarding */
}
/* removed: return 0; */
}
/* In main(): */
rte_eal_mp_remote_launch(lcore_main, NULL, CALL_MAIN);
/* removed: rte_eal_mp_wait_lcore(); */
/* removed: print_stats(); */
/* clean up the EAL */
rte_eal_cleanup(); /* UNREACHABLE */
```
The signal handler jumps straight to `exit(0)`, so `main()` never regains control
and `rte_eal_cleanup()` is never called. This leaks EAL resources.
**Suggested fix:** Restore the `while (quit == 0)` loop and `rte_eal_mp_wait_lcore()` call.
---
**3. Missing parameter documentation**
The `print_stats()` function signature changed from `void` to `int signum`,
but the parameter is used only to match the signal handler prototype and is never read.
The comment does not document the parameter, and no `(void)signum;` or `__rte_unused` annotation is present.
```c
static void
print_stats(int signum) /* parameter undocumented and unused */
{
unsigned i;
/* signum is never referenced */
```
**Suggested fix:** Add `__rte_unused` attribute or cast to void:
```c
static void
print_stats(int signum __rte_unused)
{
/* ... */
}
```
Or:
```c
static void
print_stats(int signum)
{
(void)signum;
/* ... */
}
```
Note: The commit message says "v2 - need to mark arg unused" but the patch does not actually do this.
---
## WARNINGS
**1. Release notes missing for behavior change**
Reverting a previous fix that addressed unsafe signal handling is a behavior change
that affects application stability. The release notes should document this
(even for example code, since the multi-process examples are widely referenced).
---
## INFO
**1. Comment accuracy**
The new comment says "This is not safe way to exit for a real production application; it is just an example."
This is accurate -- calling `printf()` and `exit()` in a signal handler is indeed unsafe --
but it reads as justification for known-broken code in a shipped example.
DPDK examples should demonstrate correct patterns, not anti-patterns with disclaimers.
The comment further states "Doing proper shutdown with primary/secondary requires process monitoring
and rendezvous." This is true, but the reverted approach (atomic flag + cleanup in main context)
is a significant improvement over the current unsafe handler and does not require process monitoring --
it only requires the primary to finish its own cleanup before terminating,
which is what `rte_eal_cleanup()` is for.
---
## SUMMARY
This patch reintroduces three classes of bugs that the original commit fixed:
1. **Async-signal-safety violation** (`printf()` in handler can deadlock)
2. **Resource leak** (`rte_eal_cleanup()` is now unreachable)
3. **Incomplete implementation** (parameter marked unused in commit message but not in code)
The rationale for the revert -- that `rte_eal_cleanup()` crashes secondaries --
identifies a real issue but solves it by making the primary's shutdown equally broken.
The correct fix is to address the primary/secondary shutdown race,
not to revert to unsafe signal handling.
Review-Result: ERROR
More information about the test-report
mailing list