handling shared objects

Steven Schveighoffer schveiguy at gmail.com
Mon Nov 26 14:28:33 UTC 2018


On 11/26/18 9:00 AM, Alex wrote:
> Hi all!
> Can somebody explain to me, why the example below is not working in a 
> way I'm expecting it to work?
> 
> My example is a little bit longer this time, however the half of it is 
> taken from
> https://dlang.org/library/std/concurrency/receive_only.html
> 
> ´´´
> import std.experimental.all;
> 
> struct D
> {
>      size_t d;
>      static S s;
> }
> struct S
> {
>      D[] data;
> }
> 
> struct Model
> {
>      auto ref s()
>      {
>          return D.s;
>      }
> 
>      void run()
>      {
>          "I'm running".writeln;
>          writeln(s.data.length);
>      }
> }
> 
> Model m;
> 
> void main()
> {
> 
>      D.s.data.length = 4;
>      m.run; //4
> 
>      auto childTid = spawn(&runner, thisTid);
>      send(childTid, 0);
>      receiveOnly!bool;
> }
> 
> static void runner(Tid ownerTid)
> {
>      receive((size_t dummy){
>          import core.thread : Thread;
> 
>          m.run;
>          // Send a message back to the owner thread
>          // indicating success.
>          send(ownerTid, true);
>      });
> }
> ´´´
> 
> The idea is:
> the model is something that I can declare deliberately in the 
> application. And, I assumed that if it is (globally) shared, then so are 
> all compartments of it, even if they are not explicitly part of the model.
> 
> Some problems arose:
> 1. Obviously, this is not the case, as the output is different, 
> depending on the thread I start the model function.

Yes, unless you declare the model to be shared, there is a copy made for 
each thread, independently managed.

> 2. If I declare the model object inside the main, the compiler aborts 
> with the message "Aliases to mutable thread-local data not allowed."

Right, because you are not allowed to pass unshared data between threads.

> 3. If I mark the S instance as shared, it works. But I didn't intend to 
> do this... Is this really how it meant to be?

Let's go over how the data is actually laid out:

Model has NO data in it, so it doesn't really matter if it's shared or not.

S has a single array of element type D's. There is no static data in S, 
so it has no static state (only instance state).

D has a single size_t, which is thread-local, but has a static instance 
of S in the TYPE. There is not a copy of an S for each D, just a single 
copy for each THREAD. If you make this shared, it's a shared copy for 
all threads. This means the array inside the shared S will be shared 
between all threads too.

What happens when you spawn a new thread is that the thread-local copy 
of m is created with Model.init (but it has no data, so it's not 
important). A thread-local copy of D.s is created with S.init (so an 
empty array). The reason your assignment of length in main doesn't work 
is because the init value is used, not the current value from the main 
thread.

So yes, you need to make it shared to have the sub-thread see the 
changes, if that's what you are after.

> As I'm writing the model object as well as all of its compartments, I 
> can do almost everything... but what I to avoid is to declare the 
> instances of compartments inside the model:
> They are stored locally to their modules and the single elements of them 
> have to know about the compound objects, like with the D and S structs 
> shown.

A static member is stored per thread. If you want a global that's shared 
between all threads, you need to make it shared. But the result may not 
be what you are looking for, shared can cause difficulty if your code 
wasn't written to deal with it (and a lot of code isn't).

-Steve


More information about the Digitalmars-d-learn mailing list