4 ms·
Since I now actually got around to reading the page... You end the section on Go with "Go adopted the concurrency concept from Newsqueak and added new features
by dfawcus 3y ago
Since I now actually got around to reading the page...
You end the section on Go with "Go adopted the concurrency concept from Newsqueak and added new features, such as buffered channels.". However Alef itself added those buffered channels. Which were also available in libthread and libtask, those being what I used before Go came along.
One can find copies to the Alef User[1] and Reference[3] guides from the wikipedia page, and Russ Cox's site also has links to PDFs of them [2,4]. The user guide gives the Alef syntax for such buffered channels as:
chan(int)[100] kbd;
Search for the section titled 'Asynchronous Channels' in the guide.
As to buffered channels in general, Hoare's 1985 book mentions (7.3.4 Unbuffered communication) that one could equally use buffered comms as the primitive. One can check the online version at http://www.usingcsp.com http://www.usingcsp.com, so Alef having buffered channels in 1995 doesn't seem like too much of a stretch, especially given the existence of the actor model.
You may also wish to reference this [5] brief paper by Russ Cox, it happens to mention a couple more languages (Pan and Promela). Also see [6].
The Alef (and libthread) approaches are interesting that they contain a mixture of threads and coroutines; in the former as a language facility in the latter as a library for C.
Now in terms of language syntax forms, having used both libtask in C, and Go, I do appreciate the syntax elements. Having typed (and type enforced) channels in Go makes it easier to reason about the code, likewise the ability (in Go) to split a channel in to a send-only and a receive-only version helps. With typed channels, one can also retrieve a degree of flexibility if the type in question is an interface, then one type switches on receipt of a message.
The real gain though is in select syntax, where having to manually manipulate the array of Alt elements in the libtask/libthread implementation was a distraction. Oh - and obviously Go having a GC makes for an easier time, though it is still possible to "leak" channels and/or goroutines if one freely creates/spawns and they're not carefully closed down. Hence my earlier reference to dynamic graphs.
I suspect a language with sufficient compile time generics could convert a form of libtask chanalt() call in to something which looks a lot like a Go select statement, and hence avoid the distractions. I was pondering having a go at doing that for a Zig or D library.
As to your other question, yes I'd suggest non blocking forms are necessary, I've occasionally had to use them. See the libtask/libthread 'nb' variants. They're actually implemented with a special value in the Alt arrays. I'd imagine a generic version of 'select' taking some form of lambda's could be wrap those without being too ugly.
[1] https://doc.cat-v.org/plan_9/2nd_edition/papers/alef/ug
[2] https://swtch.com/~rsc/thread/ug.pdf
[3] https://doc.cat-v.org/plan_9/2nd_edition/papers/alef/ref
[4] https://swtch.com/~rsc/thread/alef.pdf
[5] https://swtch.com/~rsc/thread/
[6] https://swtch.com/~rsc/talks/group02-thread.pdf
- Rochus 3y agoThanks for the feedback. I didn't read any papers about Alef indeed, because it seemed to be only a brief intermezzo, but I read about Limbo, which only added buffered channels as soon as Go came up. I will take a look at your references, thanks. I should also take another look at Hoare's CSP book (it's 30 years ago since I've read it). I'm aware that there were other CSP languages, and I'm indeed currently reading Ben-Ari's Spin book which uses Promela, but the goal was not to discuss all languages, but the ones relevant for the Pascal and Go heritage; it's by no means a complete historic treatment. But since Go has buffered channels (even if Joyce and SuperPascal didn't), this is a good justification to also have them in Oberon+. Concerning select, there is now a proposal in the updated paper which follows the discussion here (see https://news.ycombinator.com/item?id=38772561 https://news.ycombinator.com/item?id=38772561). For the time being I added both, a SELECT function and an extended WITH statement to the proposal, both with non-blocking versions. It's still to be discussed whether both are needed, or only one, or whatever other good idea is around.
- KMag 3y agoInstead of blocking/non-blocking, blocking with a timeout is probably a better primitive. A simple convenience would be to provide FOREVER (or maybe ONE_MILLENNIUM) and ZERO_TIME constants for the maximum (or absurdly large) and zero time duration values. Blocking and non-blocking operations can be implemented as syntactic sugar on top of a timeout (blocking forever and blocking zero <clock_granularity>ies), if you're willing to pay the increased syntactic complexity. In any case, when writing APIs, particularly for back-end code, I prefer to remind the caller to think about "what's the longest I'm willing to wait here?" and force them to be explicit if they're really willing to wait years for a result. Even/especially for async APIs, I prefer to make all of the timeouts explicit. More than one developer has asked me for help with running out of memory that happened to be from queued async thunks due to absurdly slow response from some other back-end service. Depending upon the OS-exposed primitives, perhaps the waiting forever and waiting zero ns might be optimized to use different syscalls as an implementation detail.
- Rochus 3y agoDo you happen to know whether there is a (current) implementation of libthread for Linux?