Skip to content

Commit d80ac6b

Browse files
committed
Restore YieldingContinuation for now with deprecations to favor AsyncStream
1 parent b2439d4 commit d80ac6b

File tree

2 files changed

+201
-0
lines changed

2 files changed

+201
-0
lines changed

stdlib/public/Concurrency/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,7 @@ add_swift_target_library(swift_Concurrency ${SWIFT_STDLIB_LIBRARY_BUILD_TYPES} I
7979
AsyncThrowingStream.swift
8080
AsyncStream.cpp
8181
Deque.swift
82+
YieldingContinuation.swift
8283
${swift_concurrency_objc_sources}
8384

8485
SWIFT_MODULE_DEPENDS_LINUX Glibc
Lines changed: 200 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,200 @@
1+
//===----------------------------------------------------------------------===//
2+
//
3+
// This source file is part of the Swift.org open source project
4+
//
5+
// Copyright (c) 2020-2021 Apple Inc. and the Swift project authors
6+
// Licensed under Apache License v2.0 with Runtime Library Exception
7+
//
8+
// See https://swift.org/LICENSE.txt for license information
9+
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
10+
//
11+
//===----------------------------------------------------------------------===//
12+
13+
import Swift
14+
15+
internal final class _YieldingContinuationStorage: UnsafeSendable {
16+
var continuation: Builtin.RawUnsafeContinuation?
17+
}
18+
19+
@available(SwiftStdlib 5.5, *)
20+
@available(*, deprecated, message: "`YieldingContinuation` was replaced by `AsyncStream` and will be removed shortly.")
21+
public struct YieldingContinuation<Element, Failure: Error>: Sendable {
22+
let storage = _YieldingContinuationStorage()
23+
24+
/// Construct a YieldingContinuation.
25+
///
26+
/// This continuation type can be called more than once, unlike the unsafe and
27+
/// checked counterparts. Each call to the yielding functions will resume any
28+
/// awaiter on the next function. This type is inherently sendable and can
29+
/// safely be used and stored in multiple task contexts.
30+
public init() { }
31+
32+
/// Construct a YieldingContinuation with specific types including a failure.
33+
///
34+
/// This continuation type can be called more than once, unlike the unsafe and
35+
/// checked counterparts. Each call to the yielding functions will resume any
36+
/// awaiter on the next function. This type is inherently sendable and can
37+
/// safely be used and stored in multiple task contexts.
38+
public init(yielding: Element.Type, throwing: Failure.Type) { }
39+
40+
internal func _extract() -> UnsafeContinuation<Element, Error>? {
41+
let raw = Builtin.atomicrmw_xchg_acqrel_Word(
42+
Builtin.addressof(&storage.continuation),
43+
UInt(bitPattern: 0)._builtinWordValue)
44+
return unsafeBitCast(raw, to: UnsafeContinuation<Element, Error>?.self)
45+
}
46+
47+
internal func _inject(
48+
_ continuation: UnsafeContinuation<Element, Error>
49+
) -> UnsafeContinuation<Element, Error>? {
50+
let rawContinuation = unsafeBitCast(continuation, to: Builtin.Word.self)
51+
let raw = Builtin.atomicrmw_xchg_acqrel_Word(
52+
Builtin.addressof(&storage.continuation), rawContinuation)
53+
return unsafeBitCast(raw, to: UnsafeContinuation<Element, Error>?.self)
54+
}
55+
56+
/// Resume the task awaiting next by having it return normally from its
57+
/// suspension point.
58+
///
59+
/// - Parameter value: The value to return from an awaiting call to next.
60+
///
61+
/// Unlike other continuations `YieldingContinuation` may resume more than
62+
/// once. However if there are no potential awaiting calls to `next` this
63+
/// function will return false, indicating that the caller needs to decide how
64+
/// the behavior should be handled.
65+
public func yield(_ value: __owned Element) -> Bool {
66+
if let continuation = _extract() {
67+
continuation.resume(returning: value)
68+
return true
69+
}
70+
return false
71+
}
72+
73+
/// Resume the task awaiting the continuation by having it throw an error
74+
/// from its suspension point.
75+
///
76+
/// - Parameter error: The error to throw from an awaiting call to next.
77+
///
78+
/// Unlike other continuations `YieldingContinuation` may resume more than
79+
/// once. However if there are no potential awaiting calls to `next` this
80+
/// function will return false, indicating that the caller needs to decide how
81+
/// the behavior should be handled.
82+
public func yield(throwing error: __owned Failure) -> Bool {
83+
if let continuation = _extract() {
84+
continuation.resume(throwing: error)
85+
return true
86+
}
87+
return false
88+
}
89+
}
90+
91+
@available(SwiftStdlib 5.5, *)
92+
extension YieldingContinuation where Failure == Error {
93+
/// Await a resume from a call to a yielding function.
94+
///
95+
/// - Return: The element that was yielded or a error that was thrown.
96+
///
97+
/// When multiple calls are awaiting a produced value from next any call to
98+
/// yield will resume all awaiting calls to next with that value.
99+
@available(*, deprecated, message: "`YieldingContinuation` was replaced by `AsyncStream` and will be removed shortly.")
100+
public func next() async throws -> Element {
101+
var existing: UnsafeContinuation<Element, Error>?
102+
do {
103+
let result = try await withUnsafeThrowingContinuation {
104+
(continuation: UnsafeContinuation<Element, Error>) in
105+
existing = _inject(continuation)
106+
}
107+
existing?.resume(returning: result)
108+
return result
109+
} catch {
110+
existing?.resume(throwing: error)
111+
throw error
112+
}
113+
}
114+
}
115+
116+
@available(SwiftStdlib 5.5, *)
117+
extension YieldingContinuation where Failure == Never {
118+
/// Construct a YieldingContinuation with a specific Element type.
119+
///
120+
/// This continuation type can be called more than once, unlike the unsafe and
121+
/// checked counterparts. Each call to the yielding functions will resume any
122+
/// awaiter on the next function. This type is inherently sendable and can
123+
/// safely be used and stored in multiple task contexts.
124+
@available(*, deprecated, message: "`YieldingContinuation` was replaced by `AsyncStream` and will be removed shortly.")
125+
public init(yielding: Element.Type) { }
126+
127+
/// Await a resume from a call to a yielding function.
128+
///
129+
/// - Return: The element that was yielded.
130+
@available(*, deprecated, message: "`YieldingContinuation` was replaced by `AsyncStream` and will be removed shortly.")
131+
public func next() async -> Element {
132+
var existing: UnsafeContinuation<Element, Error>?
133+
let result = try! await withUnsafeThrowingContinuation {
134+
(continuation: UnsafeContinuation<Element, Error>) in
135+
existing = _inject(continuation)
136+
}
137+
existing?.resume(returning: result)
138+
return result
139+
}
140+
}
141+
142+
@available(SwiftStdlib 5.5, *)
143+
extension YieldingContinuation {
144+
/// Resume the task awaiting the continuation by having it either
145+
/// return normally or throw an error based on the state of the given
146+
/// `Result` value.
147+
///
148+
/// - Parameter result: A value to either return or throw from the
149+
/// continuation.
150+
///
151+
/// Unlike other continuations `YieldingContinuation` may resume more than
152+
/// once. However if there are no potential awaiting calls to `next` this
153+
/// function will return false, indicating that the caller needs to decide how
154+
/// the behavior should be handled.
155+
@available(*, deprecated, message: "`YieldingContinuation` was replaced by `AsyncStream` and will be removed shortly.")
156+
public func yield<Er: Error>(
157+
with result: Result<Element, Er>
158+
) -> Bool where Failure == Error {
159+
switch result {
160+
case .success(let val):
161+
return self.yield(val)
162+
case .failure(let err):
163+
return self.yield(throwing: err)
164+
}
165+
}
166+
167+
/// Resume the task awaiting the continuation by having it either
168+
/// return normally or throw an error based on the state of the given
169+
/// `Result` value.
170+
///
171+
/// - Parameter result: A value to either return or throw from the
172+
/// continuation.
173+
///
174+
/// Unlike other continuations `YieldingContinuation` may resume more than
175+
/// once. However if there are no potential awaiting calls to `next` this
176+
/// function will return false, indicating that the caller needs to decide how
177+
/// the behavior should be handled.
178+
@available(*, deprecated, message: "`YieldingContinuation` was replaced by `AsyncStream` and will be removed shortly.")
179+
public func yield(with result: Result<Element, Failure>) -> Bool {
180+
switch result {
181+
case .success(let val):
182+
return self.yield(val)
183+
case .failure(let err):
184+
return self.yield(throwing: err)
185+
}
186+
}
187+
188+
/// Resume the task awaiting the continuation by having it return normally
189+
/// from its suspension point.
190+
///
191+
/// Unlike other continuations `YieldingContinuation` may resume more than
192+
/// once. However if there are no potential awaiting calls to `next` this
193+
/// function will return false, indicating that the caller needs to decide how
194+
/// the behavior should be handled.
195+
@available(*, deprecated, message: "`YieldingContinuation` was replaced by `AsyncStream` and will be removed shortly.")
196+
public func yield() -> Bool where Element == Void {
197+
return self.yield(())
198+
}
199+
}
200+

0 commit comments

Comments
 (0)