Skip to content

Commit

Permalink
feat: add option to provide number of objects to wait for
Browse files Browse the repository at this point in the history
  • Loading branch information
soumyamahunt committed Aug 4, 2022
1 parent 43a209e commit 20b5725
Show file tree
Hide file tree
Showing 3 changed files with 72 additions and 17 deletions.
41 changes: 25 additions & 16 deletions Sources/AsyncObjects/AsyncObject.swift
Original file line number Diff line number Diff line change
Expand Up @@ -132,70 +132,79 @@ public func waitForAll(
}

/// Waits for multiple objects to green light task execution
/// by either of them.
/// by some(provided by count) of them.
///
/// Invokes ``AsyncObject/wait()`` for all objects
/// and returns when any of the invokation completes.
/// and returns when some(provided by count) of the invokation completes.
///
/// - Parameter objects: The objects to wait for.
/// - Parameters:
/// - objects: The objects to wait for.
/// - count: The number of objects to wait for.
@inlinable
public func waitForAny(_ objects: [any AsyncObject]) async {
public func waitForAny(_ objects: [any AsyncObject], count: Int = 1) async {
await withTaskGroup(of: Void.self) { group in
objects.forEach { group.addTask(operation: $0.wait) }
for await _ in group.prefix(1) { group.cancelAll() }
for _ in 0..<count { await group.next() }
group.cancelAll()
}
}

/// Waits for multiple objects to green light task execution
/// by either of them.
/// by some(provided by count) of them.
///
/// Invokes ``AsyncObject/wait()`` for all objects
/// and returns when any of the invokation completes.
/// and returns when some(provided by count) of the invokation completes.
///
/// - Parameter objects: The objects to wait for.
/// - Parameters:
/// - objects: The objects to wait for.
/// - count: The number of objects to wait for.
@inlinable
public func waitForAny(_ objects: any AsyncObject...) async {
await waitForAny(objects)
public func waitForAny(_ objects: any AsyncObject..., count: Int = 1) async {
await waitForAny(objects, count: count)
}

/// Waits for multiple objects to green light task execution
/// by either of them within provided duration.
/// by some(provided by count) of them within provided duration.
///
/// Invokes ``AsyncObject/wait()`` for all objects
/// and returns when any of the invokation completes
/// and returns when some(provided by count) of the invokation completes
/// or the timeout expires.
///
/// - Parameters:
/// - objects: The objects to wait for.
/// - count: The number of objects to wait for.
/// - duration: The duration in nano seconds to wait until.
/// - Returns: The result indicating whether wait completed or timed out.
@inlinable
public func waitForAny(
_ objects: [any AsyncObject],
count: Int = 1,
forNanoseconds duration: UInt64
) async -> TaskTimeoutResult {
return await waitForTaskCompletion(withTimeoutInNanoseconds: duration) {
await waitForAny(objects)
await waitForAny(objects, count: count)
}
}

/// Waits for multiple objects to green light task execution
/// by either of them within provided duration.
/// by some(provided by count) of them within provided duration.
///
/// Invokes ``AsyncObject/wait()`` for all objects
/// and returns when any of the invokation completes
/// and returns when some(provided by count) of the invokation completes
/// or the timeout expires.
///
/// - Parameters:
/// - objects: The objects to wait for.
/// - count: The number of objects to wait for.
/// - duration: The duration in nano seconds to wait until.
/// - Returns: The result indicating whether wait completed or timed out.
@inlinable
public func waitForAny(
_ objects: any AsyncObject...,
count: Int = 1,
forNanoseconds duration: UInt64
) async -> TaskTimeoutResult {
return await waitForAny(objects, forNanoseconds: duration)
return await waitForAny(objects, count: count, forNanoseconds: duration)
}

/// Waits for the provided task to be completed within the timeout duration.
Expand Down
2 changes: 1 addition & 1 deletion Sources/AsyncObjects/Future.swift
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import Foundation
/// Use a future to perform some work and then asynchronously publish a single element.
/// You can initialize the future with a closure that takes a ``Future/Promise``;
/// the closure calls the promise with a `Result` that indicates either success or failure.
///
///
/// Otherwise, you can create future and fulfill it with a `Result` that indicates either success or failure
/// by using ``fulfill(with:)`` method. In the success case,
/// the future’s downstream subscriber receives the element prior to the publishing stream finishing normally.
Expand Down
46 changes: 46 additions & 0 deletions Tests/AsyncObjectsTests/AsyncObjectTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,26 @@ class AsyncObjectTests: XCTestCase {
}
}

func testMultipleObjectWaitMultiple() async throws {
let event = AsyncEvent(signaledInitially: false)
let mutex = AsyncSemaphore()
let op = TaskOperation(queue: .global(qos: .background)) {
try await Task.sleep(nanoseconds: UInt64(5E9))
}
Task.detached {
try await Task.sleep(nanoseconds: UInt64(2E9))
await event.signal()
}
Task.detached {
try await Task.sleep(nanoseconds: UInt64(3E9))
await mutex.signal()
}
op.signal()
await checkExecInterval(durationInSeconds: 3) {
await waitForAny(event, mutex, op, count: 2)
}
}

func testMultipleObjectWaitAllWithTimeout() async throws {
let event = AsyncEvent(signaledInitially: false)
let mutex = AsyncSemaphore()
Expand All @@ -56,6 +76,32 @@ class AsyncObjectTests: XCTestCase {
XCTAssertEqual(result, .timedOut)
}

func testMultipleObjectWaitMultipleWithTimeout() async throws {
var result: TaskTimeoutResult = .success
let event = AsyncEvent(signaledInitially: false)
let mutex = AsyncSemaphore()
let op = TaskOperation(queue: .global(qos: .background)) {
try await Task.sleep(nanoseconds: UInt64(7E9))
}
Task.detached {
try await Task.sleep(nanoseconds: UInt64(3E9))
await event.signal()
}
Task.detached {
try await Task.sleep(nanoseconds: UInt64(5E9))
await mutex.signal()
}
op.signal()
await checkExecInterval(durationInSeconds: 4) {
result = await waitForAny(
event, mutex, op,
count: 2,
forNanoseconds: UInt64(4E9)
)
}
XCTAssertEqual(result, .timedOut)
}

func testMultipleObjectWaitAllWithoutTimeout() async throws {
let event = AsyncEvent(signaledInitially: false)
let mutex = AsyncSemaphore()
Expand Down

0 comments on commit 20b5725

Please sign in to comment.