The procedure only ends when all its children terminate. Children can automatically terminate when there are no more ways to receive messages with a terminate option in the select clause.
Unfortunately, I don’t know Ada, so it’s hard to evaluate. I’m posting this as a placeholder in case someone with Ada experience would like to elaborate.
I did a bit of research on this a while ago, and as far as I could tell, Ada has both stack-allocated tasks, which have a structured feel (exiting the scope where they’re defined blocks waiting for the task to exit, I think?), and also heap-allocated tasks, which have potentially unbounded extent? This link is useful: Safe, Dynamic Task Creation in Ada
It is true that Ada has both stack-allocated task objects and heap-allocated task objects. For stack-allocated tasks, the tasks are awaited before the scope where the object is declared goes away. The creation of a heap-allocated task object produce a pointer value of a particular “access type,” and the place where that access type is declared determines the scope where such a task is awaited. Normally such access types are declared at the library level, meaning that it is only when the program is about to exit that it awaits such tasks.
Lighter-weight concurrency constructs are in the process of being added to Ada for the proposed 2020 version of the language. These are all “structured” concurrency constructs – parallel loops, parallel blocks, and parallel map/reduce constructs. The existing Ada heap-allocated tasks will remain the only way to “spawn off” a thread of control that runs indefinitely.