Member variables in method are null when called as delegate from thread
Steven Schveighoffer
schveiguy at gmail.com
Mon Jan 11 16:10:49 UTC 2021
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
More information about the Digitalmars-d-learn
mailing list