Here is an extract of a code
const D = {1..nx, 1..ny},
Dx = {1..(nx+1),1..ny},
Dy = {1..nx,1..(ny+1)};
param rank = D.rank;
// problem space
var halo: rank*int = (3,3);
const PSpace = D dmapped new stencilDist(D, fluff=halo, periodic=true);
var u : [PSpace] real;
var res: [PSpace] real;
forall (i,j) in Dx
{
const ul = weno5(u[i-3,j],u[i-2,j],u[i-1,j],u[i,j],u[i+1,j]);
res[i-1,j] += ul * dy;
res[i,j] -= ul * dy;
}
In the forall loop, I update res at i-1 and i. How does Chapel ensure that this happens without conflict ?
Chapel doesn't prevent conflicts from occurring in race conditions like this. It is designed such that if you tell it to do something in parallel—for example, by using a forall loop, as in this case—it will execute it in parallel, whether it is safe to so do so or not. This is in part to avoid cases where the compiler would need to make conservative / suboptimal choices based on not being able to determine whether a code is race-free or not; and in part because in some codes and applications, races can be acceptable and we did not want the language to prevent such patterns from being written.
As an example of how the difference between race-y and race-free code can be subtle, if your forall loop had iterated over Dx strided by 2, the adjacent writes would be OK. While there are cases like this one that are reasonably straightforward to classify as being safe or unsafe, in general, it can be impossible for a compiler to make these determinations, so we rely on the programmer to make them instead.
Assuming you want every iteration of the loop to update two adjacent array elements like this, one way to make it safe would be to have the res array store elements of type atomic int. As Chapel currently stands, this would require updating the += and -= operations to method calls:
var res: [PSpace] atomic real;
...
res[i-1,j].add(ul * dy);
res[i,j].sub(ul * dy);