Skip to content

Commit d9c36c3

Browse files
committed
Task+SleepIndefinitley
1 parent df80bb8 commit d9c36c3

File tree

2 files changed

+132
-0
lines changed

2 files changed

+132
-0
lines changed
Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
//
2+
// Task+SleepIndefinitely.swift
3+
// swift-timeout
4+
//
5+
// Created by Simon Whitty on 02/06/2025.
6+
// Copyright 2025 Simon Whitty
7+
//
8+
// Distributed under the permissive MIT license
9+
// Get the latest version from here:
10+
//
11+
// https://github.com/swhitty/swift-timeout
12+
//
13+
// Permission is hereby granted, free of charge, to any person obtaining a copy
14+
// of this software and associated documentation files (the "Software"), to deal
15+
// in the Software without restriction, including without limitation the rights
16+
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
17+
// copies of the Software, and to permit persons to whom the Software is
18+
// furnished to do so, subject to the following conditions:
19+
//
20+
// The above copyright notice and this permission notice shall be included in all
21+
// copies or substantial portions of the Software.
22+
//
23+
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
24+
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
25+
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
26+
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
27+
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
28+
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
29+
// SOFTWARE.
30+
//
31+
32+
#if compiler(>=6)
33+
34+
extension Task<Never, Never> {
35+
36+
private typealias State = (isCancelled: Bool, continuation: CheckedContinuation<Never, any Error>?)
37+
38+
static func sleepIndefinitely() async throws -> Never {
39+
let state = Mutex<State>((isCancelled: false, continuation: nil))
40+
return try await withTaskCancellationHandler {
41+
try await withCheckedThrowingContinuation { continuation in
42+
let isCancelled = state.withLock {
43+
if $0.isCancelled {
44+
return true
45+
} else {
46+
$0.continuation = continuation
47+
return false
48+
}
49+
}
50+
if isCancelled {
51+
continuation.resume(throwing: _Concurrency.CancellationError())
52+
}
53+
}
54+
} onCancel: {
55+
let continuation = state.withLock {
56+
$0.isCancelled = true
57+
return $0.continuation
58+
}
59+
continuation?.resume(throwing: _Concurrency.CancellationError())
60+
}
61+
}
62+
}
63+
64+
#endif
Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
//
2+
// Task+SleepIndefinitelyTests.swift
3+
// swift-timeout
4+
//
5+
// Created by Simon Whitty on 02/06/2025.
6+
// Copyright 2025 Simon Whitty
7+
//
8+
// Distributed under the permissive MIT license
9+
// Get the latest version from here:
10+
//
11+
// https://github.com/swhitty/swift-timeout
12+
//
13+
// Permission is hereby granted, free of charge, to any person obtaining a copy
14+
// of this software and associated documentation files (the "Software"), to deal
15+
// in the Software without restriction, including without limitation the rights
16+
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
17+
// copies of the Software, and to permit persons to whom the Software is
18+
// furnished to do so, subject to the following conditions:
19+
//
20+
// The above copyright notice and this permission notice shall be included in all
21+
// copies or substantial portions of the Software.
22+
//
23+
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
24+
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
25+
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
26+
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
27+
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
28+
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
29+
// SOFTWARE.
30+
//
31+
32+
#if canImport(Testing)
33+
@testable import Timeout
34+
import Foundation
35+
import Testing
36+
37+
struct TaskSleepIndefinitelyTests {
38+
39+
@Test
40+
func throwsWhenInitiallyCancelled() async {
41+
let task = Task {
42+
try? await Task.sleep(nanoseconds: 60_000_000_000)
43+
try await Task.sleepIndefinitely()
44+
}
45+
46+
task.cancel()
47+
48+
await #expect(throws: CancellationError.self) {
49+
try await task.value
50+
}
51+
}
52+
53+
@Test
54+
func throwsWhenCancelled() async {
55+
let task = Task {
56+
try await Task.sleepIndefinitely()
57+
}
58+
59+
try? await Task.sleep(nanoseconds: 200_000)
60+
task.cancel()
61+
62+
await #expect(throws: CancellationError.self) {
63+
try await task.value
64+
}
65+
}
66+
}
67+
68+
#endif

0 commit comments

Comments
 (0)