Concurrency model #746
Replies: 16 comments 89 replies
-
I'm not exactly sure what the opening example is supposed to demonstrate, but I don't like (and maybe those with more experience in this area can correct me) is that it seems to be an example of things we don't want to happen (often):
|
Beta Was this translation helpful? Give feedback.
-
I wish I could make gitbook-style/gdocs-style comments here! GitHub steadfastly refuses to create a system for commenting on documents. :-( |
Beta Was this translation helpful? Give feedback.
-
I assume the |
Beta Was this translation helpful? Give feedback.
-
Presumably that means it represents a join or something that continues the computation asynchronously?
|
Beta Was this translation helpful? Give feedback.
-
I hate monads :-) What does it do? I understand the description of |
Beta Was this translation helpful? Give feedback.
-
I see this is covered in the ensuing discussion, but you really want to be defining your generic async algorithms in terms of async operations that have not yet been scheduled for execution. Trying to juggle with running operations leads to problems in generic code. That means there is more than two states for an async operation: suspended. All async work starts suspended, transitions to running, and then to done. Also, I see a channel for success (with a value), a proposed channel for errors (exceptions), but no dedicated channel for cancellation, and no way to request that a running operation stop early. S/r doesn't have "detach" as a primitive. Detached computations break structured concurrency and put you back in a world where you can't reason about the lifetimes of objects owned or accessed by async operations. EDIT: Rather than truly detached work, in s/r you spawn "detached" work in a nursery (called |
Beta Was this translation helpful? Give feedback.
-
I'd like to get @kirkshoop and Lisa Lippincott involved in this discussion, since this is squarely in their wheelhouse: https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2019/p1677r2.pdf "This paper is focused on explaining why errors are a bad way to represent a cancelled result." IMO, it's well worth your time reading this paper before committing to using exceptions to represent cancellation in Val, especially if it's your intention to have the equivalent of |
Beta Was this translation helpful? Give feedback.
-
I wonder if cancellation deserves its own discussion thread? |
Beta Was this translation helpful? Give feedback.
-
I am familiar with Seastar ( https://github.com/scylladb/seastar/ ), a C++ async framework for linux focused on getting good performance on multicore systems. It might be worth to also take inspiration from their thread-per-core approach to async programming. Also, the Seastar devs use pragmatic solutions to the typical challenges you have in the async world, like error handling and cancellation. Seastar's design was motivated by the observation that when using "classical" multi-threaded programming (with mutexes & locks for thread safety), contention will kill good utilization of large multicore machines. Hence, it has a "one thread per cpu core"-model, and uses the convention to not share things between cores, except via explicit (async) message passing, which essentially brings you back to having multiple instantiations of the single-threaded programming model, which completely removes the need for locks/mutexes. As such, it has e.g. its own re-implementation of shared_ptr that does not have locks or atomics. Also it has its own NUMA-aware per-core memory allocator. Also has async networking (+dpdk) and async storage APIs (DMA reads/writes). Seastar's way of doing things is quite opinionated, hence might not be a good fit for every async-programming task. On the other hand, |
Beta Was this translation helpful? Give feedback.
-
I have 3 things I'd like to ask:
On avoiding function colouring:
Edit: The talk is pretty long, so I might have to split it into two parts. If you don't have time for a full explanation by the next community meeting (1st July), I can bring up function colouring in the second part (15th July). |
Beta Was this translation helpful? Give feedback.
-
@lucteo, what about the (virtual) memory overhead of stackful coroutines, and |
Beta Was this translation helpful? Give feedback.
-
I don’t think we have any plans to copy stacks around, FWIW
|
Beta Was this translation helpful? Give feedback.
-
You'd need a memory model that forbids references to stack objects when a routine yields then? Are stacks with sizes < 1 page really a concern? |
Beta Was this translation helpful? Give feedback.
-
This is an interesting part (link starts at 25min:22secs) of a talk by an engineer from Google about some new coroutine library, https://www.youtube.com/watch?v=k-A12dpMYHo&t=1519s BTW when the speaker refers to "other talks", he might be talking about this one: |
Beta Was this translation helpful? Give feedback.
-
@nmsmith wrote:
If I understand what you mean by “structures,” I'm not sure there are many such structures that we know how to reason about. The only shared state I actually understand is monotonic (or cache-like, which is similar). Everything else, it seems, is subject to logical races. |
Beta Was this translation helpful? Give feedback.
-
The Conclusions section of the BoC dissertation contains this handy evaluation of its suitability for various use-cases:
|
Beta Was this translation helpful? Give feedback.
-
Val must offer composable concurrency primitives that can be used to build generic concurrent programs.
Usage example
This document is inspired the senders/receiver model proposed for C++.
In this example,
plan
denotes a computation plan and represents work that has not been scheduled for execution yet.A computation plan is typically a lambda
[E] () fx -> T
, whereT
denotes the value that the computation produces.The environment
E
of a computation plan must conform toSinkable
.Once Val will support exceptions,
fx
may containthrows
.spawn plan
consumesplan
and schedules its execution, resulting in a computationanswer
.A computation represents work that may be in either of two states:
answer.map
attaches additional work to a computation.map
accepts a closure that receives the result of the computation as an argument and describes how that value is transformed.[Note: a computation
Async<E, T, fx>
is a monad for which the methodmap
preserves the functor laws.map
is not the monadic bind operator.]answer.sync_wait()
suspends the current thread, waiting foranswer
to terminate.The result of the computation is consumed and used to (re)initialize
n
.Detailed design
Computations
A computation is described by a type with the following API:
[Note:
Async.cancel
requires exception support.]Receivers
A receiver represents the continuation of a computation by "receiving" its value or handling errors or cancellation.
A receiver is a type that conforms to the following trait:
Standard receivers
The standard library provides a collection of general purpose receivers that can be used to compose computations.
SyncWait
SyncWait
is a receiver that suspends the current thread and waits for a computation to terminate and consumes its value or errors.Bind
Bind
implements the monadic bind operator of a computation.Alternatives considered
Treating computations as projections of their work
The current design requires the environment of a computation plan to conform to
Sinkable
, meaning that a plan cannot havelet
orinout
captures.Should that be possible, a computation should be a projection of its work, which makes it unsinkable and generally impedes on the ability to use linearity.
The
spawn
keywordAn earlier design used
async
to instead ofspawn
to create computations.Discussions with practitioners from other communities suggest that the keyword
async
conveys a different meaning.In many programming languages,
async
is a modifier indicating that a particular function is asynchronous and has a different semantics.Beta Was this translation helpful? Give feedback.
All reactions