I'm attempting to move from global isolation to a more-general isolated parameter. I thought for sure this was going to work but it does not. I have a guess as to what is going on here, but I'm looking for confirmation.

class NonSendable {
}

class TestThing {
	private let value = NonSendable()

	func notOk(isolation: isolated (any Actor)? = #isolation) {
		let op = {
			print(self.value)
		}

		Task {
			// ERROR: 'isolation'-isolated value of type '() async -> ()' passed as a strongly transferred parameter; later accesses could race
			op()
		}
	}

	@MainActor
	func ok() {
		let op = {
			print(self.value)
		}

		Task {
			op()
		}
	}
}

Capturing self in op is the key.

My guess is that in the ok example, self is implicitly Sendable because it is also implicitly MainActor-isolated. Or maybe op as a whole?

Either way, I don't fully understand why this is semantically different. How could later accesses race if the isolation both outside and inside of the Task in notOk is always the same?

I suspect that a possible explanation could be similar to what I've attempted to describe here: Ensuring _any_ isolation of a non-Sendable type - #2 by nkbelov

Calling notOk from actor A and then again from actor B captures op (and, in turn, the non-sendable self.value) into tasks isolated to two different actors.

2 Likes

Ahh, of course! Without the changes from Closure isolation control, I have to be really careful about how isolation is inherited within that Task.

Here's the solution:

	func notOk(isolation: isolated (any Actor)? = #isolation) {
		let op = {
			print(self.value)
		}

		Task {
			_ = isolation // this is critical to get the correct inheritance
			op()
		}
	}
3 Likes

TestThing is not Sendable though. It cannot be used from two different actors!

1 Like

Oh, my bad — I have been assuming for some reason that if an instance is protected by e.g. a global actor, like

@MainActor
static let testThing = TestThing()

this would make it so that other actors would still be able to reach out to it and call notOk each with their own isolation. Obviously, and luckily, this doesn't happen :upside_down_face:.

1 Like