Skip to content

tests(tail): fix race in untailable-symlink test - #13704

Open
eduardomourar wants to merge 1 commit into
uutils:mainfrom
eduardomourar:fix/tail-symlink-race-test
Open

tests(tail): fix race in untailable-symlink test#13704
eduardomourar wants to merge 1 commit into
uutils:mainfrom
eduardomourar:fix/tail-symlink-race-test

Conversation

@eduardomourar

@eduardomourar eduardomourar commented Aug 1, 2026

Copy link
Copy Markdown
Contributor

Fixes intermittent CI failures in test_tail::test_follow_name_replaced_by_symlink_is_untailable, e.g. this run (https://github.com/uutils/coreutils/actions/runs/30700365364/job/91373194296?pr=13688), which failed with:

tail: 'watched' has become inaccessible: No such file or directory instead of the expected has been replaced with an untailable symbolic link.

Root cause

The test swapped the watched file for a symlink via remove() followed by symlink_file() — two separate syscalls.
Under polling (--use-polling, 0.1s interval), the poller could observe the transient "file missing" state in between,
reporting "has become inaccessible" instead of the intended "untailable symlink" message. The test also only checked the process was alive 500ms after spawn, without confirming tail's initial read had completed — under scheduler contention the symlink swap could happen before tail's first read of the file, which legitimately follows symlinks (no different from any other file open) since the untailable-symlink guard only applies to the follow loop, not the initial read.

Fix

  • Wait for tail's initial content to actually appear in stdout before performing the swap.
  • Perform the swap as an atomic rename() over a freshly created symlink, so the intermediate missing-file state is never observable to the poller.

The test replaced the watched file with a symlink via remove()
followed by symlink_file(), letting the poller observe a transient
"file missing" state and report "has become inaccessible" instead of
"replaced with an untailable symbolic link". It also asserted the
process was alive 500ms after spawn without confirming tail's initial
read had completed, so under scheduler contention the swap could land
before tail's first (legitimately symlink-following) open of the
file, leaking the secret before the follow-loop's guard was active.

Wait for the initial content to appear in stdout before swapping, and
perform the swap as an atomic rename over a freshly created symlink
so the intermediate missing-file state is never observable.
@codspeed-hq

codspeed-hq Bot commented Aug 1, 2026

Copy link
Copy Markdown

Merging this PR will improve performance by 3.03%

⚠️ Different runtime environments detected

Some benchmarks with significant performance changes were compared across different runtime environments,
which may affect the accuracy of the results.

Open the report in CodSpeed to investigate

⚡ 1 improved benchmark
✅ 344 untouched benchmarks
⏩ 46 skipped benchmarks1

Performance Changes

Mode Benchmark BASE HEAD Efficiency
Simulation cksum_crc32b 40.9 ms 39.7 ms +3.03%

Tip

Curious why this is faster? Comment @codspeedbot explain why this is faster on this PR, or directly use the CodSpeed MCP with your agent.


Comparing eduardomourar:fix/tail-symlink-race-test (0079d4c) with main (0c8a3c7)

Open in CodSpeed

Footnotes

  1. 46 benchmarks were skipped, so the baseline results were used instead. If they were deleted from the codebase, click here and archive them to remove them from the performance reports.

@github-actions

github-actions Bot commented Aug 1, 2026

Copy link
Copy Markdown

GNU testsuite comparison:

Skip an intermittent issue tests/tail/retry (fails in this run but passes in the 'main' branch)
Skip an intermittent issue tests/tail/tail-n0f (fails in this run but passes in the 'main' branch)
Skipping an intermittent issue tests/cut/bounded-memory (passes in this run but fails in the 'main' branch)
Skipping an intermittent issue tests/date/date-locale-hour (passes in this run but fails in the 'main' branch)
Note: The gnu test tests/printf/printf-surprise is now being skipped but was previously passing.

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