Hi @00shiv —
I think you are correct that Gemini is hallucinating. As you say, the Chapel sync statement is a dynamically scoped "join tasks" concept.
The two ways to do this natively in the language would be to use a sync or atomic variable to guard a critical section. E.g., using a sync, I might write:
use Time;
config const nTasks = here.maxTaskPar;
var myLock: sync int;
coforall i in 1..nTasks {
myLock.writeEF(i); // blocks until empty, leaves full
sleep(1);
writeln("task ", i, " in critical section");
sleep(1);
const completedTask = myLock.readFE(); // readFE leaves empty
writeln("task ", completedTask, " finished");
}
Here, the use of the int value in the sync int is almost beside the point since we're really only using this sync for its full/empty state and not its value. I'm obviously using it to store a logical task ID, but not for any deep purpose, and I could just as easily write the same integer for each task. We've discussed adding support for a sync void that only had the full empty state for this reason, but it doesn't look like we've ever gotten around to adding that.
Using an atomic, I might write this pattern like:
use Time;
config const nTasks = here.maxTaskPar;
var myLock: atomic bool;
coforall i in 1..nTasks {
do {
var expect = false;
} while !myLock.compareExchange(expect, true);
sleep(1);
writeln("task ", i, " in critical section");
sleep(1);
myLock.write(false);
writeln("task ", i, " done");
}
where here, I am using the value of the boolean to indicate whether the lock is held or not.
Generally, when giving guidance between using a sync or an atomic I tend to recommend the sync in pessimistic situations where you imagine that most tasks will be waiting for their turn to get into the critical section, because they have generally been architected to minimize spending resources on blocked tasks; whereas in a situation where you expect most tasks to not be waiting on the critical section, I tend to think of the atomic as being better, in terms of being a lighter-weight mechanism, but one that uses more busy-waiting, at least as I've written it here with the continual spin on the compareExchange() call.
I'll also mention that there's an internal module modules/internal/ChapelLocks.chpl that implements a local spin lock that we use internally in our code a fair amount and that we've discussed making into a standard library or mason package. It isn't intended to be user facing and isn't stable as a result, but could be worth looking at, and of course you're welcome to use it at your own risk.
Hope this is helpful,
-Brad