Skip to content

Commit 43dd488

Browse files
committed
Add [weak self] in two places.
The reason why I am making the first change is because in a separate PR in swiftlang I am fixing a bug that caused certain captured parameters to be treated as sending parameters incorrectly. This allowed for parameters to incorrectly be allowed to be sent from one isolation domain to another. The specific problem here can be seen with the following swift code: ```swift actor B { init(callback: @escaping @sendable () -> Void) async {} } actor A { private func poke() {} func schedule() async { _ = await B( callback: { [weak self] in // closure 1 Task.detached { // closure 2 await self?.poke() } }) } } ``` When we capture the weak self from closure 1 in closure 2, we are not actually capturing self directly. Instead we are capturing the var box which contains the weak self. The box (unlike self) is actually non-Sendable. Since closure 2 is not call(once), the compiler must assume semantically that the closure can be invoked potentially multiple times meaning that it cannot allow for self to be used in Task.detached. The fix for this is to perform an inner [weak self] capture. As follows: ```swift actor A { private func poke() {} func schedule() async { _ = await B( callback: { [weak self] in // closure 1 Task.detached { [weak self] // closure 2 await self?.poke() } }) } } ``` The reason why this works is that when we form the second weak self binding, we perform a load from the outer weak self giving us an Optional<A>. Then we store that optional value back into a new weak box. Since Optional<A> is Sendable, we know that the two non-Sendable weak var boxes are completely unrelated, so we can send that new var box into the new Task.detached safely. The second `[weak self]` is just something I noticed later in the function. The `[weak self]` just makes the detached function safer.
1 parent 79964c8 commit 43dd488

File tree

1 file changed

+3
-3
lines changed

1 file changed

+3
-3
lines changed

Sources/SemanticIndex/TaskScheduler.swift

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -433,7 +433,7 @@ package actor TaskScheduler<TaskDescription: TaskDescriptionProtocol> {
433433
priority: priority ?? Task.currentPriority,
434434
description: taskDescription,
435435
taskPriorityChangedCallback: { [weak self] (newPriority) in
436-
Task.detached(priority: newPriority) {
436+
Task.detached(priority: newPriority) { [weak self] in
437437
// If the task's priority got elevated, there might be an execution slot for it now. Poke the scheduler
438438
// to run the task if possible.
439439
await self?.poke()
@@ -442,11 +442,11 @@ package actor TaskScheduler<TaskDescription: TaskDescriptionProtocol> {
442442
executionStateChangedCallback: executionStateChangedCallback
443443
)
444444
pendingTasks.append(queuedTask)
445-
Task.detached(priority: priority ?? Task.currentPriority) {
445+
Task.detached(priority: priority ?? Task.currentPriority) { [weak self] in
446446
// Poke the `TaskScheduler` to execute a new task. If the `TaskScheduler` is already working at its capacity
447447
// limit, this will not do anything. If there are execution slots available, this will start executing the freshly
448448
// queued task.
449-
await self.poke()
449+
await self?.poke()
450450
}
451451
return queuedTask
452452
}

0 commit comments

Comments
 (0)