[HN Gopher] Swift Concurrency Roadmap ___________________________________________________________________ Swift Concurrency Roadmap Author : rainworld Score : 82 points Date : 2020-10-30 17:36 UTC (5 hours ago) (HTM) web link (forums.swift.org) (TXT) w3m dump (forums.swift.org) | Ciantic wrote: | Does Swift's concurrency plan include thread-safety guarantees? I | would like to see a higher level language than Rust that includes | similar thread-safety guarantee "Thread safety isn't just | documentation; it's law". | rainworld wrote: | Yes, that's _The Second Phase._ | Ciantic wrote: | Oh great, so the eliminating data-races effectively means | it's also thread safe. | onelovetwo wrote: | could you imagine having a function with many arguments and | trying to find the async. | | internal func refreshPlayers(firstParameter: String, | secondParameter: Int, thirdParameters: Float) async { } | | these small mistakes are starting to add up with Swift. They | should really nip these things in the butt instead of adding upon | the inconsistencies. Its better to make bold decisions now that | you know is right than to change them 10 years from now when | everyone is already use to them, which is what some older | languages are dealing with now. | rcstank wrote: | Nip in the bud, not butt. It means to cut off the bud (i.e. | flower bud) before it grows into something larger. | andrewbarba wrote: | That's like saying: Could you imagine having a function with | many arguments and trying to find the return type? | | Swift already uses the space at the end of the function | declaration for things like throw and generic constraints. I | personally don't see an issue with where it is other than I | also write a lot of JavaScript and the context switching | between languages might take a couple seconds. | onelovetwo wrote: | There's no reason for "throws" to be there neither, that's | what I mean by these small mistakes adding up. | | If there was an argument that actually made sense, I'd | understand, but there is none. | | this is the argument: (https://forums.swift.org/t/swift- | concurrency-roadmap/41611/9) | myko wrote: | I agree that it feels like the language maintainers are backed | into corners and cannot correct old mistakes. | | Which feels strange coming from Apple. Google showed up to use | this with Go, write a tool that updates the code from version | "x" to version "y" instead of being beholden to source | compatibility issues in situations like this. | bestinterest wrote: | Can anyone fill me in on how this compares to Java's Project Loom | approach where they have said a hard no to coloring functions. | What are the advantages and disadvantages of async/await vs | introducing a green thread like approach for problems? | | Is async/await more suitable for user interfaces? | aardvark179 wrote: | In Loom we can make some trade offs around our knowledge of the | Java stack and the standard library. | | 1. We know that no JVM frame contains a raw pointer to anywhere | else in the stack. | | 2. We know which stack frames are JVM frames, and which are | native frames. | | 3. We know that there are unlikely to be native frames in the | portion of the stack between the point where we yield control | and the point we want to yield to. | | 4. We can change the standard library to check if we are in our | new lightweight thread or not and act appropriately. | | Knowing these we can avoid function coloring and push the | complex management of green threads into the standard library, | and reuse existing task scheduler code to manage these virtual | threads, and we can work to make thread local variables etc | work seamlessly with this new concurrency abstraction. | | This puts us in a very different design space. We can move a | portion of the stack to the heap when a thread is unmounted, | and we can change things about the GC and other internal | systems to make them aware of this new thing that can be in the | heap. | | This type of approach would be much harder in a language like | swift that tends to coexist with C and other languages that use | raw pointers or a non-moving GC, so I think the question is not | which is the better approach but which is the better approach | within your language ecosystem. | _old_dude_ wrote: | Implementing async/await is only a compiler change, so it can | be implemented rather easily on top of any languages, the | compiler transforms the code to a state machine. | | co-routine (Java Loom's one, Go one, Scheme one, etc) requires | to be able to serialize and deserialize a part of the stack | (move the stack on the the heap and vice versa), so it's | hard/impossible to implement with non managed runtimes which | like in C or objective C. | | In C, you can declare an address/pointer to an address on stack | (using &), but with a co-routine mechanism, the addresses of | parts of the stack are not constant. | | If you have a managed runtime, you rewrite those kind of | pointers when you copy parts of the stack back and forth. | ynik wrote: | async/await is more flexible in some sense. If you run the | tasks on a thread pool, it's mostly equivalent to green | threads. But if you run the tasks on a single thread, you get | cooperative multi-tasking. You can access mutable shared state | without using locks, as long as you don't use "await" while the | shared state is in an inconsistent state. For user interfaces | this is huge advantage: you can run all your code on the UI | thread; but the UI stays responsive while awaiting tasks. | | Also, here's an interesting use of async/await: software | hyperthreading: | https://isocpp.org/blog/2019/09/cppcon-2018-nano-coroutines-... | pron wrote: | Loom's virtual threads allow you to do the same with a | pluggable scheduler. | smasher164 wrote: | async/await is really just another formulation of linked stack | frames, where control is explicitly yielded. At the end of the | day, the thread stack state needs to be reified somehow, and | all of these approaches are equivalent in power. The only | difference is that now you have a type system that delineates | between functions that can and cannot be shuffled between | kernel threads. | | The benefits that Go (and potentially Loom) provide are with | the scheduler. When Go code calls into other Go code, it's fast | because preemption points can be inserted by the compiler. The | goroutine is parked when it is blocked (on I/O or some foreign | function), and in the slow path this logic is executed on a new | kernel thread. | | Although the Go approach involves more overhead in the slow | path, wrangling blocking code to work with the scheduler has | cross-cutting implications for library design. I.e, I don't | have to worry that a library I import that does file I/O will | pin my goroutine to a blocked thread by using a blocking | syscall. | mirekrusin wrote: | If all private state is managed by serial queue, doesn't it mean | you can easily deadlock yourself? Will swift statically reject | deadlocks on reentrance? | dmitriid wrote: | All languages end up with simple concurrency primitives such as | async/await. | | No one takes the next steps and introduces the high-level | primitives you actually need to work with actors and concurrency | in a sane manner: monitors, messages, supervisor trees. Erlang | has been around for thirty years, people. | pjmlp wrote: | Really? | | https://www.ponylang.io/ | | https://docs.microsoft.com/en-us/dotnet/standard/parallel-pr... | | https://dotnet.github.io/orleans/ | | https://microsoft.github.io/coyote/ | spinningslate wrote: | OK, OP was somewhat remiss in saying "no-one". But you have, | I think, missed his point. | | Instead, substitute "few mainstream language designers" and | it stands up. By mainstream I mean Java, | Javascript/Typescript, C#, C, C++, Python and such. Most have | introduced async/await. None has meaningfully gone beyond | that as far as I'm aware. Working with Erlang's concurrency | model is a refreshingly simple, consistent mental model | compared to the mismash of concurrency features provided by | the mainstream. In Erlang, it's as simple as: | | 1. Do these things need to happen concurrently? | | No: regular functions. Yes: spawn regular functions. | | Compare that to the mainstream: | | 1. Do these things need to run concurrently? | | No: regular functions. Yes: are there only a few, and/or do I | need strong isolation? | | Yes: use OS-level processes No: do I want the OS to take care | of scheduling / preemption? | | Yes: use threads No: use async/await | | Is there a chance that my async operations will be scheduled | across multiple OS threads? | | No: get speed boost from no scheduling overhead, but remember | to yield if there's any long-running actions. Yes: build my | own likely-buggy, half-baked scheduler | | Oh, and as a bonus: run back up the entire call stack to make | all functions that call mine async. | | And that's before we get to error handling. I'd take Erlang | supervision trees _every day_ over trying to figure out which | nested async callback function generated an exception. | pjmlp wrote: | Most of my links as for .NET frameworks that went behind | async/await. | | One of them (Orleans) is used to power Halo's backend. | dmitriid wrote: | Really. Only one of them is barely beyond experimental | (Pony). The rest are experimental projects. | pjmlp wrote: | Today I learned that Halo is an experimental game. | Thaxll wrote: | Because it's not a language feature it's an entire runtime / | paradigm. | dmitriid wrote: | No idea why you're getting downvoted, but you're right. ONe | of the reasons why Erlang allows for monitors, and | supervision trees and many other niceties are precisely | because the VM is built that way: Processes are isolated. | Even if one process suddenly dies, the VM will take care of | cleanup and will notify any monitoring processes, etc. | Someone wrote: | FTA: _"This is a common pattern: a class with a private queue | and some properties that should only be accessed on the queue. | We replace this manual queue management with an actor class | | [...] | | Things to note about this example: | | - Declaring a class to be an actor is similar to giving a class | a private queue and synchronizing all access to its private | state through that queue. | | - Because this synchronization is now understood by the | compiler, you cannot forget to use the queue to protect state: | the compiler will ensure that you are running on the queue in | the class's methods, and it will prevent you from accessing the | state outside those methods. | | - Because the compiler is responsible for doing this, it can be | smarter about optimizing away synchronization, like when a | method starts by calling an async function on a different | actor."_ | | The article also links to | | - https://forums.swift.org/t/concurrency-actors-actor- | isolatio... (pitch for implementing actors) | | - https://github.com/DougGregor/swift- | evolution/blob/actors/pr..., which links to | https://github.com/DougGregor/swift-evolution/blob/actors/pr... | (proposal for implementing actors) | | I don't know Erlang well, but what's missing? | dnautics wrote: | Monitors, linking, supervision trees, vm-level introspection | into the state of the actors, distribution primitives that | make actor identity nonlocal across clusters, actor | cancellation (like kill -KILL), graceful actor shutdown, sane | id serialization (how easy is it for me to serialize an actor | Id, put it on a kafka queue and have it come back in a | response so I can route the response back to the actor) etc, | etc, etc. | ardit33 wrote: | async/await are not primitives. Mutexes, semaphores, atomic | counts etc... those are true primitives in multithreading and | they have been around forever (since the 70s at least). | | I feel the Swift lang. design is like amateur hour at its best, | trying to reinvent the wheel, but still end up where it | started, but at worse overrall usability. Just re-arranging | chairs. 8 years later, and still Objective-c + GCD combo is | better at multithreading. | | In comparison: Java had decent multithreading support since | version 1.1,(one year later after its release) and it had NIO | by JDK 1.4 and full modern multithreading by JDK 1.5. | | Swift is a couple of years behind because it is trying too hard | to be cool and different. | machello13 wrote: | > worse overrall usability | | You can definitely find iOS/macOS developers who prefer | objective-c, but they're in the minority. The vast majority | would say that Swift is way, way more usable than | Objective-C. Obj-C still has some advantages (dynamism being | a big one) but for most tasks Swift makes developing both | easier and more safe. | kitsunesoba wrote: | Coming from several years of writing Obj-C, Swift has | certainly been a net benefit for me. Writing it well does | require a bit of a shift in one's way of thinking (writing | "Obj-C in Swift" is a recipe for pain) but I find that once | that hurdle is cleared it's a very productive language to | work with. | rubiquity wrote: | > async/await are not primitives. Mutexes, semaphores, atomic | counts etc... those are true primitives in multithreading | | async/await, a way to model concurrency, and | mutexes/semaphore/etc, a way to safely share data, belong to | separate categories and one does not preclude the usage of | the other, especially if your coroutines are allowed to run | on different threads. | asimpletune wrote: | I don't think they meant one precludes the other, and | "modeling concurrency" definitely is a "sharing data" | problem. In other words, you would have to build a nicer | concurrency abstraction out of a lower level primitive at | some point. ___________________________________________________________________ (page generated 2020-10-30 23:00 UTC)