Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feature adapt swift6 #37

Open
wants to merge 72 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
72 commits
Select commit Hold shift + click to select a range
f924c43
prepare typedThrow
pbk20191 May 30, 2024
3ed1069
Merge remote-tracking branch 'refs/remotes/origin/develop'
pbk20191 May 31, 2024
bfa0b13
adapt `fullTypedThrow`
pbk20191 May 31, 2024
cfd1e5d
implement custom isolation check
pbk20191 May 31, 2024
0d4a1cf
Merge remote-tracking branch 'refs/remotes/origin/develop'
pbk20191 Jun 1, 2024
8131b43
make properties private
pbk20191 Jun 1, 2024
c2fb0d0
Merge remote-tracking branch 'refs/remotes/origin/develop'
pbk20191 Jun 4, 2024
01e39cc
fix typedThrow compile issue
pbk20191 Jun 4, 2024
e7e8987
inline error conversion
pbk20191 Jun 4, 2024
58b389b
Merge remote-tracking branch 'refs/remotes/origin/master'
pbk20191 Jun 5, 2024
6e644cd
fix errors for swift 6
pbk20191 Jun 6, 2024
d9878ad
Combine&Concurrency: reimplement subscription model to fix internal b…
pbk20191 Jun 6, 2024
ee8468c
Combine & Concurrency: introduce asyncflatmap
pbk20191 Jun 7, 2024
686a892
fix test fail
pbk20191 Jun 7, 2024
7fbe6ff
change implementation to fix rare crash in test
pbk20191 Jun 7, 2024
30be947
Merge branch 'develop' into feature/swift6
pbk20191 Jun 7, 2024
42a0f21
Merge branch 'develop' into feature/swift6
pbk20191 Jun 7, 2024
d15e723
CoreData: interit Exeuctor attribute to make scheduledTaskType valid
pbk20191 Jun 7, 2024
6ea4d4d
Merge branch 'develop' into feature/swift6
pbk20191 Jun 7, 2024
1841e58
remove unused module import declaration
pbk20191 Jun 7, 2024
ab17557
suppress sendable warning in dedicated case
pbk20191 Jun 8, 2024
6977f08
suppress sendable warning on dedicated case
pbk20191 Jun 8, 2024
006f284
fix async iterator isolation
pbk20191 Jun 8, 2024
5b46411
Concurrency: suppress sendable warning on dedicated case
pbk20191 Jun 9, 2024
54c3762
AsyncFlatMap: fix test fail on precise demand, Notification: fix comp…
pbk20191 Jun 10, 2024
24d54b9
remove unwanted schema
pbk20191 Jun 10, 2024
77960fa
AsyncFlatMap: fix task leak and propagate cancellation to upstream pr…
pbk20191 Jun 10, 2024
f3c69be
Merge branch 'develop' into feature/swift6
pbk20191 Jun 10, 2024
8075e9d
Concurrency: mark support for isolation
pbk20191 Jun 10, 2024
aee8c09
CompatAsyncPublisher: fix compiler crash by assertion
pbk20191 Jun 10, 2024
7f58409
mapTask, TryMapTask: enqueue to separate task to prevent transformer…
pbk20191 Jun 10, 2024
9f15f94
Merge branch 'develop' into feature/swift6
pbk20191 Jun 10, 2024
18fbd5c
swift 6: fix compile fail
pbk20191 Jun 11, 2024
19664dc
ensure subscription deinitializer is called outside of lock
pbk20191 Jun 11, 2024
9d15737
Merge branch 'develop' into feature/swift6
pbk20191 Jun 11, 2024
4487005
AsyncFlatMap: adapting dedicated typedThrow
pbk20191 Jun 11, 2024
5ae5fa7
AsyncFlatMap: changing genernal protocol for better type conformance …
pbk20191 Jun 11, 2024
78b1932
fix DiscardingTaskGroup simulation stops too early.
pbk20191 Jun 17, 2024
a8c3e39
Merge remote-tracking branch 'refs/remotes/origin/develop'
pbk20191 Jun 17, 2024
2351dfd
fix discardingTaskgroup simulation ends too early
pbk20191 Jun 17, 2024
13d0a48
refine keep alive Taskgroup to use actor state
pbk20191 Jun 17, 2024
7f0b99b
Backport DiscardingTaskGroup: wrap defer stack with do block for imme…
pbk20191 Jun 17, 2024
507bffd
MultiMapTask: add downstreamLock to match reactive stream spec
pbk20191 Jun 21, 2024
39bb3b8
MapTasks: add locks to match Reactivestream spec on Subscriptions
pbk20191 Jun 21, 2024
1ce755c
Merge remote-tracking branch 'refs/remotes/origin/develop'
pbk20191 Jun 21, 2024
0edafc9
moving to swift 6, using package acces modifier.
pbk20191 Jun 26, 2024
124caf8
commit to test for library use case
pbk20191 Jun 27, 2024
9d018b5
Update README.md
pbk20191 Jun 28, 2024
404281d
add TaskLocal test failing case for request about issue
pbk20191 Jun 29, 2024
4e17f2d
Merge commit '9d018b535a1542270711e201ec80f76a629ad17f' into feature/…
pbk20191 Jun 29, 2024
632ae77
implement Serial Priority based RunLoopExecutor Source
pbk20191 Jul 5, 2024
d0b4e52
implement qos elevation of PriorityRunLoop. remove legacy runloop sch…
pbk20191 Jul 6, 2024
a54923b
compact qos algorithnm
pbk20191 Jul 6, 2024
4e8910a
fix available annotation
pbk20191 Jul 8, 2024
a2dde3c
Swift 6 Concurrency check 임시 대응
pbk20191 Jul 15, 2024
2b20d80
update readme and implment PruneMemory which clean up memory footprints
pbk20191 Jul 15, 2024
8a46f77
adpat new typedThrow syntax and refactor simulating DiscardTaskGroup
pbk20191 Jul 17, 2024
0fb2d87
resolve for swift6
pbk20191 Dec 19, 2024
1fc4ef1
fix test failure
pbk20191 Dec 19, 2024
f8b5c23
backport CoreDataContext perform method
pbk20191 Dec 21, 2024
8faf2ff
expose generic Error for coreData
pbk20191 Dec 21, 2024
a125334
hide wip executor class
pbk20191 Dec 21, 2024
cb4800e
refactor backport discardingTask
pbk20191 Dec 21, 2024
32073f6
disable PruneMemory feature in non-Objective-C platform
pbk20191 Dec 21, 2024
3ea51ab
ensure isolation for backport discardingTask usecase
pbk20191 Dec 21, 2024
c60b47f
refactor for concurrency checking
pbk20191 Jan 2, 2025
bc0ddb9
add isolation parameter
pbk20191 Jan 5, 2025
271ed0a
spawn child task for receive(subscription:) callback
pbk20191 Jan 6, 2025
e83bb22
adapt Swift Testing for some test case
pbk20191 Jan 26, 2025
f8d202d
implement moody::camel::ConcurrentQueue based RunLoop SerialExecutor
pbk20191 Jan 27, 2025
62ef0bb
migrate some test to Swift-Testing
pbk20191 Jan 28, 2025
8f68ec4
update RunLoopExecutor
pbk20191 Jan 30, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 24 additions & 0 deletions Package.resolved
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
{
"originHash" : "2fc989cc1b67d91eb25b0319fdf3b974c0dd91e42699a64bf0c693dd7dde9fab",
"pins" : [
{
"identity" : "swift-atomics",
"kind" : "remoteSourceControl",
"location" : "https://github.com/apple/swift-atomics.git",
"state" : {
"revision" : "cd142fd2f64be2100422d658e7411e39489da985",
"version" : "1.2.0"
}
},
{
"identity" : "swift-collections",
"kind" : "remoteSourceControl",
"location" : "https://github.com/apple/swift-collections.git",
"state" : {
"revision" : "94cf62b3ba8d4bed62680a282d4c25f9c63c2efb",
"version" : "1.1.0"
}
}
],
"version" : 3
}
95 changes: 89 additions & 6 deletions Package.swift
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
// swift-tools-version: 5.9
// swift-tools-version: 6.0
// The swift-tools-version declares the minimum version of Swift required to build this package.

import PackageDescription
Expand All @@ -18,28 +18,111 @@ let package = Package(
.library(
name: "Tetra",
targets: ["Tetra"]
)
),
],
dependencies: [
// Dependencies declare other packages that this package depends on.
// .package(url: /* package url */, from: "1.0.0"),
.package(url: "https://github.com/apple/swift-collections.git", .upToNextMajor(from: "1.1.0")),
.package(
url: "https://github.com/apple/swift-atomics.git",
.upToNextMajor(from: "1.2.0") // or `.upToNextMinor
),

],
targets: [
// Targets are the basic building blocks of a package. A target can define a module or a test suite.
// Targets can depend on other targets in this package, and on products in packages this package depends on.
.target(
name: "Namespace",
swiftSettings: [
.swiftLanguageMode(.v6)
]
),
.target(
name: "NamespaceExtension",
dependencies: [
"Namespace",
],
swiftSettings: [
.swiftLanguageMode(.v6)
]
),
.target(
name: "CriticalSection",
dependencies: [
.product(name: "Atomics", package: "swift-atomics"),

],
swiftSettings: [
.swiftLanguageMode(.v6),
.enableExperimentalFeature("StaticExclusiveOnly"),
.enableExperimentalFeature("RawLayout"),
.enableExperimentalFeature("BuiltinModule"),
]
),
.target(
name: "BackportDiscardingTaskGroup",
dependencies: [
"Namespace",
"CriticalSection",
],
swiftSettings: [
.enableUpcomingFeature("FullTypedThrows"),
.enableExperimentalFeature("IsolatedAny"),
.swiftLanguageMode(.v6)
]
),
.target(
name: "Tetra",
dependencies: [],
dependencies: [
.product(name: "DequeModule", package: "swift-collections"),
.product(name: "HeapModule", package: "swift-collections"),

"BackPortAsyncSequence",
"CriticalSection",
"BackportDiscardingTaskGroup",
"Namespace",
"NamespaceExtension"
],
swiftSettings: [
.enableUpcomingFeature("FullTypedThrows"),
.enableExperimentalFeature("IsolatedAny"),
.swiftLanguageMode(.v6)
]
),
.target(
name: "TetraConcurrentQueueShim",
linkerSettings: [
.linkedFramework("CoreFoundation")
]
),
.target(
name: "TetraRunLoopConcurrency",
dependencies: [
"TetraConcurrentQueueShim",
],
swiftSettings: [
.enableExperimentalFeature("StrictConcurrency=complete"),
.swiftLanguageMode(.v6)
]
),
.target(
name: "BackPortAsyncSequence",
dependencies: [ "Namespace"],
swiftSettings: [
.swiftLanguageMode(.v6),
]
),
.testTarget(
name: "TetraTests",
dependencies: [
"Tetra"
],
resources: [.process("Resources")]
resources: [.process("Resources")],
swiftSettings: [
.swiftLanguageMode(.v5)
]
)
]
],
cxxLanguageStandard: .cxx17
)
47 changes: 42 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -71,13 +71,16 @@ import Combine

let cancellable = (0..<20).publisher
.setFailureType(to: URLError.self)
.multiMapTask(maxTasks: .unlimited) { _ in
.multiMapTask(maxTasks: .unlimited) { _ throws(URLError) in

// Underlying Task is cancelled if subscription is cancelled before task completes.
do {
let (data, response) = try await URLSession.shared.data(from: URL(string: "https://google.com")!)
return .success(data) as Result<Data,URLError>
// below unsafe cancel is no-op, throw appropriate error to interrupt combine pipeline
// withUnsafeCurrentTask { $0?.cancel() }
return data
} catch {
return .failure(error as! URLError) as Result<Data,URLError>
throw (error as! URLError)
}
}.sink { completion in

Expand All @@ -101,8 +104,7 @@ import Tetra
continuation.yield(1)
continuation.finish()
// Underlying AsyncIterator and Task receive task cancellation if subscription is cancelled.
}.tetra.publisher
.catch{ _ in Empty().setFailureType(to: Never.self) }
}.tetra.bridge.tetra.publisher
.sink { number in

}
Expand Down Expand Up @@ -188,3 +190,38 @@ struct ContentView: View {
- more fine grained way to introduce extension methods (maybe something like `.af` in Alamofire?)

- remove all `AsyncTypedSequence` and `WrappedAsyncSequence` dummy protocol when `FullTypedThrow` is implemented.

## WIP

[PriorityRunLoop](./Sources/Tetra/Concurrency/PriorityRunLoop.swift)

TaskPriority aware NSRunLoop based `Concurrency Serial Executor`.

Can be useful for old Apple Framework that does not support `libDispatch` and only support `CFRunLoop` like `CoreLocation`, `perform(_:on:with:waitUntilDone:)`

Reorder the jobs in Priority Order, and increase QoS level, according to the TaskPriority.


[AsyncFlatMap](./Sources/Tetra/Combine/Publishers+AsyncFlatMap.swift)

Combine operator which convert the Upstream to AsyncSequence and transfer the elements to the downstream.

uses actor based cooperative control to minimize contention.

Investingating for a way to backport rich asyncSequence features before Swift 6 platform.


[BackportDiscardingTaskGroup](./Sources/BackportDiscardingTaskGroup/ThrowingTaskGroup.swift)

Backport the behavior of `Discarding(Throwing)TaskGroup`. This now possbile thanks to Swift 6 generalized AsyncSequnce. Because we can wrap the `TaskGroup` into isolation.

Investingating for a way to merge it with existing `Discarding(Throwing)TaskGroup` using some kind of opaque types rather than erased types.

Investigating a way to merge NonFailure type and Failing type using typedThrow, which is currently not possible due to Swift 6 compiler bug.


[MemorySafe Data/String](./Sources/Tetra/Foundation/PruneMemory.swift)

`Data` and `String` which erase its memory footprints when deallocated. implemented by `CoreFoundation` API.

Still can not erase memory footprints caused by Swift Briding copy and os level memory paging.
146 changes: 146 additions & 0 deletions Sources/BackPortAsyncSequence/AsyncCompactMapSequence.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,146 @@
//
// AsyncCompactMapSequence.swift
//
//
// Created by 박병관 on 6/13/24.
//


extension BackPort {


public struct AsyncCompactMapSequence<Base: AsyncSequence, ElementOfResult> where Base.AsyncIterator: TypedAsyncIteratorProtocol {

@usableFromInline
let base: Base

@usableFromInline
let transform: (Base.Element) async throws(Failure) -> ElementOfResult?

@inlinable
package init(
_ base: Base,
transform: @escaping (Base.Element) async throws(Failure) -> ElementOfResult?
) {
self.base = base
self.transform = transform
}
}


}

extension BackPort.AsyncCompactMapSequence: AsyncSequence, TypedAsyncSequence {

/// The type of element produced by this asynchronous sequence.
///
/// The compact map sequence produces errors from either the base
/// sequence or the transforming closure.
public typealias Failure = AsyncIterator.Failure
/// The type of iterator that produces elements of the sequence.
public typealias AsyncIterator = Iterator

/// The iterator that produces elements of the compact map sequence.
public struct Iterator {


@usableFromInline
var baseIterator: Base.AsyncIterator

@usableFromInline
let transform: (Base.Element) async throws(Failure) -> ElementOfResult?

@usableFromInline
var finished = false

@usableFromInline
init(
_ baseIterator: Base.AsyncIterator,
transform: @escaping (Base.Element) async throws(Failure) -> ElementOfResult?
) {
self.baseIterator = baseIterator
self.transform = transform
}


}

@inlinable
public __consuming func makeAsyncIterator() -> Iterator {
return Iterator(base.makeAsyncIterator(), transform: transform)
}
}


extension BackPort.AsyncCompactMapSequence.Iterator: AsyncIteratorProtocol, TypedAsyncIteratorProtocol {

public typealias Element = ElementOfResult
public typealias Failure = Base.AsyncIterator.Err

/// Produces the next element in the compact map sequence.
///
/// This iterator calls `next()` on its base iterator; if this call
/// returns `nil`, `next()` returns `nil`. Otherwise, `next()`
/// calls the transforming closure on the received element, returning it if
/// the transform returns a non-`nil` value. If the transform returns `nil`,
/// this method continues to wait for further elements until it gets one
/// that transforms to a non-`nil` value. If calling the closure throws an
/// error, the sequence ends and `next()` rethrows the error.
@inlinable
public mutating func next(isolation actor: isolated (any Actor)? = #isolation) async throws(Failure) -> Element? {
while !finished {
guard let element = try await baseIterator.next(isolation: actor) else {
finished = true
return nil
}
let wrapper: (Base.Element) async -> Result<Suppress<Element?>, Failure> = { [transform] in
do throws(Base.AsyncIterator.Err) {
let value = try await Suppress(base: transform($0))
return .success(value)
} catch {
return .failure(error)
}
}
do throws(Failure) {
if let transformed = try await wrapper(Suppress(base: element).base).get().base {
return transformed
}
} catch {
finished = true
throw error
}
}
return nil
}

@_disfavoredOverload
@inlinable
public mutating func next() async throws(Failure) -> Element? {
try await next(isolation: nil)
}

}

extension BackPort.AsyncCompactMapSequence: @unchecked Sendable
where Base: Sendable,
Base.Element: Sendable { }



extension BackPort.AsyncCompactMapSequence.Iterator: @unchecked Sendable
where Base.AsyncIterator: Sendable,
Base.Element: Sendable { }


extension BackPort.AsyncCompactMapSequence {

@inlinable
package init <Source:AsyncSequence, Failure:Error>(
_ source: Source,
transform: @escaping (Base.Element) async throws(Failure) -> ElementOfResult?
) where Source.AsyncIterator: TypedAsyncIteratorProtocol, Source.AsyncIterator.Err == Never, Base == AsyncMapErrorSequence<Source, Failure> {
let base = AsyncMapErrorSequence(base: source, failure: Failure.self)
self.init(base, transform: transform)
}

}
Loading