Mallocator and 'shared'

Moritz Maxeiner via Digitalmars-d-learn digitalmars-d-learn at puremagic.com
Sun Feb 12 16:27:06 PST 2017


On Sunday, 12 February 2017 at 20:08:05 UTC, bitwise wrote:
> It seems that methods qualified with 'shared' may be what 
> you're suggesting matches up with the 'bridge' I'm trying to 
> describe, but again, using the word 'shared' to mean both 
> 'thread safe' and 'not thread safe' doesn't make sense. [...]

For essentially all that follows, refer to [1][2]
`shared` (as well as `__gshared`) on a variable has the semantics 
of multiple threads sharing one single memory location for that 
variable (i.e. it will not be put into thread local storage). 
Accessing such data directly is inherently not thread safe. 
Period. You will need some form of synchronization (see [3]) to 
access such data in a thread safe manner.
Now, `shared` is supposed to additionally provide memory 
barriers, so that reads/writes on such variables are guaranteed 
not to be reordered in a way that breaks your algorithm; 
remember, the compiler (and also later the cpu when it reorders 
the opcode) is allowed to reorder reads/writes to a memory 
location to be more efficient, as long as doing so won't change 
the logic as the compiler/or cpu sees it. Example:

__gshared int a = 0;

// thread 1:
a = 1;
int b = a;
writeln(b);

// thread 2:
a += 1;

In the above, you may expect `b` to be either 1, or 2, depending 
on how the cpu interleaves the memory access, but it can, in 
fact, also be 0, since neither the compiler, nor the cpu can 
detect any reason as to why `a = 1` should need to come before 
`int b = a` and may thus reorder the write and the read. Memory 
barriers prevent such reordering in the cpu and if we had made 
`a` `shared` those barriers would've been supposed to be emitted 
by the compiler (in addition to not reordering them itself). 
Unfortunately, that emission is not implemented.

 From [4]:
> Non-static member functions can have, in addition to the usual 
> FunctionAttributes, the attributes const, immutable, shared, or 
> inout. These attributes apply to the hidden this parameter.

Thus a member function being `shared` means nothing more than 
that the instance it is called on must also be `shared`, i.e.

class Foo
{
     shared void bar();
}

Foo foo;
foo.bar();    // this is illegal, `foo` (the hidden `this` of 
`bar`) is not shared

shared Foo foobar;
foobar.bar(); // this is legal, since `foobar` is shared

That's it, there are no two meanings of `shared` depending on 
some context, there is only one: The data in question, which is 
either the attributed variable, or the object/instance of the 
member function being attributed, is shared between threads and 
accessing it directly is not thread safe.

On Sunday, 12 February 2017 at 20:08:05 UTC, bitwise wrote:
> I thought 'shared' was a finished feature, but it's starting to 
> seem like it's a WIP.

I prefer the term "unfinished" since "WIP" implies that it's 
being worked on. AFAIK there's no one currently working on 
implementing what's missing in the compiler frontend with regards 
to the spec.

On Sunday, 12 February 2017 at 20:08:05 UTC, bitwise wrote:
> This kind of feature seems like it has great potential, but is 
> mostly useless in it's current state.

I share that opinion and generally either use `__gshared` if I 
absolutely have to share data via shared memory and design 
carefully to avoid all the potential issues, or - which I much 
prefer - use message passing: `std.concurrency` is your friend.

On Sunday, 12 February 2017 at 20:08:05 UTC, bitwise wrote:
> After more testing with shared, it seems that 'shared' data is 
> mutable from many contexts, from which it would be unsafe to 
> mutate it without locking first, which basically removes any 
> gauruntee that would make 'shared' useful.

As pointed out above that's to be expected, since that's its job. 
Regarding guarantees: Since D treats data as thread local by 
default, you need either `shared` or `__gshared` to have mutable 
shared (intra-process) memory (ignoring OS facilities for 
inter-process shared memory). The main advantage is not in data 
being `shared`/`__gshared`, but in the guarantees that all the 
other (unattributed, thread local) data gets: Each thread has its 
own copies and any optimisations applied to code that accesses 
them need not consider multiple threads (I'd wager this is a 
significant boon towards D's fast compile times).
If you only talk about useful benefits of `shared` over 
`__gshared`, if the spec were properly implemented, the useful 
properties would include you not needing to worry about memory 
barriers. Other useful guaranties are the more rigorous type 
checks, when compared to `__gshared`, which are supposed to 
prevent you from committing some of the common mistakes occurring 
in non-sequential programming (see, e.g. the code example with 
`class Foo` above).

On Sunday, 12 February 2017 at 20:08:05 UTC, bitwise wrote:
> Again, tell me if I'm wrong here, but there seems to be a lot 
> of holes in 'shared'.

There are holes in the implementation of `shared`; it's spec, 
however, is complete and coherent.

[1] https://dlang.org/faq.html#shared_guarantees
[2] https://dlang.org/spec/attribute.html#shared
[3] 
https://tour.dlang.org/tour/en/multithreading/synchronization-sharing
[4] https://dlang.org/spec/class.html


More information about the Digitalmars-d-learn mailing list