| ... | @@ -47,6 +47,229 @@ brief rationale. |
... | @@ -47,6 +47,229 @@ brief rationale. |
|
|
---
|
|
---
|
|
|
|
|
|
|
|
|
|
|
|
|
|
## Outstanding issues
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
There follows a list of issues on which decisions are still to be
|
|
|
|
made. A numbered item, eg. 1.2, indicates a question, an item ending
|
|
|
|
in a letter, eg. 1.2.a, indicates a possible choice for question 1.2.
|
|
|
|
|
|
|
|
|
|
|
|
### 1. Cooperative or preemptive concurrency?
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
**1.a.** The spec requires cooperative concurrency, preemption is
|
|
|
|
allowed as an extension. Both would be specified precisely in
|
|
|
|
terms of what progress and fairness guarantees the programmer can
|
|
|
|
expect.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
**Pros**
|
|
|
|
|
|
|
|
|
|
|
|
- Allows many more implementations, including Hugs (although Hugs
|
|
|
|
needs to be updated to handle concurrent and
|
|
|
|
concurrent/reentrant foreign calls, and non-blocking I/O).
|
|
|
|
- Preemption isn't always required; a common case is an
|
|
|
|
application that relies on concurrency for I/O multiplexing,
|
|
|
|
where most threads are usually blocked.
|
|
|
|
- Cooperative systems can be faster, and are simpler to implement
|
|
|
|
(see state threads reference).
|
|
|
|
|
|
|
|
|
|
|
|
**Cons**
|
|
|
|
|
|
|
|
|
|
|
|
- Portability problems: if a programmer develops a concurrent
|
|
|
|
application on a preemptive system, there is no guarantee that
|
|
|
|
it will work as expected on a cooperative system, and the
|
|
|
|
compiler/runtime can give no useful feedback.
|
|
|
|
- Need to specify which operations are "yield points" in library
|
|
|
|
documentation.
|
|
|
|
- Long-running pure code must be refactored into the IO monad so
|
|
|
|
that explicit yield points can be inserted.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
**1.b.** Preemption is required by the spec.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
**Pros**
|
|
|
|
|
|
|
|
|
|
|
|
- Simpler from the programmer's point of view: no yield, no
|
|
|
|
worrying about latency. "write code as if the current
|
|
|
|
thread is the only one.".
|
|
|
|
|
|
|
|
|
|
|
|
**Cons**
|
|
|
|
|
|
|
|
|
|
|
|
- Imposes significant implementation constraints. Essentially
|
|
|
|
only GHC and YHC would be able to implement it. JHC has no
|
|
|
|
concept of thunks, which is a barrier to implementing general
|
|
|
|
preemption.
|
|
|
|
- Even in a preemptive system, deadlocks are easy to program, and
|
|
|
|
arbitrary starvation can result from laziness: evaluating
|
|
|
|
arbitrary expressions while holding an MVar can prevent other
|
|
|
|
threads from running. The fact that we therefore require seq
|
|
|
|
and possibly deepSeq is disturbing, as is the notion that the
|
|
|
|
programmer must think about "what is evaluated" when
|
|
|
|
programming concurrent code.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
**1.b.1.** Include thread priorities or not?
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
**Pros**
|
|
|
|
|
|
|
|
|
|
|
|
- Some applications require it
|
|
|
|
- It affects the fairness/progress guarantees, including the
|
|
|
|
possibility of priorities from the outset may be simpler.
|
|
|
|
|
|
|
|
|
|
|
|
**Cons**
|
|
|
|
|
|
|
|
|
|
|
|
- Hard to implement, no implementations yet.
|
|
|
|
|
|
|
|
### 2. Syntax for foreign call annoatations.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
**2.1.** choices for concurrent calls:
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
**2.1.a.** we annotate concurrent calls with
|
|
|
|
|
|
|
|
|
|
|
|
>
|
|
|
|
>
|
|
|
|
> **a.** concurrent
|
|
|
|
>
|
|
|
|
> **b.** mayblock
|
|
|
|
>
|
|
|
|
> **c.** mightblock
|
|
|
|
>
|
|
|
|
> **d.** blocks
|
|
|
|
>
|
|
|
|
> **e.** longrunning
|
|
|
|
>
|
|
|
|
>
|
|
|
|
|
|
|
|
>
|
|
|
|
>
|
|
|
|
> **Rationale** for using the term "block": blocking is the main
|
|
|
|
> reason for wanting concurrent calls. Concurrent calls allow the
|
|
|
|
> progress guarantee to be retained in the presence of a blocking
|
|
|
|
> foreign call. A foreign call that just takes a long time is still
|
|
|
|
> making progress.
|
|
|
|
>
|
|
|
|
>
|
|
|
|
|
|
|
|
>
|
|
|
|
>
|
|
|
|
> **Rationale** for not using the term "block": the fact that the
|
|
|
|
> call blocks is immaterial, the property we want to provide is that
|
|
|
|
> it doesn't impede progress of other Haskell threads. A long-running
|
|
|
|
> call is indistinguishable from a blocked call in terms of the
|
|
|
|
> progress of other threads.
|
|
|
|
>
|
|
|
|
>
|
|
|
|
|
|
|
|
>
|
|
|
|
>
|
|
|
|
> We often don't know whether a library call will block or not (it
|
|
|
|
> isn't documented), whereas saying a call should run concurrently
|
|
|
|
> with other threads is a choice the programmer can reasonably make.
|
|
|
|
>
|
|
|
|
>
|
|
|
|
|
|
|
|
|
|
|
|
**2.1.b.** we annotate non concurrent calls:
|
|
|
|
|
|
|
|
|
|
|
|
>
|
|
|
|
>
|
|
|
|
> **Rationale** for annotating the non-concurrent calls: this is a
|
|
|
|
> performance issue. It is always correct to make a concurrent call,
|
|
|
|
> but it might be more efficient to make a non-concurrent call if the
|
|
|
|
> call does not block. An implementation might implement *all*
|
|
|
|
> calls as concurrent, for simplicity.
|
|
|
|
>
|
|
|
|
>
|
|
|
|
|
|
|
|
>
|
|
|
|
>
|
|
|
|
> Against: John Meacham says "The FFI is inherently unsafe. We do not
|
|
|
|
> need to coddle the programer who is writing raw FFI code."
|
|
|
|
>
|
|
|
|
>
|
|
|
|
|
|
|
|
>
|
|
|
|
>
|
|
|
|
> **a.** nonconcurrent
|
|
|
|
>
|
|
|
|
> **b.** noblock
|
|
|
|
>
|
|
|
|
> **c.** returnsquickly
|
|
|
|
>
|
|
|
|
> **d.** fast
|
|
|
|
>
|
|
|
|
> **e.** quick
|
|
|
|
>
|
|
|
|
>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
**2.2.** choices for non-reentrant calls:
|
|
|
|
|
|
|
|
|
|
|
|
>
|
|
|
|
>
|
|
|
|
> **a.** nonreentrant
|
|
|
|
>
|
|
|
|
> **b.** nocallback
|
|
|
|
>
|
|
|
|
>
|
|
|
|
|
|
|
|
>
|
|
|
|
>
|
|
|
|
> **Rationale** for annotating the non-reentrant calls, as opposed
|
|
|
|
> to the reentrant ones: we want the "safe" option to be the default
|
|
|
|
> (as in the FFI spec).
|
|
|
|
>
|
|
|
|
>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
**2.3.** should we annotate foreign calls according to whether they need
|
|
|
|
to access thread-local state (TLS) or not?
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
**Pros**
|
|
|
|
|
|
|
|
|
|
|
|
- a call that doesn't need access to thread-local state, called from a bound thread, can be executed much more quickly on an implementation that doesn't run the Haskell thread directly on the bound OS thread, because it doesn't need to context switch.
|
|
|
|
|
|
|
|
|
|
|
|
**Cons**
|
|
|
|
|
|
|
|
|
|
|
|
- libraries that require TLS, eg. OpenGL, often have many fast TLS-using functions. So implementations that need the no-TLS annotation in order to get good performance, will probably still get poor performance from libraries that need TLS anyway.
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
|
|
|
|
|
## References
|
|
## References
|
|
|
|
|
|
|
|
|
|
|
| ... | | ... | |