cobegin Statements: creating groups of tasks¶
The cobegin statement creates a fixed number of related, yet
potentially heterogeneous, tasks. It is expressed by prefixing a
compound statement with the cobegin
keyword, as illustrated by the
following example:
cobegin {
writeln("Hi!");
sayHello();
{
writeln("...Greetings...");
writeln("...puny earthling...");
}
}
writeln("See you later!");
proc sayHello() {
writeln("Hello!");
}
At execution time, a distinct task is created for each child statement
within the cobegin statement. Thus, in the example above, three tasks
will be created, corresponding to the three statements immediately
within the cobegin’s block statement: one for the standalone
writeln(), one for the call to sayHi()
, and one for the compound
statement. Since these tasks are likely to execute in parallel and
don’t coordinate with one another, the ordering of their messages
relative to one another is arbitrary. Note that the two statements
within the compound statement will always be printed in the order
shown since they are executed by a single task.
Cobegin statements also specify that the original task which encounters it cannot proceed until its child tasks have completed. In the example above, this means that “See you later!” will not be printed until all of the greetings have been.
As a result of these rules, possible outputs for the code above include:
...Greetings...
...puny earthling...
Hello!
Hi!
See you later!
or
Hello!
...Greetings...
Hi!
...puny earthling...
See you later!
but never:
...puny earthling...
Hi!
...Greetings...
See you later!
Hello!
(because this would imply that the third task did not run serially and that the original task did not wait for its children to complete before going on).
Child vs. Descendent Tasks¶
It’s worth noting that cobegin statements only wait for their
immediate child tasks to complete before proceeding, ignoring any
further descendent tasks. Thus, if a cobegin’s task creates an
additional task using a begin
statement, that new task executes
asynchronously and has no bearing on the cobegin’s completion
semantics.
As an example, consider the following program:
cobegin {
writeln("Hi!");
printHellos();
}
writeln("Sorry, I've gotta leave...");
proc printHellos() {
writeln("Beginning...");
begin {
for i in 1..10 do
writeln("Hello!");
}
writeln("I'm moving on...");
}
Here, it’s possible for the “Sorry, I’ve gotta leave…” message to be printed before all of the “Hello!” messages, since they are written by an asynchronous task. However, since the original child task printed the “Beginning…” and “I’m moving on…” messages, those are guaranteed to have executed and in that order. For these reasons, the following would be a possible output:
Beginning...
Hello!
Hello!
Hi!
Hello!
Hello!
I'm moving on...
Hello!
Hello!
Hello!
Sorry, I've gotta leave...
Hello!
Hello!
Hello!
However, the following output could never occur:
Hello!
Hello!
Beginning...
Hello!
Hi!
Hello!
Sorry, I've gotta leave...
Hello!
Hello!
Hello!
I'm moving on...
Hello!
Hello!
Hello!
Hello!
First, because it’s not possible for “Hello!” messages to print before
the “Beginning…” message since the begin
will not have been
encountered yet. Second, because the “Sorry” message can’t print
prior to “I’m moving on…” since it has to wait for its immediate
children to finish.