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:

examples/users-guide/taskpar/cobegin.chpl
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:

examples/users-guide/taskpar/cobegin+begin.chpl
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.