### Initialize AsyncQueue Source: https://github.com/groue/asyncqueues/blob/main/_autodocs/api-reference/AsyncQueue.md Creates a new AsyncQueue instance with default settings. No specific setup is required before instantiation. ```swift let queue = AsyncQueue() ``` -------------------------------- ### Example Usage of AsyncQueue Source: https://github.com/groue/asyncqueues/blob/main/Sources/AsyncQueues/Documentation.docc/Documentation.md Demonstrates the sequential execution of tasks added to an AsyncQueue. Tasks are printed in the order they are enqueued. ```swift // Prints 1, 2, 3, in this order. let queue = AsyncQueue() queue.addTask { print("1") } queue.addTask { print("2") } await queue.perform { print("3") } ``` -------------------------------- ### Import AsyncQueues for AsyncQueue Source: https://github.com/groue/asyncqueues/blob/main/_autodocs/REFERENCE.md Import the AsyncQueues library to start using the AsyncQueue. This is the basic queue type for serializing operations. ```swift import AsyncQueues let queue = AsyncQueue() ``` -------------------------------- ### Initialize DiscardingAsyncQueue Source: https://github.com/groue/asyncqueues/blob/main/_autodocs/api-reference/DiscardingAsyncQueue.md Creates a new DiscardingAsyncQueue instance with default settings. No specific setup is required beyond instantiation. ```swift public init() ``` ```swift let queue = DiscardingAsyncQueue() ``` -------------------------------- ### Serialization with AsyncStream Source: https://github.com/groue/asyncqueues/blob/main/README.md Demonstrates how to serialize asynchronous operations using AsyncStream. This setup is useful for managing shared mutable resources by ensuring operations complete before new ones begin. ```swift // A program that prints 1, 2, 3, in this order. // Setup typealias Operation = @Sendable () async -> Void let (stream, continuation) = AsyncStream.makeStream(of: Operation.self) Task { for await operation in stream { await operation() } } // Serialize operations continuation.yield { print("1") } continuation.yield { print("2") } continuation.yield { print("3") } // Cleanup continuation.finish() ``` -------------------------------- ### AsyncQueue Migration Example Source: https://github.com/groue/asyncqueues/blob/main/_autodocs/QUEUE_COMPARISON.md Migrating from AsyncQueue to DiscardingAsyncQueue. The DiscardingAsyncQueue automatically handles cancellation checks, simplifying the task closure. ```swift // Before (AsyncQueue) let queue = AsyncQueue() let task = queue.addTask { if Task.isCancelled { return } try await work() } // After (DiscardingAsyncQueue) let queue = DiscardingAsyncQueue() let task = queue.addTask { try await work() // No need to check, will throw if cancelled early } ``` -------------------------------- ### AsyncQueue for Data Persistence Source: https://github.com/groue/asyncqueues/blob/main/_autodocs/QUEUE_COMPARISON.md Example of using AsyncQueue for a data persistence layer like UserDefaults, ensuring operations complete successfully. ```swift class UserDefaults { private let queue = AsyncQueue() // ✅ AsyncQueue func save(_ data: Data) async { await queue.perform { UserDefaults.standard.set(data, forKey: "key") } } func load() async -> Data? { await queue.perform { UserDefaults.standard.data(forKey: "key") } } } ``` -------------------------------- ### Thread Safety Example with AsyncQueue Source: https://github.com/groue/asyncqueues/blob/main/_autodocs/ARCHITECTURE.md Demonstrates thread-safe execution of operations using a shared AsyncQueue instance across multiple concurrent tasks. AsyncQueue is Sendable, ensuring operations are serialized correctly. ```swift let queue = AsyncQueue() // Shared across tasks Task { try await queue.perform { print("A") } } Task { try await queue.perform { print("B") } } ``` -------------------------------- ### DiscardingAsyncQueue for Network Search Source: https://github.com/groue/asyncqueues/blob/main/_autodocs/QUEUE_COMPARISON.md Example of using DiscardingAsyncQueue for a network search service, where newer searches can cancel older ones, preventing wasted API calls. ```swift class SearchService { private let queue = DiscardingAsyncQueue() // ✅ DiscardingAsyncQueue func search(_ query: String) async throws -> [Result] { do { return try await queue.perform { try await api.search(query) } } catch is CancellationError { return [] // New search cancelled this one } } } ``` -------------------------------- ### Handling Discarded Operations in DiscardingAsyncQueue Source: https://github.com/groue/asyncqueues/blob/main/_autodocs/REFERENCE.md Demonstrates handling `CancellationError` when an operation is eagerly discarded by `DiscardingAsyncQueue` before it starts execution. ```swift let queue = DiscardingAsyncQueue() do { try await queue.perform { try await someAsyncWork() } } catch is CancellationError { print("Operation was cancelled before it started") } ``` -------------------------------- ### init() Source: https://github.com/groue/asyncqueues/blob/main/_autodocs/api-reference/AsyncQueue.md Creates a new async queue with default settings. ```APIDOC ## init() ### Description Creates a new async queue with default settings. ### Returns A new `AsyncQueue` instance. ### Example ```swift let queue = AsyncQueue() ``` ``` -------------------------------- ### init() Source: https://github.com/groue/asyncqueues/blob/main/_autodocs/api-reference/CoalescingAsyncQueue.md Creates a new coalescing async queue with default settings. ```APIDOC ## init() ### Description Creates a new coalescing async queue with default settings. ### Returns A new `CoalescingAsyncQueue` instance. ### Example ```swift let queue = CoalescingAsyncQueue() ``` ``` -------------------------------- ### Recommended Queue Management Strategy Source: https://github.com/groue/asyncqueues/blob/main/_autodocs/USAGE_GUIDE.md Illustrates the best practice of creating separate queues for distinct serialization boundaries to avoid unnecessary serialization and overhead. ```swift class Manager { // Good: one queue per serialization concern private let networkQueue = AsyncQueue() private let databaseQueue = AsyncQueue() private let cacheQueue = AsyncQueue() // Avoid: too many queues (overhead) // Avoid: one queue for everything (serializes unnecessarily) } ``` -------------------------------- ### Add Task to AsyncQueue Source: https://github.com/groue/asyncqueues/blob/main/Sources/AsyncQueues/Documentation.docc/Documentation.md Adds a new top-level task to the queue. You can await the returned task to get its value. ```swift let task = queue.addTask { try await doSomething() } let result = try await task.value ``` -------------------------------- ### Initialize AsyncQueue Types Source: https://github.com/groue/asyncqueues/blob/main/_autodocs/INDEX.md Demonstrates the initialization of the three main queue types: AsyncQueue, DiscardingAsyncQueue, and CoalescingAsyncQueue. These queues manage asynchronous operations with different policies for serialization and cancellation. ```swift import AsyncQueues // Simple serialization, no cancellation let queue1 = AsyncQueue() // Eager cancellation let queue2 = DiscardingAsyncQueue() // Eager cancellation + operation coalescing let queue3 = CoalescingAsyncQueue() ``` -------------------------------- ### DiscardingAsyncQueue Cancellation Behavior Source: https://github.com/groue/asyncqueues/blob/main/_autodocs/QUEUE_COMPARISON.md Shows how DiscardingAsyncQueue skips operations if cancelled before they start, throwing CancellationError. If already running, it continues but can be checked for cancellation. ```swift let queue = DiscardingAsyncQueue() let task = queue.addTask { print("Starting") } task.cancel() do { try await task.value print("Completed") // NOT printed } catch is CancellationError { print("Cancelled") // Printed } ``` -------------------------------- ### Basic Serial Execution with AsyncQueue Source: https://github.com/groue/asyncqueues/blob/main/_autodocs/REFERENCE.md Demonstrates basic serial execution using AsyncQueue's `perform` and `addTask` methods. `perform` waits for the result, while `addTask` returns an unstructured task. ```swift import AsyncQueues let queue = AsyncQueue() // Using perform() - wait for result in the current task let result = try await queue.perform { try await fetchData() } // Using addTask() - get an unstructured task let task = queue.addTask { try await processData() } // Wait for task completion let value = try await task.value ``` -------------------------------- ### Core Methods Source: https://github.com/groue/asyncqueues/blob/main/_autodocs/INDEX.md Common methods available across all queue types for executing work and managing tasks. ```APIDOC ## Core Methods ### Description Common methods available across all queue types for executing work and managing tasks. ### Usage All three queue types support: ```swift // Execute and get result let result = try await queue.perform { try await someWork() } // Create unstructured task let task = queue.addTask { try await someWork() } // Verify execution context queue.preconditionSerialized("Must run in queue") ``` ``` -------------------------------- ### Import AsyncQueues for DiscardingAsyncQueue Source: https://github.com/groue/asyncqueues/blob/main/_autodocs/REFERENCE.md Import the AsyncQueues library to initialize a DiscardingAsyncQueue. This queue eagerly discards cancelled operations. ```swift import AsyncQueues let queue = DiscardingAsyncQueue() ``` -------------------------------- ### Migrating from AsyncStream to AsyncQueue Source: https://github.com/groue/asyncqueues/blob/main/_autodocs/USAGE_GUIDE.md Demonstrates the conversion of an AsyncStream-based operation handling to an AsyncQueue-based approach for improved type safety and error handling. ```swift let queue = AsyncQueue() queue.addTask { print("1") } queue.addTask { print("2") } ``` -------------------------------- ### Queue Types Source: https://github.com/groue/asyncqueues/blob/main/_autodocs/INDEX.md Instantiate different types of queues for various concurrency and cancellation needs. ```APIDOC ## Queue Types ### Description Instantiate different types of queues for various concurrency and cancellation needs. ### Usage ```swift import AsyncQueues // Simple serialization, no cancellation let queue1 = AsyncQueue() // Eager cancellation let queue2 = DiscardingAsyncQueue() // Eager cancellation + operation coalescing let queue3 = CoalescingAsyncQueue() ``` ``` -------------------------------- ### AsyncQueue perform() Signatures Source: https://github.com/groue/asyncqueues/blob/main/_autodocs/QUEUE_COMPARISON.md Compares the 'perform' method signatures across AsyncQueue, DiscardingAsyncQueue, and CoalescingAsyncQueue, including options for policy control. ```swift // AsyncQueue public func perform( operation: sending () async throws -> sending Success ) async rethrows -> sending Success // DiscardingAsyncQueue public func perform( operation: sending () async throws -> sending Success ) async throws -> sending Success // Can throw CancellationError // CoalescingAsyncQueue public func perform( operation: sending () async throws -> sending Success ) async throws -> sending Success // Can throw CancellationError public func perform( policy: Policy, operation: sending @escaping () async throws -> sending Success ) async throws -> sending Success // With policy control ``` -------------------------------- ### Module Structure of AsyncQueues Source: https://github.com/groue/asyncqueues/blob/main/_autodocs/REFERENCE.md Illustrates the hierarchical structure of the AsyncQueues module, highlighting the main types and their internal components. ```text AsyncQueues ├── AsyncQueue (main type) ├── DiscardingAsyncQueue (main type) ├── CoalescingAsyncQueue (main type) │ └── Policy (enum) └── Internal components (not exported) ├── PrimitiveAsyncQueue ├── Semaphore ├── Mutex └── TaskCancellationState ``` -------------------------------- ### Verify Execution Context in AsyncQueue Source: https://github.com/groue/asyncqueues/blob/main/_autodocs/INDEX.md Demonstrates how to assert that a piece of code is running within the context of a specific queue. This is useful for ensuring correct serialization and preventing race conditions. ```swift // Verify execution context queue1.preconditionSerialized("Must run in queue") ``` -------------------------------- ### perform(_:) Source: https://github.com/groue/asyncqueues/blob/main/_autodocs/api-reference/DiscardingAsyncQueue.md Executes an asynchronous operation, ensuring it runs after previously enqueued operations. Throws CancellationError if the task is cancelled before previous operations complete. ```APIDOC ## perform(_:) ### Description Returns the result of the given operation. The `operation` closure runs after previously enqueued operations are completed. If the current task is cancelled before the previously enqueued operations complete, `operation` is not executed and this method throws a `CancellationError`. The operation is considered "discarded". Otherwise, `operation` can check `Task.isCancelled` or `Task.checkCancellation()` to detect cancellation. ### Parameters #### Path Parameters - operation (sending () async throws -> sending Success) - Required - An async closure that returns a value of type `Success` or throws an error ### Returns `Success` — The result of executing `operation`. ### Throws - Any error thrown by `operation` - `CancellationError` if the current task is cancelled before previously enqueued operations complete ### Example ```swift let queue = DiscardingAsyncQueue() do { let value = try await queue.perform { try await someAsyncValue() } } catch is CancellationError { print("Operation was cancelled before it could start") } ``` ``` -------------------------------- ### DiscardingAsyncQueue Constructors Source: https://github.com/groue/asyncqueues/blob/main/_autodocs/api-reference/DiscardingAsyncQueue.md Initializes a new DiscardingAsyncQueue with default settings. ```APIDOC ## init() ### Description Creates a new discarding async queue with default settings. ### Returns A new `DiscardingAsyncQueue` instance. ### Example ```swift let queue = DiscardingAsyncQueue() ``` ``` -------------------------------- ### AsyncQueue Basic Error Handling Source: https://github.com/groue/asyncqueues/blob/main/_autodocs/USAGE_GUIDE.md Demonstrates how errors thrown from within an AsyncQueue's perform block are rethrown and can be caught. ```swift let queue = AsyncQueue() // Errors are simply rethrown do { try await queue.perform { try await somethingThatMayFail() } } catch { print("Operation failed: \(error)") } ``` -------------------------------- ### Import AsyncQueues for CoalescingAsyncQueue Source: https://github.com/groue/asyncqueues/blob/main/_autodocs/REFERENCE.md Import the AsyncQueues library to create a CoalescingAsyncQueue. This queue optimizes by cancelling earlier discardable operations if newer ones are enqueued. ```swift import AsyncQueues let queue = CoalescingAsyncQueue() ``` -------------------------------- ### Initialize CoalescingAsyncQueue Source: https://github.com/groue/asyncqueues/blob/main/_autodocs/api-reference/CoalescingAsyncQueue.md Creates a new coalescing async queue with default settings. Use this when you need a basic queue for serializing async operations. ```swift public init() ``` ```swift let queue = CoalescingAsyncQueue() ``` -------------------------------- ### Nesting Queues for Serial Execution Source: https://github.com/groue/asyncqueues/blob/main/_autodocs/REFERENCE.md Illustrates nesting queues safely, where each queue maintains its own serialization context. Operations within nested queues run serially across all involved queues. ```swift let queue1 = AsyncQueue() let queue2 = AsyncQueue() await queue1.perform { try await queue2.perform { // Runs serially in both queues } } ``` -------------------------------- ### Execute Work with AsyncQueue Source: https://github.com/groue/asyncqueues/blob/main/_autodocs/INDEX.md Shows how to execute asynchronous work within a queue and retrieve its result. This method ensures that operations are serialized according to the queue's policy. ```swift // Execute and get result let result = try await queue1.perform { try await someWork() } ``` -------------------------------- ### Simple Data Fetching with AsyncQueue Source: https://github.com/groue/asyncqueues/blob/main/_autodocs/USAGE_GUIDE.md Use AsyncQueue to serialize network requests and avoid race conditions. This is suitable for operations where cancellation is not a requirement. ```swift import AsyncQueues class DataManager { private let queue = AsyncQueue() // Serialize access to avoid concurrent requests func fetchData(id: String) async throws -> Data { try await queue.perform { let url = URL(string: "https://api.example.com/data/\(id)")! let (data, _) = try await URLSession.shared.data(from: url) return data } } } // Usage let manager = DataManager() let data = try await manager.fetchData(id: "123") ``` -------------------------------- ### Access Queue Identifiers Source: https://github.com/groue/asyncqueues/blob/main/_autodocs/INDEX.md Demonstrates how to retrieve the unique identifiers for different queue types. These IDs can be used for debugging or logging purposes. ```swift // Queue identifiers let id1: AsyncQueue.ID = queue1.id let id2: DiscardingAsyncQueue.ID = queue2.id let id3: CoalescingAsyncQueue.ID = queue3.id ``` -------------------------------- ### Types Source: https://github.com/groue/asyncqueues/blob/main/_autodocs/INDEX.md Definitions for policies and queue identifiers used within the AsyncQueues library. ```APIDOC ## Types ### Description Definitions for policies and queue identifiers used within the AsyncQueues library. ### Usage ```swift // Coalescing policy CoalescingAsyncQueue.Policy.required CoalescingAsyncQueue.Policy.discardable // Queue identifiers let id1: AsyncQueue.ID = queue1.id let id2: DiscardingAsyncQueue.ID = queue2.id let id3: CoalescingAsyncQueue.ID = queue3.id ``` ``` -------------------------------- ### Verifying Serialization Context Source: https://github.com/groue/asyncqueues/blob/main/_autodocs/REFERENCE.md Demonstrates using `preconditionSerialized` to assert that a specific piece of code is executed within a queue's serialization context. Failure to meet this condition will halt execution. ```swift let queue = AsyncQueue() await queue.perform { queue.preconditionSerialized("This code must run in the queue") // If this assertion fails, execution stops } ``` -------------------------------- ### PrimitiveAsyncQueue Initialization Source: https://github.com/groue/asyncqueues/blob/main/_autodocs/ARCHITECTURE.md Initializes the PrimitiveAsyncQueue by creating an AsyncStream and spawning a background task to process operations sequentially. ```swift final class PrimitiveAsyncQueue { private let queueContinuation: AsyncStream.Continuation init() { let (queueStream, queueContinuation) = AsyncStream.makeStream(of: Operation.self) self.queueContinuation = queueContinuation // Background task that processes operations sequentially Task { for await operation in queueStream { await operation() } } } } ``` -------------------------------- ### AsyncQueue Public API Source: https://github.com/groue/asyncqueues/blob/main/_autodocs/README.md Documentation for the AsyncQueue type, a basic serialization queue that supports both throwing and non-throwing operations with deferred cancellation handling. ```APIDOC ## AsyncQueue ### Description Represents a basic serialization queue for simple operations without immediate cancellation. ### Methods #### `init()` - **Description**: Constructor for the AsyncQueue. #### `perform(operation:)` - **Description**: Executes a given operation and returns its result. - **Parameters**: - `operation` (Operation): The operation to perform. #### `perform(policy:operation:)` - **Description**: Executes a given operation with a specified policy. This method is specific to CoalescingAsyncQueue and not directly applicable to AsyncQueue in its basic form. #### `addTask(operation:)` - **Description**: Creates an unstructured task to perform the operation. - **Parameters**: - `operation` (Operation): The operation to add as a task. #### `addTask(policy:operation:)` - **Description**: Creates a task with a specified policy. This method is specific to CoalescingAsyncQueue and not directly applicable to AsyncQueue in its basic form. #### `preconditionSerialized(_:file:line:)` - **Description**: Asserts that the current context is within the serialization context of the queue. ### Types - `AsyncQueue.ID`: Identity type for tracking AsyncQueue instances. ### Conformances - `Sendable`: Indicates that the queue is thread-safe and can be passed between tasks. - `Identifiable`: Allows tracking of queue instances via an `.id` property. ``` -------------------------------- ### perform(_:) Source: https://github.com/groue/asyncqueues/blob/main/_autodocs/api-reference/AsyncQueue.md Executes an asynchronous operation after all previously enqueued operations have completed. This method ensures sequential execution and returns the result of the operation. ```APIDOC ## perform(_:) ### Description Returns the result of the given operation. The `operation` closure runs after previously enqueued operations are completed. If the current task is cancelled, this method still runs the `operation` closure. Check `Task.isCancelled` or `Task.checkCancellation()` to detect cancellation. ### Method `async rethrows` ### Parameters #### Path Parameters - **operation** (`sending () async throws -> sending Success`) - Required - An async closure that returns a value of type `Success` or throws an error ### Returns `Success` — The result of executing `operation`. ### Throws Any error thrown by `operation`. ### Example ```swift let queue = AsyncQueue() let value = try await queue.perform { try await someAsyncValue() } ``` ``` -------------------------------- ### AsyncQueue addTask() Signatures Source: https://github.com/groue/asyncqueues/blob/main/_autodocs/QUEUE_COMPARISON.md Compares the 'addTask' method signatures for AsyncQueue, DiscardingAsyncQueue, and CoalescingAsyncQueue, showing variations for throwing and non-throwing operations. ```swift // AsyncQueue (non-throwing) public func addTask( operation: sending @escaping () async -> Success ) -> Task // AsyncQueue (throwing) public func addTask( operation: sending @escaping () async throws -> Success ) -> Task // DiscardingAsyncQueue public func addTask( operation: sending @escaping () async throws -> Success ) -> Task // CoalescingAsyncQueue public func addTask( policy: Policy = .required, operation: sending @escaping () async throws -> Success ) -> Task ``` -------------------------------- ### AsyncQueue Error Handling Source: https://github.com/groue/asyncqueues/blob/main/_autodocs/QUEUE_COMPARISON.md Demonstrates error handling for AsyncQueue, where errors originate only from the performed operation. ```swift let queue = AsyncQueue() do { let result = try await queue.perform { try await fetchData() } print("Success: \(result)") } catch { print("Error: \(error)") } ``` -------------------------------- ### Execute Work with CoalescingAsyncQueue Policy Source: https://github.com/groue/asyncqueues/blob/main/_autodocs/INDEX.md Shows how to execute asynchronous work with a specified policy on a CoalescingAsyncQueue. This allows control over whether new operations should discard or be required if an older one is pending. ```swift // Execute with policy let result = try await queue3.perform(policy: .discardable) { try await someWork() } ``` -------------------------------- ### Database Transactions with AsyncQueue Source: https://github.com/groue/asyncqueues/blob/main/_autodocs/QUEUE_COMPARISON.md Ensures database transactions complete successfully without cancellation, as any interruption would corrupt state. Failure is treated as fatal. ```swift class Database { private let queue = AsyncQueue() // ✅ AsyncQueue func transaction(_ block: () async throws -> T) async throws -> T { try await queue.perform { try await beginTransaction() defer { try await rollback() } let result = try await block() try await commit() return result } } ``` -------------------------------- ### preconditionSerialized(_:file:line:) Source: https://github.com/groue/asyncqueues/blob/main/_autodocs/api-reference/DiscardingAsyncQueue.md Ensures that the current task is executing an operation within the queue's serialized context. Stops execution if not. ```APIDOC ## preconditionSerialized(_:file:line:) ### Description Stops program execution if the current task is not executing an operation of this queue. Use this method to ensure code runs within the queue's serialized context. ### Parameters #### Path Parameters - message (@autoclosure () -> String) - Optional - The message to print if the assertion fails - file (StaticString) - Optional - The file name to print if the assertion fails - line (UInt) - Optional - The line number to print if the assertion fails ### Example ```swift let queue = DiscardingAsyncQueue() try await queue.perform { queue.preconditionSerialized("This code must run in the queue") } ``` ``` -------------------------------- ### Precondition Serialized Execution Source: https://github.com/groue/asyncqueues/blob/main/_autodocs/api-reference/CoalescingAsyncQueue.md Ensures that the enclosed code runs within the queue's serialized context. Use this to enforce execution order and prevent concurrent access to shared resources. ```swift let queue = CoalescingAsyncQueue() try await queue.perform { queue.preconditionSerialized("This code must run in the queue") } ``` -------------------------------- ### User Input Handler with DiscardingAsyncQueue Source: https://github.com/groue/asyncqueues/blob/main/_autodocs/USAGE_GUIDE.md Employ DiscardingAsyncQueue to handle user input, allowing for the early cancellation of previous operations when new input arrives. This is useful when older operations become redundant. ```swift import AsyncQueues class SearchViewController { private let queue = DiscardingAsyncQueue() func handleSearchInput(_ query: String) { Task { do { let results = try await queue.perform { try await searchAPI(query: query) } updateUI(with: results) } catch is CancellationError { // Search was cancelled (new search started before this one completed) print("Search cancelled") } catch { showError(error) } } } private func searchAPI(query: String) async throws -> [SearchResult] { // Simulate API call try await Task.sleep(nanoseconds: 1_000_000_000) return [] } } // Usage pattern: Each new search cancels the previous one // searchViewController.handleSearchInput("apple") // starts search // searchViewController.handleSearchInput("app") // cancels first, starts new ``` -------------------------------- ### Testing Serialization Order with AsyncQueue Source: https://github.com/groue/asyncqueues/blob/main/_autodocs/USAGE_GUIDE.md Verifies that operations added to an AsyncQueue are executed in the order they are added, including operations performed via `perform`. ```swift func testSerializationOrder() async throws { let queue = AsyncQueue() var executionOrder: [Int] = [] queue.addTask { executionOrder.append(1) } queue.addTask { executionOrder.append(2) } try await queue.perform { executionOrder.append(3) } // Wait a bit for tasks to execute try await Task.sleep(nanoseconds: 100_000_000) XCTAssertEqual(executionOrder, [1, 2, 3]) } ``` -------------------------------- ### Resource Lock Serialization with AsyncQueue Source: https://github.com/groue/asyncqueues/blob/main/_autodocs/USAGE_GUIDE.md Serialize access to a shared resource using `AsyncQueue` to ensure that read and write operations are performed sequentially, preventing race conditions and maintaining data consistency. ```swift class SharedResource { private let accessQueue = AsyncQueue() private var internalState = "" func read() async -> String { await accessQueue.perform { internalState // Serialized read } } func write(_ value: String) async throws { try await accessQueue.perform { internalState = value try await persistState() } } } ``` -------------------------------- ### Add Unstructured Task to AsyncQueue Source: https://github.com/groue/asyncqueues/blob/main/_autodocs/INDEX.md Illustrates how to add an unstructured task to a queue. This task can be awaited later for its result. The task will be executed according to the queue's serialization policy. ```swift // Create unstructured task let task = queue1.addTask { try await someWork() } ``` -------------------------------- ### Access Coalescing Policy Types Source: https://github.com/groue/asyncqueues/blob/main/_autodocs/INDEX.md Shows how to access the different coalescing policies available for CoalescingAsyncQueue, specifically `.required` and `.discardable`. ```swift // Coalescing policy CoalescingAsyncQueue.Policy.required CoalescingAsyncQueue.Policy.discardable ``` -------------------------------- ### perform(operation:) Source: https://github.com/groue/asyncqueues/blob/main/_autodocs/api-reference/CoalescingAsyncQueue.md Returns the result of the given operation. The operation runs after previously enqueued operations are completed. If the current task is cancelled before the previously enqueued operations complete, the operation is not executed and this method throws a `CancellationError`. ```APIDOC ## perform(operation:) ### Description Returns the result of the given operation. The `operation` closure runs after previously enqueued operations are completed. If the current task is cancelled before the previously enqueued operations complete, `operation` is not executed and this method throws a `CancellationError`. The operation is considered "discarded". Otherwise, `operation` can check `Task.isCancelled` or `Task.checkCancellation()` to detect cancellation. Any previously enqueued discardable operation is cancelled when this method is called. ### Parameters #### Path Parameters - operation (sending () async throws -> sending Success) - Required - An async closure that returns a value of type `Success` or throws an error ### Returns `Success` — The result of executing `operation`. ### Throws - Any error thrown by `operation` - `CancellationError` if the current task is cancelled before previously enqueued operations complete ### Example ```swift let queue = CoalescingAsyncQueue() let value = try await queue.perform { try await someAsyncValue() } ``` ``` -------------------------------- ### preconditionSerialized Source: https://github.com/groue/asyncqueues/blob/main/_autodocs/api-reference/CoalescingAsyncQueue.md Ensures that the current code is executing within the serialized context of the queue. Stops program execution if the condition is not met. ```APIDOC ## preconditionSerialized(_:file:line:) ### Description Stops program execution if the current task is not executing an operation within this queue's serialized context. This is useful for asserting that certain code paths are only executed when managed by the queue. ### Method Signature ```swift public func preconditionSerialized(_ message: @autoclosure () -> String = String(), file: StaticString = #fileID, line: UInt = #line) ``` ### Parameters #### Path Parameters * **message** (`@autoclosure () -> String`) - Optional - Defaults to an empty string. The message to display if the precondition fails. * **file** (`StaticString`) - Optional - Defaults to the current file identifier. The file name to include in the failure message. * **line** (`UInt`) - Optional - Defaults to the current line number. The line number to include in the failure message. ### Example ```swift let queue = CoalescingAsyncQueue() try await queue.perform { queue.preconditionSerialized("This code must run in the queue") } ``` ``` -------------------------------- ### Maintaining a Strong Queue Reference Source: https://github.com/groue/asyncqueues/blob/main/_autodocs/USAGE_GUIDE.md Ensures queue operations execute by keeping a strong reference to the queue object, preventing premature deallocation. ```swift class Manager { let queue = AsyncQueue() // Keep strong reference } ``` -------------------------------- ### Coordinated Cleanup with AsyncQueue Source: https://github.com/groue/asyncqueues/blob/main/_autodocs/USAGE_GUIDE.md Ensures all pending operations complete before performing cleanup actions like closing sockets. ```swift class ConnectionManager { private let queue = AsyncQueue() func closeConnection() async throws { try await queue.perform { // Ensures all pending operations complete before closing try await flushPendingRequests() try await closeSocket() try await cleanup() } } } ``` -------------------------------- ### AsyncQueue Cancellation Behavior Source: https://github.com/groue/asyncqueues/blob/main/_autodocs/QUEUE_COMPARISON.md Demonstrates how AsyncQueue executes operations even after cancellation. Operations must manually check for cancellation. ```swift let queue = AsyncQueue() let task = queue.addTask { print("Starting") try await Task.sleep(nanoseconds: 1_000_000_000) // 1 second print("Done") } task.cancel() try await task.value // Prints "Starting", then "Done" after 1 second ``` -------------------------------- ### DiscardingAsyncQueue to CoalescingAsyncQueue Migration Source: https://github.com/groue/asyncqueues/blob/main/_autodocs/QUEUE_COMPARISON.md Migrating from DiscardingAsyncQueue to CoalescingAsyncQueue. CoalescingAsyncQueue allows specifying policies for tasks, such as .required or .discardable. ```swift // Before (DiscardingAsyncQueue) let queue = DiscardingAsyncQueue() queue.addTask { try await requiredWork() } queue.addTask { try await optionalWork() } // After (CoalescingAsyncQueue) let queue = CoalescingAsyncQueue() queue.addTask(policy: .required) { try await requiredWork() } queue.addTask(policy: .discardable) { try await optionalWork() } ``` -------------------------------- ### addTask(operation:) Source: https://github.com/groue/asyncqueues/blob/main/_autodocs/api-reference/DiscardingAsyncQueue.md Adds an asynchronous operation to the queue, returning an unstructured Task. Cancellation before prior operations complete results in CancellationError. ```APIDOC ## addTask(operation:) ### Description Returns an unstructured Task that runs the given operation. If the returned task is cancelled before the previously enqueued operations complete, `operation` is not executed and the task throws a `CancellationError`. The operation is considered "discarded". Otherwise, `operation` can check `Task.isCancelled` or `Task.checkCancellation()` to detect cancellation. You need to keep a reference to the task if you want to cancel it by calling the `Task.cancel()` method. Discarding your reference to the task doesn't implicitly cancel that task. ### Parameters #### Path Parameters - operation (sending @escaping () async throws -> Success) - Required - An async closure that returns a value of type `Success` or throws an error ### Returns `Task` — A task that executes the `operation`. ### Throws - Any error thrown by `operation` (through the returned task) - `CancellationError` if the task is cancelled before previously enqueued operations complete ### Example ```swift let queue = DiscardingAsyncQueue() let task = queue.addTask { try await someAsyncValue() } do { let value = try await task.value } catch is CancellationError { print("Task was cancelled before it could start") } ``` ``` -------------------------------- ### Handling Cancellation in AsyncQueue Source: https://github.com/groue/asyncqueues/blob/main/_autodocs/REFERENCE.md Shows how to check for cancellation within an operation enqueued in an `AsyncQueue`. Cancellation is handled after previously enqueued operations complete. ```swift let queue = AsyncQueue() await queue.perform { if Task.isCancelled { print("I'm cancelled") } } ``` -------------------------------- ### CoalescingAsyncQueue Execution Flow: .required Policy Source: https://github.com/groue/asyncqueues/blob/main/_autodocs/ARCHITECTURE.md Using perform() with the .required policy creates an unstructured task that is checked for cancellation using a TaskCancellationState. This ensures the operation executes normally while allowing for cancellation handling. ```text Create unstructured task via addTask(policy: .required, ...) ↓ Use withTaskCancellationHandler to catch cancellation ↓ Execute normally, cancellation checked via TaskCancellationState ``` -------------------------------- ### Sharing a Single Queue Instance Source: https://github.com/groue/asyncqueues/blob/main/_autodocs/USAGE_GUIDE.md Ensures operations are serialized correctly by using a single, shared queue instance across different parts of the application. ```swift let sharedQueue = AsyncQueue() // Use sharedQueue in multiple places ``` -------------------------------- ### TaskCancellationState Enum and Mutex Extension Source: https://github.com/groue/asyncqueues/blob/main/_autodocs/ARCHITECTURE.md Defines the TaskCancellationState enum to track task status and provides an extension on Mutex to manage task creation and cancellation. ```swift enum TaskCancellationState { case notStarted case cancellable(@Sendable () -> Void) case cancelled } extension Mutex { func startTask(_ makeTask: () -> Task) -> Task func cancelTask() } ``` -------------------------------- ### Image Processing with DiscardingAsyncQueue Source: https://github.com/groue/asyncqueues/blob/main/_autodocs/QUEUE_COMPARISON.md Handles image processing where only the latest image needs processing. Previous processing tasks are discarded if a new image is selected, saving CPU resources. ```swift class ImageProcessor { private let queue = DiscardingAsyncQueue() // ✅ DiscardingAsyncQueue func processImage(_ image: UIImage) async throws -> UIImage { try await queue.perform { try await applyFilters(image) } } } // Usage: Only the latest image needs processing var currentTask: Task? func startProcessing(_ image: UIImage) { currentTask?.cancel() currentTask = Task { let processed = try await imageProcessor.processImage(image) updateUI(processed) } } ``` -------------------------------- ### TaskLocal for Active Queue Tracking Source: https://github.com/groue/asyncqueues/blob/main/_autodocs/ARCHITECTURE.md Queues utilize a @TaskLocal variable to track active queue IDs, enabling context verification and nested queue tracking with zero runtime cost when not in use. ```swift @TaskLocal private static var currentQueueIds = Set() ``` -------------------------------- ### Task Tracking for Explicit Control Source: https://github.com/groue/asyncqueues/blob/main/_autodocs/USAGE_GUIDE.md Maintain references to tasks within an AsyncQueue to enable explicit control over their lifecycle, such as cancellation or monitoring completion. ```swift class ImageProcessor { private let queue = AsyncQueue() private var currentTask: Task? func processImage(_ image: UIImage) { // Keep reference to track/cancel currentTask = queue.addTask { let result = try await applyFilters(image) return result } } func cancelProcessing() { currentTask?.cancel() currentTask = nil } func waitForCompletion() async throws -> UIImage { guard let task = currentTask else { throw NSError(domain: "No processing in progress", code: -1) } return try await task.value } } ``` -------------------------------- ### Discardable Operations with CoalescingAsyncQueue Source: https://github.com/groue/asyncqueues/blob/main/_autodocs/REFERENCE.md Shows how to use `CoalescingAsyncQueue` to mark operations as required or discardable. Discardable operations are cancelled if a newer operation is enqueued. ```swift let queue = CoalescingAsyncQueue() // Required operation - always completes try await queue.perform { try await saveToServer() } // Discardable operation - cancelled if another is enqueued try await queue.perform(policy: .discardable) { try await fetchLatestData() } ``` -------------------------------- ### Ensure Execution within AsyncQueue Context Source: https://github.com/groue/asyncqueues/blob/main/_autodocs/api-reference/AsyncQueue.md Use `preconditionSerialized` to verify that a block of code is executing within the serialized context of the `AsyncQueue`. This is crucial for maintaining data integrity and preventing race conditions in concurrent operations. ```swift public func preconditionSerialized( _ message: @autoclosure () -> String = String(), file: StaticString = #fileID, line: UInt = #line ) ``` ```swift let queue = AsyncQueue() await queue.perform { queue.preconditionSerialized("This code must run in the queue") } ``` -------------------------------- ### Add Unstructured Task with CoalescingAsyncQueue Policy Source: https://github.com/groue/asyncqueues/blob/main/_autodocs/INDEX.md Illustrates adding an unstructured task to a CoalescingAsyncQueue with a specific policy. This is useful for managing concurrent updates where newer requests might supersede older ones. ```swift // Create task with policy let task = queue3.addTask(policy: .required) { try await someWork() } ``` -------------------------------- ### Nested Queues for Different Serialization Concerns Source: https://github.com/groue/asyncqueues/blob/main/_autodocs/USAGE_GUIDE.md Utilize multiple AsyncQueues to manage distinct serialization requirements for reads and writes, allowing independent processing while enabling coordination for atomic operations. ```swift class DatabaseManager { private let writeQueue = AsyncQueue() private let readQueue = AsyncQueue() func read(_ query: String) async throws -> T { try await readQueue.perform { // Multiple reads can be serialized separately try await queryDatabase(query) } } func write(_ data: [String: Any]) async throws { try await writeQueue.perform { // Writes are serialized independently from reads try await updateDatabase(data) } } // Atomic operation: serialized in both queues func atomicReadThenWrite(_ readQuery: String, writeData: [String: Any]) async throws -> T { try await readQueue.perform { let result = try await queryDatabase(readQuery) try await writeQueue.perform { try await updateDatabase(writeData) } return result } } } ``` -------------------------------- ### Verification with preconditionSerialized Source: https://github.com/groue/asyncqueues/blob/main/_autodocs/USAGE_GUIDE.md Use `preconditionSerialized` to add debug assertions that ensure critical code sections are executed within the expected serialization context of an AsyncQueue. ```swift class DatabaseTransaction { private let queue = AsyncQueue() private var transactionActive = false func beginTransaction() async throws { try await queue.perform { queue.preconditionSerialized("Must run in queue") // Verify we're in the queue's context transactionActive = true try await startTransaction() } } func commitTransaction() async throws { try await queue.perform { queue.preconditionSerialized("Must run in queue") // Development safety check precondition(transactionActive, "No active transaction") try await commit() transactionActive = false } } } ``` -------------------------------- ### Identifiable Conformance Source: https://github.com/groue/asyncqueues/blob/main/_autodocs/api-reference/CoalescingAsyncQueue.md CoalescingAsyncQueue conforms to the Identifiable protocol, providing a unique ID for tracking and identity purposes. ```APIDOC ## Identifiable Conformance ### Description `CoalescingAsyncQueue` conforms to `Identifiable`, allowing it to be uniquely identified. Access its identifier through the `id` property. ### Property * **id** (`CoalescingAsyncQueue.ID`) - The unique identifier for the queue. ### Example ```swift let queue = CoalescingAsyncQueue() let queueID = queue.id // Type: CoalescingAsyncQueue.ID ``` ``` -------------------------------- ### Debouncing Operations with DiscardingAsyncQueue Source: https://github.com/groue/asyncqueues/blob/main/_autodocs/USAGE_GUIDE.md Implement debouncing for operations using `DiscardingAsyncQueue` to limit the rate at which a function can be called, useful for scenarios like search input. ```swift class SearchController { private let queue = DiscardingAsyncQueue() func search(_ query: String) async throws -> [Result] { try await Task.sleep(nanoseconds: 300_000_000) // 300ms debounce do { return try await queue.perform { try await performSearch(query) } } catch is CancellationError { return [] // User typed more, cancel this search } } } ``` -------------------------------- ### DiscardingAsyncQueue Public API Source: https://github.com/groue/asyncqueues/blob/main/_autodocs/README.md Documentation for the DiscardingAsyncQueue type, which prioritizes eager task cancellation, discarding operations before they begin. ```APIDOC ## DiscardingAsyncQueue ### Description Represents a queue that performs eager task cancellation, canceling operations before they start. ### Methods #### `init()` - **Description**: Constructor for the DiscardingAsyncQueue. #### `perform(operation:)` - **Description**: Executes a given operation. All operations are throwing. - **Parameters**: - `operation` (Operation): The operation to perform. #### `addTask(operation:)` - **Description**: Creates an unstructured task to perform the operation. All operations are throwing. - **Parameters**: - `operation` (Operation): The operation to add as a task. ### Types - `DiscardingAsyncQueue.ID`: Identity type for tracking DiscardingAsyncQueue instances. ### Conformances - `Sendable`: Indicates that the queue is thread-safe and can be passed between tasks. - `Identifiable`: Allows tracking of queue instances via an `.id` property. ``` -------------------------------- ### addTask(operation:) Source: https://github.com/groue/asyncqueues/blob/main/_autodocs/api-reference/AsyncQueue.md Adds a throwing asynchronous operation to the queue. The operation will be executed after all preceding operations are finished. Returns a Task that can be used to monitor or cancel the operation. ```APIDOC ## addTask(operation:) - Throwing Overload ### Description Returns an unstructured Task that runs the given throwing operation. The `operation` closure runs after previously enqueued operations are completed. If the returned task is cancelled, it still runs the `operation` closure. Check `Task.isCancelled` or `Task.checkCancellation()` to detect cancellation. You need to keep a reference to the task if you want to cancel it by calling the `Task.cancel()` method. Discarding your reference to the task doesn't implicitly cancel that task. ### Method `@discardableResult public func addTask(operation: sending @escaping () async throws -> Success) -> Task` ### Parameters #### Path Parameters - **operation** (`sending @escaping () async throws -> Success`) - Required - An async closure that returns a value of type `Success` or throws an error ### Returns `Task` — A task that executes the `operation`. ### Throws The error of `operation` is propagated through the returned task. ### Example ```swift let queue = AsyncQueue() let task = queue.addTask { try await someAsyncValue() } let value = try await task.value ``` ``` -------------------------------- ### CoalescingAsyncQueue Error Handling with Discard Policy Source: https://github.com/groue/asyncqueues/blob/main/_autodocs/USAGE_GUIDE.md Illustrates error handling for CoalescingAsyncQueue when using the .discardable policy, catching CancellationError for early cancellations. ```swift let queue = CoalescingAsyncQueue() do { try await queue.perform(policy: .discardable) { try await somethingThatMayFail() } } catch is CancellationError { print("Cancelled by task cancellation or newer operation") } catch { print("Operation failed: \(error)") } ``` -------------------------------- ### Semaphore Structure Source: https://github.com/groue/asyncqueues/blob/main/_autodocs/ARCHITECTURE.md Defines a Semaphore structure with methods for waiting and signaling, used for synchronization between operations and the background task. ```swift struct Semaphore { private let stream: AsyncStream private let continuation: AsyncStream.Continuation func wait() async func waitUnlessCancelled() async throws func signal() } ``` -------------------------------- ### Add Task with Discardable Policy Source: https://github.com/groue/asyncqueues/blob/main/_autodocs/api-reference/CoalescingAsyncQueue.md Adds an operation to the queue with a discardable policy. The task will be cancelled if another operation is enqueued before it completes. Keep a reference to the task to manage its lifecycle. ```swift let queue = CoalescingAsyncQueue() // This task will be cancelled if another operation is enqueued let task = queue.addTask(policy: .discardable) { try await someAsyncValue() } do { let value = try await task.value } catch is CancellationError { print("Task was cancelled by a subsequent operation or explicit cancellation") } ``` -------------------------------- ### Add Throwing Task to AsyncQueue Source: https://github.com/groue/asyncqueues/blob/main/_autodocs/api-reference/AsyncQueue.md Adds a throwing asynchronous operation to the queue, returning an unstructured Task. The operation runs after preceding operations complete. Keep a reference to the returned Task to manage cancellation. ```swift let queue = AsyncQueue() let task = queue.addTask { try await someAsyncValue() } let value = try await task.value ``` -------------------------------- ### Perform Operation with AsyncQueue Source: https://github.com/groue/asyncqueues/blob/main/Sources/AsyncQueues/Documentation.docc/Documentation.md Executes an asynchronous operation within the queue and returns its result. This is useful for operations where you need the return value directly. ```swift let value = try await queue.perform { try await someValue() } ``` -------------------------------- ### API Updates with CoalescingAsyncQueue Source: https://github.com/groue/asyncqueues/blob/main/_autodocs/QUEUE_COMPARISON.md Manages API updates, ensuring critical saves complete while allowing background refreshes to be skipped if a save occurs. Prevents unnecessary network traffic. ```swift class APIManager { private let queue = CoalescingAsyncQueue() // ✅ CoalescingAsyncQueue // Required: user-initiated save func saveChanges(_ data: Data) async throws { try await queue.perform(policy: .required) { try await uploadToServer(data) } } // Discardable: background refresh func refreshData() async throws { try await queue.perform(policy: .discardable) { try await downloadLatest() } } } ```