Discussion: Unavailability from asynchronous contexts

Alright, taking into account the feedback here; I have most of an initial implementation that should be mostly ready: GitHub - etcwilde/swift at ewilde/concurrency/UnavailableFromAsync-attribute.
I still need to write up the full evolution proposal and incorporate a few things.

After various discussions, the performance cost of either of the guaranteed checking is too expensive and would need far more consideration as it adds another color to functions. As a result, I've gone with a thinner model, so it will be possible to circumvent the checking by wrapping unavailable calls in a synchronous closure and calling that. The attribute can only be placed on function declarations and on constructor declarations, but not on destructors since we can't guarantee where those will be called and they should be safe to use from anywhere.

Since it is thin checking, we don't need a way to stop the mechanism when the API are actually being held safely, since wrapping the unsafe function in another function or closure will suffice. It is up to the API developers to ensure that they are propagating that annotation correctly when working with mutexes, semaphores, and thread-local storage.

The annotation will start as a warning in Swift 5.6(?) and turn into a proper hard error in Swift 6.

In accommodating the discussion, I've made the following changes:

  • The checking is weak, only looking at the declarations used in async contexts
  • The annotation can only be used on constructors and function declarations
  • The spelling is @unavailableFromAsync.

In order to get this implemented fairly quickly, I haven't added any of the bells nor the whistles yet.

Yep, that sounds like a good reason not to go down this route to me.

I went down the @available route to match up with the direction @completionHandlerAsync took.
This is kind of similar to that, but with a harder error. The difference being that this should be a harder error about correctness, while that is more of a suggestion. I do like using a different syntax though as it simplifies the implementation a fair bit. :slight_smile: Maybe not the right reason, but it works for me.

An optional message: or renamed: probably makes sense in most cases. Most things should be wrappable in something like the pthread_mutex_with_lock function described above, though I'm not sure all of them will be.

Since we aren't doing the strong checking, I don't think we need an unsafe or unchecked flag anymore; wrapping the call in a synchronous closure call will have the same effect as not checking it.

@George, I think we could accommodate this here. While I'm going back-and-forth on whether the goals align better with [Pitch] @completionHandlerAsync attribute - #27 by bnbarham, it might make sense to have that be part of this attribute. A spelling could look something like @unavailableFromAsync(weak, message: "This is slow") for the blocking, but not technically wrong, or @unavailableFromAsync(message: "This causes resource starvation") for the "it's wrong, and you'll hurt if you do this". The weak form would stay a warning in swift 6, while the strong form will eventually be an error.

1 Like