[Yhc] Re: some initial questions
Simon Marlow
simonmar at gmail.com
Tue Mar 7 15:14:04 EST 2006
Thomas Shackell wrote:
> The first thing to note is that concurrency is implemented in Yhc by the
> interpretter running some instructions for one process, then some more
> instructions for the next process and so on.
>
> This is rather than using one OS thread per Haskell process because all
> processes can access the global heap, so if using OS threads it would be
> necessary to either lock every heap access (incredibly slow) or use some
> of the truely nasty tricks that concurrent GHC uses.
I resent that :-) There are other choices too: one OS thread per
Haskell thread works fine if there's a Giant Lock around the whole
runtime (this is the approach that OCaml uses, for instance). Your
approach is clearly the right one in the context of YHC, though.
I think it's fantastic that you guys have got concurrency working, BTW.
Can you really context switch between two arbitrary instructions? Is
the stack in a GC'able state at all times?
> The first problem, as you note, is that originally we only had one stack
> but now every process needs its own stack. After considering possible
> options I decided the easiest way was:
> - process stacks become normal nodes in the heap, and are garbage
> collected like other heap nodes. This preserves the property that
> we can't run out of heap but not stack (and vice versa).
>
> - a process is initially allocated a small stack space in the heap
> and if that runs out we simply allocate a new stack space (twice as
> large as before) and let the old stack be garbage collected.
This is exactly what GHC does. I found we had to be very careful with
pointers to the old version of the stack, which occur from time to time.
eg. ThreadIds are just pointers to the TSO (== stack), they have to be
indirected to the new stack when accessed.
> Now going back, the approach of running some instructions from one
> thread then some more from another is fine providing every instruction
> runs quickly. They all do, with one exception, PRIMITIVE. This is
> because PRIMITIVE is used for IO actions/FFI calls.
>
> The solution? Well the basic idea is "just spawn an OS thread to do the
> IO action while the main thread continues running Haskell".
>
> In detail, when PRIMITIVE is called for an IO action/FFI call:
>
> - an OS thread is taken from a pool of threads
>
> - the main thread tells the OS thread to run the IO action/FFI call
>
> - the main thread then reschedules so another haskell process can run
>
> - eventually the OS thread complete it's action and is put back in
> the pool. It adds the process to the list of processes that have
> completed their IO actions and are waiting to be rescheduled.
>
> - when the main thread next reschedules it checks the list of
> completed IO actions and makes the corresponding blocked processes
> ready to run again.
What happens if the FFI call invokes a callback? (a foreign export, or
foreign import wrapper)? Can callbacks be invoked by multiple OS
threads? Are you planning to implement bound threads?
Cheers,
Simmon
More information about the Yhc
mailing list