Mallocator and 'shared'

Moritz Maxeiner via Digitalmars-d-learn digitalmars-d-learn at puremagic.com
Mon Feb 13 09:44:10 PST 2017


On Monday, 13 February 2017 at 14:20:05 UTC, Kagamin wrote:
> Thread unsafe methods shouldn't be marked shared, it doesn't 
> make sense. If you don't want to provide thread-safe interface, 
> don't mark methods as shared, so they will not be callable on a 
> shared instance and thus the user will be unable to use the 
> shared object instance and hence will know the object is thread 
> unsafe and needs manual synchronization.

To be clear: While I might, in general, agree that using shared 
methods only for thread safe methods seems to be a sensible 
restriction, neither language nor compiler require it to be so; 
and absence of evidence of a useful application is not evidence 
of absence.

On Monday, 13 February 2017 at 14:20:05 UTC, Kagamin wrote:
> ps Memory barriers are a bad idea because they don't defend 
> from a race condition, but they look like they do :)

There are two very common pitfalls in non-sequential programming 
with regards to reads/writes to memory shared between threads:
Issue 1: Sequencing/Interleaving of several threads into the 
logical memory access order
Issue 2: Reordering of code within one thread

Code that changes semantics because of issue 1 has race 
conditions; fixing it requires synchronization primitives, such 
as locking opcode, transactional memory, etc.

Code that changes semantics because of issue 2 may or may not 
have race conditions, but it definitely requires memory barriers.

Claiming that memory barriers are a bad idea because they don't 
defend against race conditions, but look like they do (when 
that's what synchronization is for) is similar enough to saying 
airbags in cars are a bad idea because they don't keep your body 
in place, but look like they do (when that's what seat belts are 
for).
My point here being that I don't understand what made you state 
that memory barriers look like they deal with race conditions, as 
they have nothing to do with that.

To be clear: Synchronization (the fix for race conditions) does 
not help you to deal with issue 2. If my last example had instead 
been

---
__gshared int f = 0, x = 0;
Object monitor;

// thread 1
synchronized (monitor) while (f == 0);
// Memory barrier required here
synchronized (monitor) writeln(x)

// thread 2
synchronized (monitor) x = 42;
// Memory barrier required here
synchronized (monitor) f = 1;
---

you'd still need those memory barriers. Also note that the 
synchronization in the above is not needed in terms of semantics. 
The code has no race conditions, all permutations of the 
(interleaved) memory access order yield the same output from 
thread 1. Also, since synchronization primitives and memory 
barriers have different runtime costs, depending on your hardware 
support and how they are translated to that support from D, 
there's no "one size fits all" solution on the low level we're on 
here.

My opinion on the matter of `shared` emitting memory barriers is 
that either the spec and documentation[1] should be updated to 
reflect that sequential consistency is a non-goal of `shared` 
(and if that is decided this should be accompanied by an example 
of how to add memory barriers yourself), or it should be 
implemented. Though leaving it in the current "not implemented, 
no comment / plan on whether/when it will be implemented" state 
seems to have little practical consequence - since no one seems 
to actually work on this level in D - and I can thus understand 
why dealing with that is just not a priority.

On Monday, 13 February 2017 at 14:20:05 UTC, Kagamin wrote:
> use std.concurrency for a simple and safe concurrency, that's 
> what it's made for.

I agree, message passing is considerably less tricky and you're 
unlikely to shoot yourself in the foot. Nonetheless, there are 
valid use cases where the overhead of MP may not be acceptable.

[1] https://dlang.org/faq.html#shared_guarantees


More information about the Digitalmars-d-learn mailing list