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

Reply via email to