
15 Years of Concurrency - runesoerensen
http://joeduffyblog.com/2016/11/30/15-years-of-concurrency/
======
krat0sprakhar
To note:

> Out of the bunch, Rust has impressed me the most. They have delivered on
> much of what we set out to deliver with Midori, but actually shipped it
> (whereas we did not). My hat goes off to that team, seriously, because I
> know first hand what hard, hard, hard work this level of type system hacking
> is.

~~~
jacquesgt
But see also:

> In particular, I’m still conflicted about whether all those type system
> extensions were warranted. Certainly immutability helped with things far
> beyond safe concurrency. And so did the side-effect annotations, as they
> commonly helped to root out bugs caused by unintended side-effects. The
> future for our industry is a massively distributed one, however, where you
> want simple individual components composed into a larger fabric. In this
> world, individual nodes are less “precious”, and arguably the correctness of
> the overall orchestration will become far more important. I do think this
> points to a more Go-like approach, with a focus on the RPC mechanisms
> connecting disparate pieces.

~~~
kibwen
I don't think this is incompatible with Rust's goal, which is to bring safety
to the sort of low-level domain where C is currently dominant and systems
can't afford to be structured as isolated nodes communicating only via RPC. In
contrast, Midori was aimed at a higher-level general-purpose domain, like Go
(and Erlang, too, which is even better than Go at the "cluster of nodes"
approach, yet doesn't have a type system at all).

~~~
k__
Could it be, that many desicions for Rust have been made to make it more
appealing to C devs?

Like, opting out of purity annotations or forced immutability?

I'm not saying these desicions are good or bad, I don't know much about system
programming. But I often ask myself what the main motivators for a language
are.

~~~
tupshin
FWIW,after spending a couple of years with rust as my primary programming
language, I'm convinced that they made the right decision wrt mutability
(immutable by default, but mutability availale when needed), as rust's
lifetime system eliminates nearly all the remaining pain points around
mutability (data races).

On the other hand, I think it is very important that some notion of purity be
incorporated, even if only as an opt-in.

~~~
k__
> spending a couple of years with rust as my primary programming language

Oh, how did you achieve that? :)

~~~
Groxx
Probably not with too much difficulty. 1.0's alpha was nearly 2 years ago. The
github repo goes back to mid 2010: [https://github.com/rust-
lang/rust/graphs/contributors](https://github.com/rust-
lang/rust/graphs/contributors)

~~~
steveklabnik
See also [https://github.com/graydon/rust-
prehistory](https://github.com/graydon/rust-prehistory)

~~~
k__
I didn't want to imply that Rust is to young for people to have been spending
years using it.

I wanted to know how you could get a Rust-job (using it as primary programming
language) today :)

~~~
steveklabnik
Ah. So, most organizations using Rust in production are moving existing
employees over or hiring more generally.

I'd hit up these people: [https://www.rust-lang.org/en-
US/friends.html](https://www.rust-lang.org/en-US/friends.html)

------
sriram_malhar
One of the things that this blog ought to have clearly mentioned is that all
this business about static analysis, ownership types, read-write annotations,
purity, immutability and all that is due to a simple conflation of two axes
that ought to be orthogonal: mutation and aliasing.

This is what Rust gets right: you can have an object be mutable (in which case
you have only one pointer to it), or you can multiply alias pointers, all of
which can only read the object. The type system enforces a static RW locking
scheme in a manner of speaking; it ensures that only one of those capabilities
is available at any one point in time.

In other words, Rust ensures that mutation by itself is not a problem, that
passing mutable structures between threads is statically checked for safety.
You don't _have_ to make an object immutable in order to share safely by
reference.

\---------------

It'd have been nice to hear about the "P" language, also from MSR
([https://www.microsoft.com/en-
us/research/publication/p-safe-...](https://www.microsoft.com/en-
us/research/publication/p-safe-asynchronous-event-driven-programming/)). No
pointers, just value types, communicating state machines, produces model-
checkable code. What's not to like?

~~~
pjmlp
> It'd have been nice to hear about the "P" language

And already used in production, even if in a small use case.

------
scribu
For anyone else wondering what "Midori" is, it looks like it was an internal
Microsoft research project:

[http://joeduffyblog.com/2015/11/03/blogging-about-
midori/](http://joeduffyblog.com/2015/11/03/blogging-about-midori/)

~~~
honkhonkpants
I've been reading these but it's become apparent that the author lives in a
universe parallel to mine. I remember reading that in most languages "A null
dereference or out-of-bounds array access is treated the same way as a network
connectivity problem or parsing error" and scratching my head, because while I
know some languages are like that (Java) there are others were a null
dereference and the failure to establish a network connection have radically
different effects (C++). At another point discussing error handling he says
"This leads us to a beautiful sweet spot. It does suffer from the performance
problems I mentioned earlier, but does very well on all other dimensions." so
the author's sweet spot is a spot with poor performance, which also put me off
because his articles all talk about performance but never concretely (in terms
of nanoseconds).

Anyway it's an interesting, if long, series but most of the interest for me
has been in reading things from a very different perspective from the one I
hear at work every day.

~~~
Locke1689
I'm not really clear what this comment implies?

What system do you work with that doesn't have null pointers but also cares
about eliminating as many fine-grained locks as possible?

~~~
asveikau
I believe the point you are missing: The article compares a null pointer
deference and network failure.

One of those is routine.

The other is a serious logic error that strictly speaking a correct program
cannot be said to do.

~~~
Locke1689
After re-reading I think I see that point, but I still don't understand the
overall comment...

Is the author saying his standard language is C++? Everything I know about
Midori said that it was competitive with C++ on performance in almost every
way. Regarding error handling in particular, Midori used a combination of
returning values and safe exceptions, both of which seem similar to C++.

It also explicitly _doesn 't_ have the aforementioned conflation of bugs and
runtime errors.

------
amelius
15 Years of Concurrency ... and now we're all using JavaScript, which has a
single-thread model, and doesn't allow cross-thread structural sharing of data
structures (without going through a number of hoops).

~~~
pkolaczk
Which ironically, is a model with probably best performance and scaling
characteristics. If you want high performance, you should avoid sharing
mutable stuff as much as possible.

~~~
danarmak
The bigger problem is that Javascript disallows (or at least, browsers don't
currently provide) efficient sharing of _immutable_ values between
threads/workers.

Because JS has no notion of immutability, sharing is done via copying - but
what's worse, copying via full-blown serialization, not just in-memory
blitting or COW. At least, that was the state of things when I last checked a
year ago.

~~~
maxpolun
Modern JS does allow you to send large buffers from one worker to another with
just a pointer copy (because it stops being accessible from the sender).

~~~
danarmak
Yes, but even with that restriction, it doesn't let you transfer ownership of
structured messages, only buffers and images; so a serialization roundtrip is
needed.

------
signa11
from the article:

So, we made some:

Isolation is paramount, and we will embrace it wherever possible.

Message passing will connect many such isolated parties through strongly typed
RPC interfaces.

Namely, inside of a process, there exists a single message loop, and, by
default, no extra parallelism.

A “promises-like” programming model will be first class so that:

Synchronous blocking is disallowed. All asynchronous activity in the system is
explicit. Sophsiticated (sic) coordination patterns are possible without
resorting to locks and events.

with the exception of trying concurrency primitives on heavy-weight os
provided processes, this looks like recreation/rediscovering of erlang
primitives once again :)

would one be chastisized for wagering that erlang is the lisp of concurrency
world ?

edit-001: slight clarification (hopefully)

~~~
brightball
Every time I read about concurrency in other languages I see them trying to
become Erlang but without the core design decisions that make Erlang works.

Always reminds me of this quote:

"Any sufficiently complicated concurrent program in another language contains
an ad hoc informally-specified bug-ridden slow implementation of half of
Erlang." \- Robert Vriding

~~~
pjmlp
The problem is that Erlang is only good a network communication and parallel
programming, which is of course to be expected, given the language design
goals.

The problem is when one tries to make a general purpose solution out of that
design.

For example, how would Erlang look like in GPGPU programming, video/audio
codecs, graphics programming, GUI,...

~~~
signa11
> The problem is when one tries to make a general purpose solution out of that
> design.

yup. go(lang) / rust seems to be exploring some ideas with an almost similar
approach, but not _solely_ focussed on n/w programming. are there other
languages out there as well ?

and a slightly open ended question as well: in your opinion, do you think a
language needs to implement a complete runtime (similar to erlang) with it's
own 'process' abstraction, scheduler, i/o etc. etc. to provide concurrency
support ?

~~~
pjmlp
Well, C++, D, .NET, Java, Swift, Objective-C, Rust... are all exploring the
runtime approach for concurrency support.

The main difference across those languages and Erlang/Go is how they expose
the runtime to the developers.

It isn't a black box, rather they exposes a default set of behaviors and as
set of APIs that allow to fine tune its behaviors.

~~~
signa11
> Well, C++, D, .NET, Java, Swift, Objective-C, Rust... are all exploring the
> runtime approach for concurrency support.

wasn't it hans-boehm, who famously posited (?) that concurrency needs to be
part of the language, rather than bolted on after the fact ? do you think that
makes all these other languages kind of poorer w.r.t concurrency ?

edit-001: specifically, this is the paper that i am talking about:
[http://www.hpl.hp.com/techreports/2004/HPL-2004-209.pdf](http://www.hpl.hp.com/techreports/2004/HPL-2004-209.pdf)

~~~
pjmlp
But concurrency is part of the language.

What you are missing is that those languages are quite expressive, thanks to
generics, operator overloading, trailing lambdas, and APIs into runtime the
are available as tools for the programmer.

So you get, for example, async/await built into the language, with a default
behavior. But at the same time, the necessary runtime hooks that allow any
library writer to fine tune and built other concurrency models on top of it.

For the user of those concurrency models, it is indistinguishable where they
are coming from.

~~~
brightball
The disnguishing factor is prescheduling vs cooperative scheduling. In all
those other languages each process has to hand control back to the scheduler -
meaning runaway code can negatively impact everything, pause the entire stack
to garbage collect and can't transparently distribute itself across machines
because of shared memory (you can't transparently pass a pointer).

Those are design decisions you can't bolt on later. Isolation, prescheduling
and message passing without shared memory get you access to all of that.

~~~
pjmlp
Yes you can, has been done in Java, .NET, Active Oberon and Modula-3, just
from the top of my head.

It is only a matter of making use of the said compiler hooks and underlying
machinery.

~~~
brightball
I'm not familiar with Active Oberon or Modula 3, but Java and .NET absolutely
do not. You can bolt on message passing but it's a constraint of the VM to
properly handle prescheduling and heap per process isolation.

Mimicking the patterns doesn't replace the VM.

~~~
pjmlp
Java and .NET are not only the Java and C# programming languages.

There are for example, distributed agent programming languages built on top of
them, including network aware distributed GCs.

------
relics443
This looks like it's gonna be a long, interesting read. I better save up my
bathroom time...

~~~
okreallywtf
Not sure why you got down voted, I feel the same way. I read what I was sure
was 3/4 of it only to realize it was about 1/8 but I'm definitely going to
save it and finish.

I'm slightly (very?) jealous that I am not so passionate about any part of
software as the author. I am interested in lots of areas but no one piece of
the puzzle draws me and its bittersweet sometimes to read the thoughts of
someone who seems so passionate about their field.

~~~
garrettgrimsley
>Not sure why you got down voted

Because the comment adds nothing to the conversation.

~~~
module0000
It adds humor, and we like that sort of thing on HN

~~~
scott_s
Post that only try to be funny tend to be downvoted on HN. We try to avoid
reddit style threads where people just post one-liners.

------
michaelsbradley
This is a fantastic write-up.

It was back in 2011 that I first started wrapping my head around the
complexities of concurrent programming. Even in single-threaded environments,
such as a JavaScript processes, concurrency woes can escalate quickly in terms
of the non-determinism introduced by asynchronous operations (timers, I/O) and
their callbacks.

My trying to understand and deal with those complexities resulted in an
exploration of functional-reactive programming concepts, which in turn led me
to make a "deeper dive" into computer programming language design and various
computer science topics. Concurrency is still the CS topic that makes my ears
perk up the most.

------
romaniv
I've recently stumbled upon a very interesting paper about concurrency. Naming
and Synchronization in a Decentralized Computer System:

[http://publications.csail.mit.edu/lcs/pubs/pdf/MIT-LCS-
TR-20...](http://publications.csail.mit.edu/lcs/pubs/pdf/MIT-LCS-TR-205.pdf)

It's written in 1978, but if I interpret it correctly, it's still conceptually
ahead of everything mentioned in the article (the closest thing being STM).
Anyone else here seen it?

~~~
pjmlp
No, but thanks for bringing it up, it appears an interesting read.

------
victorcase
Hey another article at YC frontpage about saying "DO NOT MESS WITH STATE" to
scale;

"Shared-memory multithreading really isn’t the future, we thought, and notably
absent from all of my prior work was fixing this problem."

I'm thinking about..

Immutability is the way to scale complex systems?

Every system can be implemented as immutable by definition?

We need to look to build new data structures - like Tries - to be Immutable-
friendly?

When Immutability doesn't worth it?

~~~
colanderman
Computer memory is mutable. For multiple processors to make use of the same
memory requires contending for that memory _somehow_ (else the processors are
not communicating in any meaningful way).

You can mathematically try to hide that however you want, but somewhere deep
in your language runtime stack will be a shared-memory concurrent mutable
algorithm, be it a lock, queue, or otherwise.

The alternative is to ditch shared memory entirely and use e.g. hardware IPC
to manage contention. Tilera tried that, they had 64-core processors with a
hardware IPC mesh and incoherent cache. But last I checked they switched focus
to coherent shared-memory concurrency. Not sure what the driver for the change
was but it's telling.

EDIT: Not to mention that half the article focused on promise-style
concurrency, which has nothing to do with shared memory or even multicore.
Immutability does nothing to address that I/O exists and has latency, and that
people try to hide it in frankly bizarre ways. (Really… not pumping the event
loop to avoid deadlock? That's a design issue, you should be using queues
instead of mutexes and eliminate cycles if you find you have that problem.)

~~~
victorcase
I will search about Tilera :) Your answer open my mind cause I hadn't thought
about hardware. It got me thinking.. Are GPU architecture more friendly to be
immutable or It have the same "problem" of shared memory too? And.. by the way
thanks to sharing your knowledge :)

~~~
Athas
Functional array programming on GPUs works pretty well, and is an immutable
programming style. Your object granularity tends to be pretty big (entire
arrays), so the low-level shared memory concerns that inevitably arise at some
point in the software stack are easy to handle for the compiler and/or
runtime. Basically, you have much fewer synchronisation points, so you can
afford for them to be slow.

It's a more restricted programming style than common functional programming -
in particular, recursion and advanced control flow is out - but that's the
name of the game on GPUs, no matter what you do.

~~~
pjmlp
And there are at least Haskell and F# implementations for GPGPU, not sure
about other languages.

------
agentgt
Although distributed programming is different than concurrency (albeit one I
suppose could argue) I was hoping for some mentioning of Join Calculus.
Particularly Polyphonic C# [1] (given Duffy's MS background).

I have been meaning to re-investigate Join Calculus (the last time I did was
in OCaml a long time ago) and to why it never really took off.

Some programming patterns can be found here:
[https://en.wikipedia.org/wiki/Join-
pattern](https://en.wikipedia.org/wiki/Join-pattern)

[1]:
[https://en.wikipedia.org/wiki/Polyphonic_C_Sharp](https://en.wikipedia.org/wiki/Polyphonic_C_Sharp)

~~~
sriram_malhar
True. Both polyphonic C# and JoCaml were lovely projects.

------
nickpsecurity
Weird thing is that the inspirations didnt factor in only commercially-
depolyed schemes for safe concurrency: Ada's and Eiffel SCOOP. The SCOOP model
was even ported to Java at one point.

~~~
jwatte
Erlang CSP is also commercially deployed and safe.

------
agentgt
On Joe Duffy's profile there is what I thought to be a hexadecimal: 284d69f1cb

I tried converting it to ascii and decimal and nothing interesting came out.
Anyone know what that is? Maybe a key signature?

~~~
ludwigvan
Probably a part of the hash of the first Git commit of his new project.

