swift
51c01a07 - [Concurrency] Fix failing concurrency tests on Linux (#90701)

Commit
35 days ago
[Concurrency] Fix failing concurrency tests on Linux (#90701) Fixes a use-after-free where a Task could be destroyed while another thread still held (and was about to release) that Task's status-record lock. The TaskStatusLock is a hybrid lock: an "is-locked" bit in the ActiveTaskStatus word plus a real mutex living in the Task's private storage. On unlock, withStatusRecordLock clears the bit via CAS *before* it releases the mutex, and it still touches the Task afterwards (to trace the status change and to unlock). Once the bit is clear, other threads may make lock-free progress on the Task. The Task Stealers change (ccaac5299bfd, rdar://160967177, #86082) moved the swift_task_enqueue for the Task's own Job inside that locked region (in flagAsAndEnqueueOnExecutor). The enqueue consumes the caller's only reference to the Task, and the wake/resume callers (completeFuture, runWaitingTask, resumeTaskAfterContinuation) hold no reference of their own. So once the Task was published, a worker could dequeue it, run it to completion, and release the last reference thus destroying the Task, including the mutex before the enqueuing thread finished the trace and unlock. That thread then operated on freed memory. Split "decide what to enqueue" from "enqueue it": rename swift_task_enqueueSelfOrStealer to swift_task_getSelfOrStealerForEnqueue, which does the status mutation under the lock and *returns* the Job to enqueue (the Task itself or a fresh stealer) instead of enqueuing it. flagAsAndEnqueueOnExecutor now calls swift_task_enqueue on that Job only after the lock has been released, so the Task is never reachable by a runner while we still need to touch it. It returns nullptr when there is nothing to enqueue (the async-let escalation path). swift_executor_escalate is exempt from the drop-the-lock rule and enqueues its returned Job while still holding the lock. Its callers must hold a refcount on the Task for the duration of the escalate, so the Task cannot be destroyed out from under it. Also, update the surrounding documentation to describe the new decide-then-enqueue contract and add a note at the unlock site in withStatusRecordLock warning that the Task must not have been published before that point unless the caller holds its own reference.
Author
Parents
Loading