Isolation checking for `TaskLocal.withValue`

I'm not sure whether it's a problem with the implementation of [Accepted] SE-0420: Inheritance of actor isolation or a completely different issue, but I just ran into this problem with the async version of TaskLocal.withValue():

func withTaskLocal(
  isolation: isolated (any Actor)? = #isolation,
  _ body: (consuming NonSendableValue, isolated (any Actor)?) async throws -> Void
) async rethrows {
  try await $someTaskLocal.withValue(theValue) {
    // warning: Passing argument of non-sendable type 'NonSendableValue' outside of actor-isolated context may introduce data races; this is an error in the Swift 6 language mode
    try await body(NonSendableValue(), isolation)
  }
}

To me it looks like TaskLocal.withValue should be inheriting the isolation of the caller, but is instead treating its closure argument as nonisolated / @Sendable.

I don't think this should be related to SE-0431: `@isolated(any)` Function Types, because TaskLocal shouldn't be scheduling the closure concurrently?

This could all just be because these proposals are all in flux right now, but thought I should post about this in case it's a known issue / bug.

cc @ktoso

This likely falls into the category of issues that will be fixed with this: adopting #isolation in the stdlib -- the with...Continuation as well as withValue APIs all share the same issue of relying on the not-so-great @_unsafeInheritExecutor which we're replacing now with #isolation.

I'm actively working in this area over here: [Concurrency] Correct withContinuation APIs isolation handling by ktoso ยท Pull Request #72578 ยท apple/swift ยท GitHub which I'll actually extend to fix all APIs using this @_unsafeInheritExecutor attribute. This should address a bunch of issues like these while at it.

3 Likes

Uhm, I'm re-reading this snippet again and find it rather confusing -- the closure body is not async byt but you try await it and the body is kind of messed up with missing { etc...

I think this is about the following though:

func withTaskLocal(isolation: isolated (any Actor)? = #isolation,
                     _ body: (consuming NonSendableValue, isolated (any Actor)?) -> Void) async {
    Self.$local.withValue(12) {
      // warning: Passing argument of non-sendable type 'NonSendableValue' outside of actor-isolated context may introduce data races; this is an error in the Swift 6 language mode
      body(NonSendableValue(), isolation)
    }
  }

and yes this produces warning -- but actually I believe this is a bug in the non-sendable analysis and we're tracking that already. :slight_smile:

The above code is valid and should not produce a warning; it's the same issue as linked in the previous post PR.

1 Like

Thanks @ktoso! Sorry for the typos in the original snippet, I updated it and it should be valid syntax now.

1 Like

Thanks! Iโ€™ll double check this then :slight_smile:

This topic was automatically closed after 9 hours. New replies are no longer allowed.