Skip to content

Commit 5ac5640

Browse files
committed
Skeleton for an AsyncSubject/AsyncThrowingSubject implementation
apple#176
1 parent 4d50bf6 commit 5ac5640

File tree

5 files changed

+219
-0
lines changed

5 files changed

+219
-0
lines changed

Diff for: Sources/AsyncAlgorithms/AsyncAlgorithms.docc/AsyncAlgorithms.md

+1
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ This package has three main goals:
3131
- <doc:Reductions>
3232
- <doc:RemoveDuplicates>
3333
- <doc:Select>
34+
- <doc:Subject>
3435
- <doc:Throttle>
3536
- <doc:Timer>
3637
- <doc:Zip>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
# Subject
2+
3+
* Author(s):
4+
5+
[
6+
[Source](https://github.com/apple/swift-async-algorithms/blob/main/Sources/AsyncAlgorithms/AsyncSubject.swift),
7+
[Source](https://github.com/apple/swift-async-algorithms/blob/main/Sources/AsyncAlgorithms/AsyncThrowingSubject.swift) |
8+
[Tests](https://github.com/apple/swift-async-algorithms/blob/main/Tests/AsyncAlgorithmsTests/TestSubject.swift)
9+
]
10+
11+
## Introduction
12+
13+
14+
15+
## Proposed Solution
16+
17+
18+
19+
## Detailed Design
20+
21+
22+
23+
## Alternatives Considered
24+
25+
26+
27+
## Credits/Inspiration
28+

Diff for: Sources/AsyncAlgorithms/AsyncSubject.swift

+80
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
//===----------------------------------------------------------------------===//
2+
//
3+
// This source file is part of the Swift Async Algorithms open source project
4+
//
5+
// Copyright (c) 2022 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+
//
10+
//===----------------------------------------------------------------------===//
11+
12+
///
13+
public final class AsyncSubject<Element: Sendable>: AsyncSequence, Sendable {
14+
/// The iterator for an `AsyncSubject` instance.
15+
public struct Iterator: AsyncIteratorProtocol, Sendable {
16+
fileprivate let subject: AsyncSubject<Element>
17+
private var active: Bool = true
18+
19+
fileprivate init(_ subject: AsyncSubject<Element>) {
20+
self.subject = subject
21+
}
22+
23+
/// Await the next sent element or finish.
24+
public mutating func next() async -> Element? {
25+
guard active else {
26+
return nil
27+
}
28+
29+
let value = await withTaskCancellationHandler {
30+
await subject.next()
31+
} onCancel: { [subject] in
32+
subject.cancelNext()
33+
}
34+
35+
if let value {
36+
return value
37+
} else {
38+
active = false
39+
return nil
40+
}
41+
}
42+
}
43+
44+
fileprivate func cancelNext() {
45+
46+
}
47+
48+
fileprivate func next() async -> Element? {
49+
return await withUnsafeContinuation { (continuation: UnsafeContinuation<Element?, Never>) in
50+
fatalError()
51+
}
52+
}
53+
54+
func cancelSend() {
55+
fatalError()
56+
}
57+
58+
fileprivate func send(_ element: Element) async {
59+
fatalError()
60+
}
61+
62+
/// Create a new `AsyncSubject` given an element type.
63+
public init(element elementType: Element.Type = Element.self) { }
64+
65+
///
66+
public func send(_ element: Element) {
67+
fatalError()
68+
}
69+
70+
/// Send a finish to all awaiting iterations.
71+
/// All subsequent calls to `next(_:)` will resume immediately.
72+
public func finish() {
73+
74+
}
75+
76+
/// Create an `Iterator` for iteration of an `AsyncChannel`
77+
public func makeAsyncIterator() -> Iterator {
78+
Iterator(self)
79+
}
80+
}

Diff for: Sources/AsyncAlgorithms/AsyncThrowingSubject.swift

+93
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,93 @@
1+
//===----------------------------------------------------------------------===//
2+
//
3+
// This source file is part of the Swift Async Algorithms open source project
4+
//
5+
// Copyright (c) 2022 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+
//
10+
//===----------------------------------------------------------------------===//
11+
12+
///
13+
public final class AsyncThrowingSubject<Element: Sendable, Failure: Error>: AsyncSequence, Sendable {
14+
public struct Iterator: AsyncIteratorProtocol, Sendable {
15+
let subject: AsyncThrowingSubject<Element, Failure>
16+
var active: Bool = true
17+
18+
init(_ subject: AsyncThrowingSubject<Element, Failure>) {
19+
self.subject = subject
20+
}
21+
22+
public mutating func next() async throws -> Element? {
23+
guard active else {
24+
return nil
25+
}
26+
27+
do {
28+
let value = try await withTaskCancellationHandler {
29+
try await subject.next()
30+
} onCancel: { [subject] in
31+
subject.cancelNext()
32+
}
33+
34+
if let value = value {
35+
return value
36+
} else {
37+
active = false
38+
return nil
39+
}
40+
} catch {
41+
active = false
42+
throw error
43+
}
44+
}
45+
}
46+
47+
/// Create a new `AsyncThrowingSubject` given an element type.
48+
public init(_ elementType: Element.Type = Element.self) { }
49+
50+
fileprivate func cancelNext() {
51+
fatalError()
52+
}
53+
54+
fileprivate func next() async throws -> Element? {
55+
fatalError()
56+
}
57+
58+
fileprivate func cancelSend() {
59+
fatalError()
60+
}
61+
62+
fileprivate func send(_ element: Element) async {
63+
let continuation: UnsafeContinuation<Element?, Error>? = await withUnsafeContinuation { continuation in
64+
fatalError()
65+
}
66+
continuation?.resume(returning: element)
67+
}
68+
69+
fileprivate func terminateAll(error: Failure? = nil) {
70+
fatalError()
71+
}
72+
73+
///
74+
public func send(_ element: Element) {
75+
fatalError()
76+
}
77+
78+
/// Send an error to all awaiting iterations.
79+
/// All subsequent calls to `next(_:)` will resume immediately.
80+
public func fail(_ error: Error) where Failure == Error {
81+
terminateAll(error: error)
82+
}
83+
84+
/// Send a finish to all awaiting iterations.
85+
/// All subsequent calls to `next(_:)` will resume immediately.
86+
public func finish() {
87+
terminateAll()
88+
}
89+
90+
public func makeAsyncIterator() -> Iterator {
91+
Iterator(self)
92+
}
93+
}

Diff for: Tests/AsyncAlgorithmsTests/TestSubject.swift

+17
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
//===----------------------------------------------------------------------===//
2+
//
3+
// This source file is part of the Swift Async Algorithms open source project
4+
//
5+
// Copyright (c) 2022 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+
//
10+
//===----------------------------------------------------------------------===//
11+
12+
@preconcurrency import XCTest
13+
import AsyncAlgorithms
14+
15+
final class TestChannel: XCTestCase {
16+
17+
}

0 commit comments

Comments
 (0)