Hacker News new | ask | show | jobs
by knz42 1807 days ago
A lot of the complexity comes from the lack of expressivity in languages to relate variables (or data structure fields) semantically to each other. If there was a way to tell the compiler "these variables are always accessed in tandem", the compiler could be smart about ordering and memory fences.

The idea to extend programming languages and type systems in that direction is not new: folk who've been using distributed computing for computations have to think about this already, and could teach a few things to folk who use shared memory multi-processors.

Here's an idea for ISA primitives that could help a language group variables together: bind/propagate operators on (combinations of) address ranges. https://pure.uva.nl/ws/files/1813114/109501_19.pdf

2 comments

Even with that expressivity, someone who incorrectly relates or forgets to relate two variables could experience the same issues. It's still important to address what happens when the program has data races or when it is data-race-free but the memory model permits overreaching optimizations. The language and implementation should strive to make a program approximately correct.
That's Java's Object.lock() mechanism.

All variables inside of an object (aka: any class) are assumed to be related to each other. synchronized(foobar_object){ baz(); } ensures that all uses of foobar_object inside the synchronization{} area are sequential (and therefore correct).

--------

The issue is that some people (a minority) are interested in "beating locks" and making something even more efficient.

In Java, any object can be used to synchronize any data, e.g.

  synchronized(foobar_object){ foo(); }
  synchronized(foobar_object){ bar(); }
  synchronized(foobar_object){ baz(); }
Will have foo, bar, baz methods well behaved in any data that they share regardless of whether they are foobar methods or methods of any other class(es). It is exactly analogous to the S(a) -> S(a) synchronizing instruction from the article that establishes a happens-before partitioning each thread into before/after the S(a).

The only time synchronized(explicit_object) relates to anything else is when also using the keyword where `synchronized void foo()` is equivalent (with a minor performance difference) to `synchronized(this) { ... }` wrapping the entire body of the foo method.

Although in highly parallel code, the primitives from java.util.concurrent are to be preferred.

I highly advise reading "Java Concurrency in Practice".

Note that future Java primitive classes don't have monitors.

Seems like a vague way of saying that locks 'don't scale' or aren't composable, which is certainly the case but straying from the topic of memory models.