A mostly* FIFO queuing system leveraging Swift's async/await structured concurrency. Also includes FutureTask
which allows you to arbitrairily delay/start a task.
*mostly: async/await is utilized to append tasks to the queue, so, if several tasks are appended in quick succession, we are at the whims of async/await to order things correctly. However, the start** order is guaranteed once the task is added.
**started: if the queue allows multiple concurrent tasks, shorter tasks started after a longer task will likely finish prior to the longer, first task.
import AsyncConcurrentQueue
// AsyncConcurrentQueue
Task {
// initialize a new queue
let queue = AsyncConcurrentQueue()
// set how many tasks may run concurrently
queue.setMaximumConcurrentTasks(5)
// feed it a bunch of tasks to perform.
for i in 1...20 {
let task = await queue.createTask {
print("starting \(i)")
try await Task.sleep(for: .seconds(Double.random(in: 0.5...2)))
print("finishing \(i)")
return i
}
Task {
let value = try await task.value
print("Got \(value) from \(i)")
}
}
}
// FutureTask
Task {
// create future task. Note that it does not run right away.
let futureTask = FutureTask {
print("Waited until called")
}
// create normal task for comparison. Note that it runs almost immediately.
Task {
print("Ran immediately")
}
// create a separate future task. It doesn't run right away, but also has a cancellation handler.
let canceller = FutureTask(
operation: {
print("Will never run")
},
onCancellation: {
print("Cancelled before activation")
})
// wait half a second
try await Task.sleep(for: .seconds(0.5))
// cancel the `canceller` task. The cancellation handler will run.
canceller.cancel()
// wait another couple seconds
try await Task.sleep(for: .seconds(2))
// activate the `futureTask`, allowing it to proceed now.
futureTask.activate()
}
The current implementation is pretty poor at handling FIFO when tasks are added in quick succession. My future plan is to add sync methods for adding tasks, taking in async closures (similar to now), but to revamp the internals to just immediately append the tasks to an array. This would solidify the order. THEN the async system would one by one retrieve the first element of the array to create the async tasks. (That's the plan, at least. There will be several challenges, like maintaining the generic return value of the queued tasks, but that's a problem for Monday Michael.)