Skip to content

Instantly share code, notes, and snippets.

@ertembiyik
Created October 11, 2025 13:18
Show Gist options
  • Save ertembiyik/80876683c5390f4b51ba652949b69441 to your computer and use it in GitHub Desktop.
Save ertembiyik/80876683c5390f4b51ba652949b69441 to your computer and use it in GitHub Desktop.
KeyedTaskExecutor
import Foundation
fileprivate protocol CancellableOperation {
func cancelOperation()
}
extension Task: CancellableOperation {
fileprivate func cancelOperation() {
self.cancel()
}
}
final class KeyedTaskExecutor: @unchecked Sendable {
#if compiler(>=6.0)
typealias ThrowingOperation<Success> = @isolated(any) @Sendable () async throws -> sending Success
typealias Operation<Success> = @isolated(any) @Sendable () async -> sending Success
#else
typealias ThrowingOperation<Success: Sendable> = @Sendable () async throws -> Success
typealias Operation<Success: Sendable> = @Sendable () async -> Success
#endif
private var tasks: [String: CancellableOperation] = [:]
private let lock = NSLock()
func executeOperation<Success>(for key: String,
@_inheritActorContext operation: @escaping Operation<Success>) async -> Success {
let task = self.lock.withLock {
if let existingTask = self.tasks[key] as? Task<Success, Never> {
return existingTask
}
let newTask = Task<Success, Never> {
return await operation()
}
self.tasks[key] = newTask
return newTask
}
defer {
self.lock.withLock {
self.tasks[key] = nil
}
}
return await task.value
}
func executeOperation<Success>(for key: String,
@_inheritActorContext operation: @escaping ThrowingOperation<Success>) async throws -> Success {
let task = self.lock.withLock {
if let existingTask = self.tasks[key] as? Task<Success, Error> {
return existingTask
}
let newTask = Task<Success, Error> {
return try await operation()
}
self.tasks[key] = newTask
return newTask
}
defer {
self.lock.withLock {
self.tasks[key] = nil
}
}
return try await task.value
}
func cancelOperation(with key: String) {
self.lock.withLock {
self.tasks[key]?.cancelOperation()
self.tasks[key] = nil
}
}
}
@ertembiyik
Copy link
Author

ertembiyik commented Oct 11, 2025

allows to execute only one task at a time for a given key by awaiting an existing one if present

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment