Skip to content

Only touch currentForklessSave on the main thread - #4467

Merged
JimB123 merged 2 commits into
valkey-io:forklessfrom
nitaicaro:forkless-fix-currentforklesssave-thread
Aug 20, 2026
Merged

Only touch currentForklessSave on the main thread#4467
JimB123 merged 2 commits into
valkey-io:forklessfrom
nitaicaro:forkless-fix-currentforklesssave-thread

Conversation

@nitaicaro

@nitaicaro nitaicaro commented Aug 18, 2026

Copy link
Copy Markdown
Contributor

currentForklessSave was cleared in forklessSaveProcessor, which runs on the worker thread, while forklessSaveCancel reads it (and its iterator) on the main thread. Two threads writing/reading the same pointer with no lock is a data race that can crash the cancel path.

The contract is that we always pass an ITEM_CLOSE when we finish, which causes the cleanup function to get called which clears up currentForklessSave anyway, so this should be safe.

Update: that cleanup runs after the async file close, but forklessSaveComplete nulls the iterator earlier - so a BGSAVE CANCEL in that window would terminate a NULL iterator and crash. So I also clear currentForklessSave where we null the iterator in forklessSaveComplete

currentForklessSave was cleared in forklessSaveProcessor, which runs on
the worker thread, while forklessSaveCancel reads it (and its iterator)
on the main thread. Two threads writing/reading the same pointer with no
lock is a data race that can crash the cancel path.

Remove the worker-thread clear. The pointer is already cleared on the
main thread on every end path: cleanupSaveInfoAndEmitEndMetrics() for a
save that started, and the werr path in forklessSaveToDisk() if it
failed to start. Now only the main thread accesses currentForklessSave,
so cancel can no longer race the clear.

Signed-off-by: Nitai Caro <caronita@amazon.com>
@coderabbitai

coderabbitai Bot commented Aug 18, 2026

Copy link
Copy Markdown

Important

Review skipped

Auto reviews are disabled on base/target branches other than the default branch.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro Plus

Run ID: bee1e35d-3427-4f22-98a6-e5ec567b61d3

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@valkey-review-bot valkey-review-bot Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

The worker-thread write is removed, but the pointer now outlives the iterator and leaves a main-thread cancellation crash window.

Comment thread src/forkless.c
currentForklessSave is the handle forklessSaveCancel() uses to terminate
the running iterator. forklessSaveComplete() set the iterator to NULL but
left currentForklessSave non-NULL until the later asynchronous
file-close cleanup. During that window a BGSAVE CANCEL would call
forklessSaveCancel() and pass the now-NULL iterator to
bgIteratorTerminate(), which dereferences it.

Clear currentForklessSave together with the iterator so the cancel
handle never outlives what it cancels. It stays main-thread-only.

Signed-off-by: Nitai Caro <caronita@amazon.com>
@codecov

codecov Bot commented Aug 20, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 79.29%. Comparing base (0a50265) to head (c85c9bd).
⚠️ Report is 5 commits behind head on forkless.

Additional details and impacted files
@@             Coverage Diff              @@
##           forkless    #4467      +/-   ##
============================================
- Coverage     79.69%   79.29%   -0.41%     
============================================
  Files           175      175              
  Lines         94841    93222    -1619     
============================================
- Hits          75581    73916    -1665     
- Misses        19260    19306      +46     
Files with missing lines Coverage Δ
src/forkless.c 84.86% <100.00%> (ø)

... and 23 files with indirect coverage changes

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@JimB123
JimB123 merged commit 9e26ccd into valkey-io:forkless Aug 20, 2026
28 checks passed
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.

2 participants