Swift Concurrency Roadmap

Right. I think the libdispatch has done a fair amount of harm to the platform because it made it feel like multithreading was easy, developers started using it without understanding what was going on under the hood and terrible performance ensued. This is the same thing but on steroids.

Having personally gone the way of the libdispatch since it was introduced in Snow Leopard, having written a lot of heavily async code, and eventually having understood the hard way that it could not work well without carefully thinking about threads, I am quite afraid of the long-running consequences of something like this.

My first advice to developers about multithreading today is: don't do it, unless you know you need to introduce concurrency and if you do, apply it with care. Most programs don't need a great deal of concurrency and most are not good candidate for it (because they lack long enough non-contended tasks to execute concurrently). This proposal will make everyone use concurrency and multitrhreading with no understanding of the implications.

8 Likes

I guess the advice is simple: Use as few actors as possible.

Also actors are great because they force a share nothing sort of attitude. Its great because its simple and clean without the need of complicated locks.

But, they are bad for some cases because they may copy heavily too much between threads. So they will behave poorly if you need to pass to much stuff between threads.

In those cases threads being able to share with fine-grained locks would be a better approach..

4 Likes

This is very exciting. Swift solving these problems will have a profound impact on me, my career and even the industry going forward. Thank you to all for the effort you’ve put into it.

What’s the story for back-deployment to earlier OS versions? Will we have to wait a handful of years after the dust has settled to use it (e.g.: because it’s dependent on some platform specific features)?

4 Likes

Posting on behalf of @saeta and @compnerd (and on my own behalf of course).

Some of the systems programming folks we've been talking with would dispute whether that is good. They point out that, aside from having lower absolute performance costs than queues, in a lock-based system there are proven tools for deadlock detection and debugging. [^1]

While we think an async/await system is probably essential for Swift, we are also concerned that this proposal may be ignoring the foundations that proven concurrent programming is built upon. By starting from the middle (e.g. without updating the memory and ownership models), choices incompatible with systems programming may be baked into the language.

Most of all—and this is difficult because we do appreciate the obvious effort that has gone into these proposals on behalf of the whole Swift community—a massive document set is being exposed for the first time, with very little prior opportunity to discuss the approach here in the forums. That makes it something of a fait accompli, which nobody can reasonably challenge at a fundamental level without implying a huge cost to the proposers.

[1] Incidentally we don’t understand the claim being made that deadlocks are eliminated, since as far as we know, two actors can easily wait on one another’s results.

26 Likes

Also, unfair locks perform much, much better under contention than fair locks or queues.

I guess that's a livelock and not a deadlock, as the APIs are async?

2 Likes

Worse, they don't prevent interleaving the code (AFAIU). So another "partial task" can be executed on actor1 while it's waiting for the result from actor2. So less deadlock, but could lead to unexpected result to untrained eyes (or even trained ones). We can go to Async Function thread if we want to discuss this. I think it's mentioned there.

My understanding is that livelocks involve unfairness where a lock is repeatedly denied rather than a deadlock where there is lack of progress. Is there a terminology difference here?

My experience as a programmer that was already here before the libdispatch was introduced is that developers will use way too many actors, just as they use way too many queues. Actors also seem to make it super easy to dispatch very small tasks, such as protecting shared state and properties. When this is out there, people will use it and it will be impossible to explain to them that they should really not use it that much.

3 Likes

As I see it, Actors are one of the main building blocks of safe concurrent code. Plus there's the distributed actors... So I think it's absolutely awesome to get this in as a first class citizen. Even if it currently can lead to less efficient use. Also, wouldn't it be possible for a future runtime to decide, possibly at runtime, that some actors belong together, and hence merge their dispatch queues (if they use dispatch queues)?

And async/await is really great for simplifying concurrent code in a big way.

I think these proposals are amazing, and I really look forward to using them :+1:

2 Likes

We're working closely with the libdispatch team on making sure the level of real concurrency is appropriate for the underlying system. It's always possible we'll still get it wrong of course, but don't assume that the current libdispatch and pthread primitives are all we have available :slight_smile:

Having put a lot of effort into removing multithreading from systems to make them go faster over the last few years, this is something I'm trying to keep a close eye on.

45 Likes

Yeah, one of the underappreciated aspects of having lightweight tasks is being able to write concurrent code without inflicting multithreading on yourself when one thread will do.

14 Likes

That's good to hear. If there is some special magic happening to make actors perform better than what libdispatch queues are doing, this is certainly something that needs to be explained. Because as currently stated, actors do not seem like the best of idea.

2 Likes

Irrespective of any magic, an "actor" only needs some form of mutually exclusive scheduling mechanism for its executor. Dispatch queues are but one possibility, but threads or event loops could work just as well.

2 Likes

How does that work? How does the runtime know wheter an actor should dispatch to its private queue or do differently?

2 Likes

You can override Actor.execute function (works in progress over Actor & Actor Isolation thread). tl;dr, it's a customization point for customizing how the actor would execute a (partial) task.

4 Likes

And let me guess, by default it will dispatch to its private queue, right? Which most developers will do, not understanding the consequences. I'm sorry if I sound very dismissive, I think it's great to improve concurrency support in the language, I'm just trying to make sure this will not turn into "The Return of the libdispatch". As I said, I'm afraid this has the long-running potential to be misused and do more harm than good, I certainly do not want to see that happen.

We can continue to discuss the actor execution system if you want on that thread, especially that I just asked this exact question and got answered:

Though if you're criticizing the roadmap at large (nothing wrong with that!) then we can probably stay here.

The overall shape of what's being proposed here is not that dissimilar to the Swift Concurrency Manifesto posted three years ago. That Swift would pursue async/await with actors should not come as a surprise, and without the level of detail provided by these proposals I'm not sure we'd be able to have a meaningful discussion.

These are very broad concerns lacking any specifies to ground them. An ownership model can certainly help in some cases (a uniquely-referenced entity is safe to pass from one actor to the next), but if your claim is that we need to both introduce a full ownership model and move all code over to it to enable concurrency, you're going to need to make a strong case that such a model exists and can be widely deployed for Swift.

As for the memory model, we are intentionally attacking a level above the memory model with actors. The lower-level memory model can come later, for use by experts who want to tune specific things lower in the stack. It's almost surely not the right level to focus on for the vast majority of Swift developers.

... because you can only communicate with other actors via async functions, which can be suspended, so you always make progress.

Doug

16 Likes

Which means an actor's functions can be interleaved mid-execution, is that correct? If actor A calls out to actor B and suspends, this allows another function to be executed in actor A before actor B replies and resumes execution in actor A.