Pitch #3: ConcurrentValue and @concurrent closures

I just posted draft #4 in a new pitch thread incorporating much of the feedback received in the last few days. I think it is best to discuss this draft on that thread, but I wanted to acknowledge some points here.

Yes, I agree with you. The new draft splits the protocol into a ConcurrentValue and UnsafeConcurrentValue protocol, and argues that class conformance should always use the Unsafe version of this. I'd appreciate your thoughts on pitch 4 thread.

Agreed, same comment. Flipping the parity of the proposal to require explicit conformance and John McCall's suggestion of adding an UnsafeConcurrentValue has provided a really nice model for this that seems clean.

The problem here is that you don't get transitive memory safety. Consider Array<NSMutableString> for example. While you can pass the array itself around, dereferencing the pointers to the strings inside of it would cause race conditions given the possibility for mutation. One major purpose of this proposal is to define away safety problems like that.

They aren't out of the game. The proposal provides a number of ways to address this with library-based solutions, see for example this section. Those library solutions are not part of this proposal though, they should be subsequent proposals.

I'm not exactly sure how all the rules would stack out here, but I'm concerned about the UX that this would provide. With the "concurrent closures capture by value" rule, you get simple and predictable errors when you try to mutate values inside of the closure, and an easy way to explain the issue to the user.

With a flow sensitive rule, you get errors at the runDetatched call site because you have an assignment to x (potentially much later in the function). x may also be captured by other sync closures in the function, so you get transitive lifetime checking issues, which can be done with dataflow but make it even more difficult to explain to users.

I agree that this is an important concern. I think it makes sense to add a clang attributes to the key APIs (eg. dispatch) to control whether blocks get imported as concurrent closures in Swift. Perhaps the default should be that they should come in as concurrent closures if escaping, but there is an attribute to turn that off? I'm not sure the best sense of this. I don't think that thin function pointers need to be concurrent because they can't capture anything.

Yes, I think there is another missing link in the actor type system that is worth exploring. I'm not sure if the exploration goes anywhere, though. The observation is that in:

actor SomeActor {
  func foo() {}
  func test(other: SomeActor) {
     other.foo()  // ill formed because of cross actor reference
     self.foo()    // ok
  }
}

Here we have two values of SomeActor type: "other" and "self". However, other is actually has something more like a async SomeActor type (and we allow omitting the async) and self is more like sync SomeActor (which we currently cannot spell).

If we had the ability to spell this, it would allow a lot of interesting things, including proper exposure of unsafe APIs that poke at the internals of an actor across actor boundaries, etc.

Anyway, I have a picture in my brain of what this could look like, but it isn't fully built out, I'll try to throw some e-ink to e-paper about this this weekend if I have time to bake them out a bit better.

Good questions. I changed the wording to be a bit more specific (with your help :), thank you!). The intent is that global functions automatically get @concurrent function type when their signatures permit it because they cannot capture any values. I think it is reasonable to allow an explicit @concurrent as well, but that would be redundant from the general inference. If one specified it, but a non-ConcurrentValue argument/result type is specified, we should diagnose the error.

The reason for this is a desire to provide a consistent model that scales from global functions to nested functions to closures.

In any case, the proposal has been updated quite a bit with some pretty big design changes, I think it would be best to continue the discussion in the pitch #4 thread. Thank you everyone for the input!

-Chris

2 Likes