let task:Task<Void, Never> =
{
var sleep:Task<Void, Never> = .init
{
try? await Task.sleep(for: .milliseconds(heartbeat))
}
// sends the sleep task to someone who might cancel it
send(sleep: sleep)
await sleep.value
}
task.cancel()
// will this resume immediately?
await task.value
main task: awaits task, can cancel task
task: started from the main task, creates sleep tasks that escape to someone who may cancel them early, and awaits on those sleep tasks.
sleep: started from the task task, sleeps until the time runs out, or someone cancels it.
now, my question is: what happens to the sleep tasks if the main task cancels the task task?
You're using un-structured tasks here, so there's no cancellation relation between any of these tasks.
Only child tasks, i.e. "structured concurrency", have the property that "inner" ("child") tasks are cancelled when their parents are. Only ways to create child tasks are: TaskGroup and async let.
At least in this toy example, the sleep task could be an async let or a group child task and you'd get cancellation propagation as expected.
// Relatedly, I'll see if/how/when we can update the swift book to provide more details about these things
for the longest amount of time i thought “structured concurrency” meant Task, and “unstructured concurrency” referred to things like EventLoopFuture...
I agree we should have better and more explicit docs in the swift book about this btw. It’s a … process, to get things fixed there but we should be able to do so soon since it’s become open source and easier to submit changes to
yeah, i figured that out around late 2021, my point is the swift 5.5+ concurrency features were pitched as “adding structured concurrency” to the language, so for a long time i thought all of the new features (including Task) counted as “structured concurrency”.
i think a lot of the early docs also fixated a lot on the difference between Task.init and Task.detached, which made it seem like “structuredness” had something to do with “detachedness”.
Exactly this - I thought that .detached was what broke it out the structure, not to mention that there was some _inherentContext or something similar in the Task initialization, which I took to mean that it was inheriting structure from the parent.
I'll also add that I've watched that video more than once. Perhaps it's my fault for not letting that point sink in, but maybe it could have been clearer in the video? I'll have to watch it again with new context to provide better feedback.