On 1/11/21 10:42 AM, Arafel wrote:
On 11/1/21 14:42, Steven Schveighoffer wrote:

That isn't exactly true. Member variables are members of the object. If the object is shared, the member variables are shared. If the object is local the variables are local.

Thread local really only applies to *static* variables, such as globals or members declared static. If that were the case, yes, the other thread would not see the object.

I did not respond to the OP because I also don't know why it wouldn't work. But I also don't know what all the code is doing.


Out of curiosity, what happens with members that are declared `shared` in a non-shared object?

A shared member is a sharable member of the class. It does not put the item in global storage.

There are some... odd rules.

struct S
{
   static int a; // TLS
   shared static int b; // shared data storage
   shared int c; // local variable, but its type is shared(int)
immutable int d; // local immutable variable, settable only in constructor
   immutable int e = 5; // stored in data segment, not per instance!
__gshared int f; // stored in global segment, typed as int, not shared(int)
}

I thought that declaring an object `shared` "promotes" its members, but that it wasn't strictly needed for a non-shared object to have shared members, but I might be wrong here.

There are 2 different things here -- storage and type. shared as a storage class (i.e. without the parentheses) means 2 things:

1. for variables that are declared to be globals (either static or at module level), shared puts it in the shared data segment vs. thread local storage. For variables in all other declaration contexts, they are just stored where declared (either inside the instance or on the stack or whatever).
2. The type is modified to shared(T) instead of T.

```
struct S {}

class A {
     S s1;
     shared S s2;
}

void main() {
     A a1 = new A();
     pragma(msg, typeof(a1.s1)); // S
     pragma(msg, typeof(a1.s2)); // shared(S)
     shared A a2 = new shared A();
     pragma(msg, typeof(a2.s1)); // shared(S)
     pragma(msg, typeof(a2.s2)); // shared(S)
}
```

https://run.dlang.io/is/skCfvE

Of course I don't know the practical differences in the actual accessibility of the different members beyond the type system.

In the type system, shared basically means "other threads may have access". Right now, shared is kind of useless, because nothing is truly enforced except implicit conversions to/from shared are disallowed. In the future, shared data will be REQUIRED to be cast to unshared for usage.


Is there any way to check if a pointer is actually TLS or global storage? If `a1.s2` can't be properly accessed from different threads, I'd consider that a big bug in the `shared` implementation.

You are misunderstanding, a1 is stored on the heap, a2 is stored on the heap. In both cases a1.s2 and a2.s2 are stored in the object that is on the heap. The *type* being shared means you can pass its address to another thread (and semantically, shared means "another thread may be using this").

In order to ask the compiler to stick it in TLS or global shared storage, you have to mark it `static`.

-Steve

Reply via email to