# \[pre-pitch\] Nonisolated nonsending Clock protocol

**URL:** <https://forums.swift.org/t/pre-pitch-nonisolated-nonsending-clock-protocol/89960>\
**Category:** Pitches\
**Created:** [October 8, 2026, 6:41pm UTC](https://forums.swift.org/t/pre-pitch-nonisolated-nonsending-clock-protocol/89960 "2026-10-08T18:41:38Z")\
**Posts on this page:** 9\
**Page:** 1

<div class="post-metadata">

**Author:** ![mbrandonw](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/mbrandonw/32/349_2.png) [@mbrandonw](https://forums.swift.org/u/mbrandonw)\
**Post date:** [October 8, 2026, 6:41pm UTC](https://forums.swift.org/t/pre-pitch-nonisolated-nonsending-clock-protocol/89960/1 "2026-10-08T18:41:38Z")

</div>

I'd like to pre-pitch updating the `Clock` protocol to have a `nonisolated(nonsending)` sleep, just to get people's first impression.

The `Clock` protocol has one method requirement, and currently it is [`@concurrent`](https://github.com/swiftlang/swift/blob/d67cb16cc806e243ca9a1200b6e4209d1ed6dc20/stdlib/public/Concurrency/Clock.swift#L42). This means every time `clock.sleep` is called it will jump to the concurrent executor, whether it needs to or not. Other parts of the standard library have gotten the `nonisolated(nonsending)` treatment, such as `withTaskCancellationHandler` and `withCheckedContinuation`, so why not `Clock`?

And if you need a more concrete reason, consider a theoretical `ImmediateClock` conformance that does not actually suspend when asked to sleep. Such a clock can be handy in testing code where we don't actually care about the passage of time and just want to squash all of time down to a single instant.

With such a clock, what would you expect the following to print when run in a main.swift executable?

```swift
let clock = ImmediateClock()
var count = 0
let task = Task.immediate {
  while count < 1_000 {
    try await clock.sleep(for: .seconds(1), tolerance: nil)
    count += 1
  }
}
print("count", count)
try await task.value
print("count", count)

```

To me, since `Task.immediate` runs all synchronous code immediately, and since `clock.sleep` does not actually suspend in `ImmediateClock`'s implementation, I would hope the entire loop is done synchronously and "count 1000" is printed twice to the console.

But this does not happen. Instead "count 0" is printed first, and then "count 1000". This code does incur actual suspension points even though `ImmediateClock` implements `sleep` as a synchronous method. It is not possible to properly implement `ImmediateClock` using the `Clock` protocol alone. But if `Clock`'s `sleep` requirement were made `nonisolated(nonsending)`, this code would deterministically behave exactly how we want.

---

<div class="post-metadata">

**Author:** ![FranzBusch](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/franzbusch/32/19447_2.png) [@FranzBusch](https://forums.swift.org/u/FranzBusch)\
**Post date:** [October 8, 2026, 7:18pm UTC](https://forums.swift.org/t/pre-pitch-nonisolated-nonsending-clock-protocol/89960/2 "2026-10-08T19:18:28Z")

</div>

I agree that we need this but we also need to adopt `nonisolated(nonsending)` in the concrete `Clock` types, in `Task.sleep(clock)`, all the internal `async` methods that connect those two, and `Task.yield` as well. To motivate this more, this is required for performance. A sleep on the `MainExecutor` is currently hopping to a global executor thread and can cause priority inversions.

Now the tricky part is in the `Clock` protocol since adding `nonisolated(nonsending)` is an API breaking change. Coincidentally I was thinking about this today and I think we can stage this in by adding the new requirement with a default implementation that calls the existing `@concurrent`. Since all parameters are required to be `Sendable` this should be safe.

---

<div class="post-metadata">

**Author:** ![mbrandonw](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/mbrandonw/32/349_2.png) [@mbrandonw](https://forums.swift.org/u/mbrandonw)\
**Post date:** [October 8, 2026, 7:28pm UTC](https://forums.swift.org/t/pre-pitch-nonisolated-nonsending-clock-protocol/89960/3 "2026-10-08T19:28:43Z")

</div>

Yeah, updating all interconnected pieces to be `nonisolated(nonsending)` would definitely be part of the proposal.

> [@FranzBusch](#):
>
> Coincidentally I was thinking about this today and I think we can stage this in by adding the new requirement with a default implementation that calls the existing `@concurrent`.

To avoid needing to come up with a new name for this requirement we could just add `sleep(until:tolerance:isolation:)`. Unless that is what you mean?

---

<div class="post-metadata">

**Author:** ![mbrandonw](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/mbrandonw/32/349_2.png) [@mbrandonw](https://forums.swift.org/u/mbrandonw)\
**Post date:** [October 9, 2026, 6:24pm UTC](https://forums.swift.org/t/pre-pitch-nonisolated-nonsending-clock-protocol/89960/4 "2026-10-09T18:24:31Z")

</div>

I've got a draft of the changes [here](https://github.com/swiftlang/swift/compare/main...pointfreeco:swift:nins-clocks?expand=1). You mentioned `Task.yield()`, but as far as I can tell that does not involve clocks at all, so left it out.

---

<div class="post-metadata">

**Author:** ![grynspan](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/grynspan/32/15315_2.png) [@grynspan](https://forums.swift.org/u/grynspan)\
**Post date:** [October 9, 2026, 6:56pm UTC](https://forums.swift.org/t/pre-pitch-nonisolated-nonsending-clock-protocol/89960/5 "2026-10-09T18:56:39Z")

</div>

> [@FranzBusch](#):
>
> Now the tricky part is in the `Clock` protocol since adding `nonisolated(nonsending)` is an API breaking change.

`@abi` to the rescue?

---

<div class="post-metadata">

**Author:** ![FranzBusch](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/franzbusch/32/19447_2.png) [@FranzBusch](https://forums.swift.org/u/FranzBusch)\
**Post date:** [October 9, 2026, 7:17pm UTC](https://forums.swift.org/t/pre-pitch-nonisolated-nonsending-clock-protocol/89960/6 "2026-10-09T19:17:01Z")

</div>

> [@mbrandonw](#):
>
> I've got a draft of the changes [here](https://github.com/swiftlang/swift/compare/main...pointfreeco:swift:nins-clocks?expand=1)

That’s sadly not enough since it breaks ABI of a few methods.

> [@grynspan](#):
>
> `@abi` to the rescue?

Doesn’t work on protocols as far as I know.

---

<div class="post-metadata">

**Author:** ![mbrandonw](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/mbrandonw/32/349_2.png) [@mbrandonw](https://forums.swift.org/u/mbrandonw)\
**Post date:** [October 9, 2026, 7:41pm UTC](https://forums.swift.org/t/pre-pitch-nonisolated-nonsending-clock-protocol/89960/7 "2026-10-09T19:41:26Z")

</div>

> [@FranzBusch](#):
>
> That’s sadly not enough since it breaks ABI of a few methods.

Yeah, I don't claim it's the final code. I am not familiar with `@export(implementation)` / ABI to know what is and is not breaking.

I imagine whatever was done for [`withTaskCancellationHandler`](https://github.com/swiftlang/swift/pull/85518/changes) and [`withCheckedContinuation`](https://github.com/swiftlang/swift/pull/84944) can be done for `Clock.sleep(for:)` and `Task.sleep(for:clock:)`? I just pushed a commit to the [branch](https://github.com/swiftlang/swift/compare/main...pointfreeco:swift:nins-clocks?expand=1) to do that. Mostly shooting in the dark here, but is this theoretically possible to do in a backwards compatible way or is there something fundamentally different about adding `nonisolated(nonsending)` to clock-related APIs?

---

<div class="post-metadata">

**Author:** ![ktoso](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/ktoso/32/44526_2.png) [@ktoso](https://forums.swift.org/u/ktoso)\
**Post date:** [October 9, 2026, 11:16pm UTC](https://forums.swift.org/t/pre-pitch-nonisolated-nonsending-clock-protocol/89960/8 "2026-10-09T23:16:36Z")

</div>

> [@mbrandonw](#):
>
> I imagine whatever was done for [`withTaskCancellationHandler`](https://github.com/swiftlang/swift/pull/85518/changes) and [`withCheckedContinuation`](https://github.com/swiftlang/swift/pull/84944) can be done for `Clock.sleep(for:)` and `Task.sleep(for:clock:)`? I just pushed a commit to the [branch](https://github.com/swiftlang/swift/compare/main...pointfreeco:swift:nins-clocks?expand=1) to do that. Mostly shooting in the dark here, but is this theoretically possible to do in a backwards compatible way or is there something fundamentally different about adding `nonisolated(nonsending)` to clock-related APIs?

While I appreciate the interest and help these are very difficult to land changes for, especially since it is **protocol requirements** …

I’m not sure this is a good task for someone to contribute, we’ll have to do extensive testing and perhaps some trickery to land those changes anyway so I’m not sure this is actually very helpful to prepare PRs blind for like this…

It is a topic on my list of things to fix and will be looking at those soon though.

---

<div class="post-metadata">

**Author:** ![mbrandonw](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/mbrandonw/32/349_2.png) [@mbrandonw](https://forums.swift.org/u/mbrandonw)\
**Post date:** [October 12, 2026, 6:23pm UTC](https://forums.swift.org/t/pre-pitch-nonisolated-nonsending-clock-protocol/89960/9 "2026-10-12T18:23:18Z")

</div>

Sounds good, looking forward to seeing some movement on this.
