On Friday, 24 January 2014 at 09:50:44 UTC, Dicebot wrote:
On Friday, 24 January 2014 at 08:11:53 UTC, Stanislav Blinov wrote:
...Unless the thread is started with a delegate (literal or member function), which implicitly gains an unsynchronized view of its enclosing scope (or class).

Which means accessing shared data pretty much by definition.

I would rephrase that as "implicitly sharing not explicitly shared data".

It actually should not even compile without explicit casts. I guess yet another delegate qualifier bug.

Why shouldn't it compile? Safe spawner (std.concurrency.spawn) does not accept delegates. Unsafe one (core.thread.Thread) does. I wouldn't consider it a bug since in the context of one thread it's pretty much a feature :) But I agree that some special syntax for threading and otherwise restricting sharing would be great. Maybe another set of parentheses?

Consider:

// Call could be anything. Just a call, do-some-work-and call,
// spawn a thread, etc.
void call(F,Args...)(F dg,Args args) if (is(F == delegate)) {
    dg(args);
}

void main() {

    int myPreciousInt = 42;
    string myPreciousString = "precious";

// current syntax, horrifying if call spawns a thread with that delegate
    call({
        myPreciousInt = 151; // modifies main's myPreciousInt
        myPreciousString = "stolen"; // ditto
    });

    // current syntax with parameters, ditto
    call((int i){
        myPreciousInt = i;
        myPreciousString = "stolen";
    });

    // explicit capture syntax:
    call((myPreciousInt){    // capture by value
        myPreciousInt = 132; // changes local variable
myPreciousString = "stolen"; // would not compile, variable is not captured
    });

    // explicit capture with parameters:
    call((ref myPreciousInt)(int i){ // capture by reference
myPreciousInt = 144; // will modify main's myPreciousInt too
    });

}


This could be extended to support various capture qualifies:

ref - by reference
in - by move
ref shared - by reference if variable is shared

etc.

Looks like C++'s [](){}, perhaps, but with D's powerful compile-time facilities we could do so much more. For example, the above, coupled with some introspection with e.g. __traits(captures, dg) could give way to implementing safe thread spawners even for delegates: e.g. disallow capturing non-shared data by reference.

As I mentioned, currently pretty much all that can be done is a shallow copy of the stack (which means allocation), and I don't even know how portable or safe that is :)


Um, duh, but in d data is already TLS.

1) not necessarily, is is only default

2) "using TLS data" usually implies "using _own_ TLS data" as you shouldn't be able to get reference to TLS of other thread without dirty hacks (it breaks basic type system assumptions)

In my understanding delegates are pretty much unique at that. Probably because they were redesigned like that since D1, but never were taken one step further towards multithreading.

Reply via email to