Struggling to implement parallel foreach...

Manu turkeyman at gmail.com
Fri Jun 14 01:40:46 UTC 2019


So, I want to get to here:

foreach(x; data.parallel(bucketSize))
{
  // run on worker threads...
}

Phobos alleges to have code for this, but it's not threadsafe, and
missing all the pieces that I'm having trouble with.

The TL;DR boils down to this; opApply receives a delegate that is NOT shared.

Problem:

struct S
{
  int opApply(scope int delegate(Iter) shared loopBody) { ... }
}

S s;
foreach(i; s) { ... }

Error: function `S.opApply(scope int delegate(int) shared loopBody)`
is not callable using argument types `(int delegate(int i) pure
nothrow @nogc @safe)`
cannot pass argument `__foreachbody2` of type `int delegate(int i)
pure nothrow @nogc @safe` to parameter `scope int delegate(int) shared
loopBody`

Obviously the loop body is not a shared function... but that's what is required.

So, it seems the arguments to opApply are considered when infering the
types for the foreach loop counters, so features of the delegate are
being inferred already, but I need the attributes of the delegate to
be inferred too, specifically, the `scope` attribute on the delegate
needs to be applied to the loop function.

This has cascading issues; the closure contains references to outer
objects, but if the delegate is `shared`, then all those references
transitively inherit the shared attribute too. It should work exactly
like I if it was writing a `shared` method to some struct, and all the
members are shared... so the body of the foreach needs to typecheck
assuming the transitive application of the inferred attributes from
the opApply signature.

As an experiment, I tried this:

void test()
{
        int x;
        void fun() shared
        {
            x++; // <-- error here
        }
        fun();
}

And I got this error:

Error: `shared` function `test.fun` cannot access non-shared data `x`

That looks like a bug. `fun` is a delegate, and it's shared, which
means its `this` pointer is shared, which means `this.x` should be
shared... but that seems not to be the case.

I think this is a bug. The compile error should have been that I was
unable to call fun() with a non-shared delegate (since the calling
scope is not shared).

I think fixing that issue, combined with inferring the opApply
argument attributes onto the foreach body lambda give everything I
need to implement a threadsafe foreach.

That was super hard to follow, sorry in advance!


More information about the Digitalmars-d mailing list