TLF = thread local functions

Stanislav Blinov stanislav.blinov at gmail.com
Fri Jan 24 02:43:04 PST 2014


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.


More information about the Digitalmars-d-learn mailing list