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

Reply via email to