Skip to content

[6.2][Concurrency] Fix alreadyLocked in withStatusRecordLock. #81206

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
May 1, 2025

Conversation

mikeash
Copy link
Contributor

@mikeash mikeash commented Apr 30, 2025

Explanation: A locking bug can cause concurrent mutation of task records, resulting in crashes during concurrent task cancellation.
Scope: Affects Swift Concurrency code that cancels tasks.
Issue: rdar://150327908
Risk: Low, the problematic case now reloads the is-locked flag from the task with the lock held instead of using the existing value, which is definitely more correct. No other functional changes.
Testing: Added a concurrent cancellation test that verifies this fix.
Reviewer: @al45tair

Cherry-pick #81205 to release/6.2.

If the preloaded status is locked, then we need to reload it in order to distinguish between the current thread holding the lock and another thread holding the lock. Without this, if another thread holds the lock, then we won't set the is-locked bit. We'll still actually hold the lock, but other threads may perform operations locklessly if the bit is not set, which can cause a crash. By reloading status in that case, we ensure that the bit is always set correctly.

This manifested as crashes in task cancellation but could cause other task-related issues as well.

Also remove an assert of !isStatusRecordLocked() in AsyncTask::complete(). We allow other threads to access tasks and take the lock for things like cancellation, so the lock may legitimately be held at that point.

rdar://150327908

If the preloaded status is locked, then we need to reload it in order to distinguish between the current thread holding the lock and another thread holding the lock. Without this, if another thread holds the lock, then we won't set the is-locked bit. We'll still actually hold the lock, but other threads may perform operations locklessly if the bit is not set, which can cause a crash. By reloading status in that case, we ensure that the bit is always set correctly.

This manifested as crashes in task cancellation but could cause other task-related issues as well.

Also remove an assert of !isStatusRecordLocked() in AsyncTask::complete(). We allow other threads to access tasks and take the lock for things like cancellation, so the lock may legitimately be held at that point.

rdar://150327908
(cherry picked from commit 325b66a)
@mikeash mikeash requested a review from a team as a code owner April 30, 2025 17:57
@mikeash
Copy link
Contributor Author

mikeash commented Apr 30, 2025

@swift-ci please test

@mikeash mikeash merged commit 23e21ff into swiftlang:release/6.2 May 1, 2025
5 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.

4 participants