[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)