Structured concurrency

95 views
Skip to first unread message

Matthew Browne

unread,
Dec 8, 2024, 6:37:44 AM12/8/24
to object-co...@googlegroups.com
I came across this article about concurrency, which makes some pretty bold claims:


I'm curious what people here think about this, especially as it relates to concurrency and messaging in an OO or DCI system.

The structured concurrency concept has caught on... In addition to the library in Python and other languages, both the Kotlin and Swift languages have built support for this into the language and standard libraries.

At first I thought it sounded fairly incompatible with the actor model, which is the main approach that I'm (somewhat) familiar with for concurrent objects, but then I saw that some folks have successfully implemented the actor model on top of this...and maybe structured concurrency also offers new alternatives to the actor model that still enable an object messaging paradigm...? ...if that should even be the goal if you want to build an understandable and easy-to-read concurrent system? Maybe it depends on the use case, e.g. the best approach for a system to run a simulation might be different than for concurrent operations in a business system?

Obviously I'm still very much in the early discovery phase with this :-)

James O Coplien

unread,
Dec 8, 2024, 8:30:46 AM12/8/24
to object-co...@googlegroups.com


On 8 Dec 2024, at 12.37, Matthew Browne <mbro...@gmail.com> wrote:

I came across this article about concurrency, which makes some pretty bold claims:


I find it confusing. Most inexpert folks (as in this article) do not differentiate between concurrency and parallelism. What they describe in the opening paragraphs could be either. Most operating systems — certainly those with a single core — implement the semantics as parallelism, because concurrency is impossible when you have only one program counter. Parallelism gives the illusion of concurrency — but it is only an illusion.

Much of the time it doesn’t matter, from the perspective of computational logic, to differentiate between them, since both approaches are equally fickle as regards what you can expect to happen in what order. Semaphores are the usual solution to reducing the uncertainty in both cases.

If you care about performance then the difference between concurrency and parallelism is important. Parallelism is unlikely to give you performance gains except maybe to reduce the overhead from spin locks. Concurrency in a fork/join setup can give you some gains.

I honestly don’t know of some OSs running on multicore CPU clusters actually implement the classic APIs for parallelism (as they list in the article) using concurrency. If so, then these APIs would start to violate any assumptions the caller makes about parallelism (e.g., that the total wall clock time for a given computation is constant, ignoring CPU sharing with other users and other activities on the computer.)

James O Coplien

unread,
Dec 8, 2024, 9:00:26 AM12/8/24
to object-co...@googlegroups.com
Oh, and the article is missing something else: exceptions. Exceptions are the modern-day goto.

I actually was on the committee for a Ph.D thesis that explored the interactions between parallelism ad exceptions. Don’t ask. I hated it.

Matthew Browne

unread,
Dec 8, 2024, 12:52:13 PM12/8/24
to object-co...@googlegroups.com

That's interesting about exceptions...I know they're controversial, but personally I never found them that problematic or any worse than returning error objects...but most of my experience with them in asynchronous code hasn't been particularly complex asynchronous code. Perhaps I'm missing something.

Re: concurrency vs. parallelism, I don't see any reason why "structured concurrency" couldn't be used for parallelism in addition to concurrency. Couldn't it be as simple (not that this is necessarily super simple to implement) as just having one thread pool for each CPU core, and when an async task needs to be run, assign it to whichever thread pool is most available?

There is an implementation of parallelism for Trio in Python:

https://trio-parallel.readthedocs.io/en/latest/

...and it looks like the same thing would be possible in Kotlin and other languages.


--
You received this message because you are subscribed to the Google Groups "object-composition" group.
To unsubscribe from this group and stop receiving emails from it, send an email to object-composit...@googlegroups.com.
To view this discussion visit https://groups.google.com/d/msgid/object-composition/43DCBD74-9C6F-42B8-81C0-880B09DC3D05%40gmail.com.

Egon Elbre

unread,
Dec 9, 2024, 6:23:04 AM12/9/24
to object-composition
On Sunday, 8 December 2024 at 15:30:46 UTC+2 Cope wrote:

I find it confusing. Most inexpert folks (as in this article) do not differentiate between concurrency and parallelism. What they describe in the opening paragraphs could be either. Most operating systems — certainly those with a single core — implement the semantics as parallelism, because concurrency is impossible when you have only one program counter. Parallelism gives the illusion of concurrency — but it is only an illusion.

I believe the article does distinguish them, but the definitions are reversed.

For Go community they usually refer to https://www.youtube.com/watch?v=oV9rvDllKEg as the definition source -- i.e. parallelism two things actually running together and concurrency as the illusion. That kind of definition is also used elsewhere e.g. https://docs.oracle.com/cd/E19455-01/806-5257/6je9h032b/index.html and https://ghcmutterings.wordpress.com/2009/10/06/parallelism-concurrency/ and https://en.wikipedia.org/wiki/Concurrent_computing.

When I did read older papers on mentioning concurrency and parallelism they did seem to be used interchangeably, so yeah... not sure whether there's a good definitive source for the definitions.

Egon Elbre

unread,
Dec 9, 2024, 6:32:31 AM12/9/24
to object-composition
I think the issue stems somewhat from mixing message passing and locking based implementations.

If you stick to a purely message passing system, then "goroutine" based approach is not a huge problem. However, if you start sharing memory between goroutines, then the ownership of resources gets quickly complicated and the interaction somewhat complicated. And Go does tend to use the mixing approach.

e.g. you have a database and a spawn a goroutine that interacts with it -- if you don't have a synchronization for shutting down the goroutine before the database, you may end up with a goroutine that uses a closed database connection.

So roughly speaking, goroutines need to be handled in a similar way as file handles; it needs to be clear who owns them and who needs to wait for them to be completed -- otherwise bugs are easy to slip in. (many mistakes in https://songlh.github.io/paper/go-study.pdf would be prevented)

And yeah, nurseries are one way to fix that. You can see in Go codebases that things like errgroup.Group are quite widely used for a similar effect.

+ Egon

Matthew Browne

unread,
Dec 9, 2024, 8:42:45 AM12/9/24
to object-co...@googlegroups.com

Hi Egon, thanks for pointing out the discrepancy in definitions. In response to Cope's message, I had done a bit more reading about this and the articles I was reading used "parallelism" to mean truly simultaneously executing on multiple cores, matching the definition from the Go community you mentioned. And without realizing it, that's the definition I was using in my reply to Cope, which was probably confusing.


--
You received this message because you are subscribed to the Google Groups "object-composition" group.
To unsubscribe from this group and stop receiving emails from it, send an email to object-composit...@googlegroups.com.

Matthew Browne

unread,
Dec 10, 2024, 7:28:55 AM12/10/24
to object-co...@googlegroups.com

@Egon et al., so would you say that you disagree with the author's claim that "go" statements (or async/await as the syntax is in many languages) is so harmful to comprehension of asynchronous code that ideally languages shouldn't directly expose those primitives at all? I was struck by the author's claim that the mere presence of a "go" or "await" statement makes the entire program harder to analyze than necessary, and should be avoided in the same way that modern languages don't allow any form of unrestricted "goto" statements.

You seem to be saying that if you are disciplined about sticking to pure message passing (I assume you mean both OO and non-OO messaging), that can work just as well as nurseries or the actor model or other things that impose more rules and structure. But is the risk of misuse worth it given that there are alternatives? I don't mean that as a leading question; I'm not sure I agree with the author either.

It's unclear to me whether the author would consider the actor model (without nurseries) to be too unstructured as well. He might be on to something with the risks of go/async/await keywords even existing in a language, but I wonder if it's one of those things where it should still be available for advanced uses or developers building concurrency libraries, rather than simply being eliminated like unrestricted "goto", which i don't think anyone misses.

Egon Elbre

unread,
Dec 10, 2024, 7:54:51 AM12/10/24
to object-composition
I wouldn't say the languages shouldn't expose it at all, but more that the default tools that you reach for should be something more constrained. Sometimes you do need more flexibility while designing concurrent data-structures, but you also are more likely to make mistakes that way. Similarly, you touch assembly only when you really need to and sometimes you really need to.

Yes, the mere presence of "go" and "await" makes a magnitude of difference in how difficult is to verify that the code is correct. This happens when there's any asynchrony at all -- e.g. user interface requests some data over network (via callbacks), while the interface is still interactive.

With regards to pure message passing I mean asynchronous message sending and no shared state. That seems to lead better defined failures when something is not responding or is not available. i.e. if you are sending a message and are required to handle the timeout or failure, then you are handling the failures better. Of course there are other problems that this approach might introduce. No silver bullets.

I would say that the nurseries approach does feel a bit nicer in practice, because using actor based approaches requires designing some sort of protocol to communicate between the actors. But then again, I don't have any extensive experience with the actor model to say for certain. AFAIR Erlang also has libraries that help you manage the lifecycles of actors -- e.g. supervisor.

Overall, my rule of thumb, if I see a naked `go` keyword in domain part of the codebase, then there's usually a bug somewhere lurking around. And the code will be better by replacing it with some sort of concurrency primitive (where one of the options could be something like a nursery).

Matthew Browne

unread,
Dec 10, 2024, 8:05:38 AM12/10/24
to object-co...@googlegroups.com
On 12/10/24 7:54 AM, Egon Elbre wrote:
> And the code will be better by replacing it with some sort of
> concurrency primitive (where one of the options could be something
> like a nursery).
Now I'm curious, what would you say is the most common "concurrency
primitive" in Go aside from the one you mentioned that's similar to a
nursery?

Egon Elbre

unread,
Dec 10, 2024, 8:30:00 AM12/10/24
to object-composition
I haven't done research on which is the most common, but the top 4 probably are:

* ad hoc `go` without waiting for the completion -- usually a bug, but relatively common
* ad hoc `go` with some channel that's used to signal and wait for completion
* sync.WaitGroup + `go`, behaves like a nursery, but with more code
* errgroup.Group, which is pretty much a nursery

However, a lot of server code doesn't use them directly, but the concurrency is built into
the http.Server... and rest of the code protects shared things with mutexes.

Matthew Browne

unread,
Dec 26, 2024, 6:05:36 AM12/26/24
to Egon Elbre, object-composition
Hi all,
I came across a programming language called E, which seems relevant to this discussion (and also recent discussions about object messaging):

http://www.erights.org/

The language was originally written as part of a PhD dissertation by its creator, Mark Miller:
http://www.erights.org/talks/thesis/index.html

For now I'm just curious: is anyone in this group familiar with it, or have you tried it? And if so, what did you think?

Cheers,
Matt

Ezequiel Birman

unread,
Dec 27, 2024, 7:18:12 PM12/27/24
to object-co...@googlegroups.com, Egon Elbre
For now I'm just curious: is anyone in this group familiar with it, or have you tried it? And if so, what did you think?
Hi Matthew and everyone.

Many ideas that originated in E (and maybe even in the Xanadu project?) found a place in various other languages: Java and Lisp are mentioned in the erights page, and more recently JavaScript (Caja, SES), Scheme, Pony and Newspeak; but also in lower-level systems, operating systems including a formally-verified OS kernel, and even hardware. There is a complete list at https://github.com/dckc/awesome-ocap.

Of the languages and projects mentioned earlier, my sympathies lie with the Guile Scheme based Goblins and OcapN, the OO language Pony and especially Newspeak.

Mark Miller's thesis is a very interesting read, and every chapter ends with a “related work” section. The object-capability model seems to compose nicely with distributed computing, but Miller's thesis deals explicitly with concurrency too.

The other relevant work in my opinion, is David Reed's Naming and Synchronisation in a Decentralised Computer System. I still haven't finished reading it but so far is great. I'm not aware of realisations of his ideas other than the projects he was involved with, e.g. NAMOS, and Croquet.

-- 
Eze

Matthew Browne

unread,
Dec 27, 2024, 8:06:54 PM12/27/24
to object-composition
Hi Eze,
Thanks for sharing this! It's very interesting stuff... Obviously having security built into the language from the ground up is a great thing, and prior to discovering E I had already heard good things about the object-capability model.

But what interested me most wasn't the security features, but the concurrency features, which seem to be inspired in some ways by the actor model but also include some other improvements compared with roll-your-own concurrency (and maybe even compared with successful existing implementations of Actors), while still providing a sequential subset of the language to simplify things when you don't need your messages to be asynchronous.

I feel like the combination of DCI and something like this could be fruitful. I don't want to waste the group's time on conjecture while I'm just beginning to learn about this, but I do appreciate you sharing your knowledge about it and maybe this well get others' creative juices flowing too :-)
--
You received this message because you are subscribed to the Google Groups "object-composition" group.
To unsubscribe from this group and stop receiving emails from it, send an email to object-composit...@googlegroups.com.
Reply all
Reply to author
Forward
0 new messages