Skip to content

Remove AsyncIterator: Sendable requirement from debounce #197

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

Closed
wants to merge 1 commit into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
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
121 changes: 0 additions & 121 deletions Sources/AsyncAlgorithms/AsyncDebounceSequence.swift

This file was deleted.

115 changes: 115 additions & 0 deletions Sources/AsyncAlgorithms/Debounce/AsyncDebounceSequence.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,115 @@
//===----------------------------------------------------------------------===//
//
// This source file is part of the Swift Async Algorithms open source project
//
// Copyright (c) 2022 Apple Inc. and the Swift project authors
// Licensed under Apache License v2.0 with Runtime Library Exception
//
// See https://swift.org/LICENSE.txt for license information
//
//===----------------------------------------------------------------------===//

extension AsyncSequence {
/// Creates an asynchronous sequence that emits the latest element after a given quiescence period
/// has elapsed by using a specified Clock.
@available(macOS 13.0, iOS 16.0, watchOS 9.0, tvOS 16.0, *)
public func debounce<C: Clock>(for interval: C.Instant.Duration, tolerance: C.Instant.Duration? = nil, clock: C) -> AsyncDebounceSequence<Self, C> where Self: Sendable {
AsyncDebounceSequence(self, interval: interval, tolerance: tolerance, clock: clock)
}

/// Creates an asynchronous sequence that emits the latest element after a given quiescence period
/// has elapsed.
@available(macOS 13.0, iOS 16.0, watchOS 9.0, tvOS 16.0, *)
public func debounce(for interval: Duration, tolerance: Duration? = nil) -> AsyncDebounceSequence<Self, ContinuousClock> where Self: Sendable {
self.debounce(for: interval, tolerance: tolerance, clock: .continuous)
}
}

/// An `AsyncSequence` that emits the latest element after a given quiescence period
/// has elapsed.
@available(macOS 13.0, iOS 16.0, watchOS 9.0, tvOS 16.0, *)
public struct AsyncDebounceSequence<Base: AsyncSequence, C: Clock>: Sendable where Base: Sendable {
/// This class is needed to hook the deinit to observe once all references to the ``AsyncDebounceSequence`` are dropped.
///
/// If we get move-only types we should be able to drop this class and use the `deinit` of the ``AsyncDebounceSequence`` struct itself.
final class InternalClass: Sendable {
fileprivate let storage: DebounceStorage<Base, C>

fileprivate init(storage: DebounceStorage<Base, C>) {
self.storage = storage
}

deinit {
storage.sequenceDeinitialized()
}
}

/// The internal class to hook the `deinit`.
let internalClass: InternalClass

/// The underlying storage
fileprivate var storage: DebounceStorage<Base, C> {
self.internalClass.storage
}

/// Initializes a new ``AsyncDebounceSequence``.
///
/// - Parameters:
/// - base: The base sequence.
/// - interval: The interval to debounce.
/// - tolerance: The tolerance of the clock.
/// - clock: The clock.
public init(_ base: Base, interval: C.Instant.Duration, tolerance: C.Instant.Duration?, clock: C) {
let storage = DebounceStorage<Base, C>(
base: base,
interval: interval,
tolerance: tolerance,
clock: clock
)
self.internalClass = .init(storage: storage)
}
}

@available(macOS 13.0, iOS 16.0, watchOS 9.0, tvOS 16.0, *)
extension AsyncDebounceSequence: AsyncSequence {
public typealias Element = Base.Element

public func makeAsyncIterator() -> AsyncIterator {
AsyncIterator(storage: self.internalClass.storage)
}
}

@available(macOS 13.0, iOS 16.0, watchOS 9.0, tvOS 16.0, *)
extension AsyncDebounceSequence {
public struct AsyncIterator: AsyncIteratorProtocol {
/// This class is needed to hook the deinit to observe once all references to the ``AsyncIterator`` are dropped.
///
/// If we get move-only types we should be able to drop this class and use the `deinit` of the ``AsyncIterator`` struct itself.
final class InternalClass: Sendable {
private let storage: DebounceStorage<Base, C>

fileprivate init(storage: DebounceStorage<Base, C>) {
self.storage = storage
self.storage.iteratorInitialized()
}

deinit {
self.storage.iteratorDeinitialized()
}

func next() async rethrows -> Element? {
try await self.storage.next()
}
}

let internalClass: InternalClass

fileprivate init(storage: DebounceStorage<Base, C>) {
self.internalClass = InternalClass(storage: storage)
}

public mutating func next() async rethrows -> Element? {
try await self.internalClass.next()
}
}
}
Loading