RE: [Haskell-cafe] FFI and callbacks

On 20 July 2005 16:23, John Goerzen wrote:
On Wed, Jul 20, 2005 at 04:10:38PM +0100, Simon Marlow wrote:
On 20 July 2005 14:35, John Goerzen wrote:
These systems generally have some sort of an opaque main loop, implemented in C. This loop would usually never return, or perhaps only return once the UI is destroyed.
Is the library thread-safe or not? I mean OS-thread safe. If it is, then you're home and dry: just compile your program with GHC's -threaded option, and any foreign calls that need to run concurrently with Haskell threads must be declared "safe" (which is the default in fact, so instead I should really advise you to mark all calls that don't need to run concurrently as "unsafe", because that's good for performance).
That's half of the question. The other half is: what is the canonical way of handling this mainloop that doesn't return from Haskell? Do we just treat it that way in Haskell as well, or would some people forkIO it?
You could forkIO the main loop if you want; it shouldn't make any difference, except that GHC terminates the program when the main thread completes, so it's more convenient to have the main loop run in the main thread, so that the program terminates when the main loop returns.
Does any part of your answer vary depending on whether the Haskell threads in question are forkIO threads or forkOS threads? I imagine that it might.
No, and I don't think you need forkOS here. This paper might help shed some light: http://www.haskell.org/~simonmar/papers/conc-ffi.pdf
If the library isn't thread-safe, then you're in the same boat as Gtk and wxHaskell (I believe) where there are known problems with having a multithreaded Haskell GUI app. You can only have one OS thread (and hence only one Haskell thread) doing GUI operations, and that is the
What about multiple forkIO threads?
You should think of Haskell as having a one-to-one mapping between OS threads and Haskell threads. The only difference between forkIO and forkOS is that a forkIO thread can migrate from one OS thread to another, whereas a forkOS thread always runs on the same OS thread. This difference is therefore only visible when you make FFI calls, because there's no way in Haskell to tell what OS thread you're running on. In practice, GHC uses an optimised implementation to take advantage of the fact that forkIO threads are allowed to migrate from one OS thread to another, and it runs all the forkIO threads using a small pool of worker OS threads. The design of forkIO/forkOS and the FFI is carefully crafted to accommodate both GHC's optimised implementation and simpler implementations such as one-to-one or a giant lock.
thread in the main loop. You have to somehow set up a communication between your other Haskell threads and the thread running the main loop - perhaps you can send requests of type (IO ()) down a channel to the main loop thread which wakes up occasionally to run them, for example.
I imagine there could be exceptions to this.. For instance, perhaps a given operation needs to do 5 things, perhaps simultaneously, before displaying a result. It could, perhaps, fire off five threads and just not display a result until all five have terminated, yes?
Sure, but it's just a matter of converting direct GUI calls into RPCs to the GUI thread, using some appropriate RPC mechanism. Cheers, Simon

On 2005-07-20, Simon Marlow
You could forkIO the main loop if you want; it shouldn't make any difference, except that GHC terminates the program when the main thread completes, so it's more convenient to have the main loop run in the main thread, so that the program terminates when the main loop returns.
Here's my concern. I assume that, internally, forkIO is implemented using poll() or select(). This obviously requires that all parts of the program cooperate with this mechanism. If this mainloop never returns (that is, it blocks until the UI is destroyed), how do other forkIO'd threads ever get a chance to run? Assuming my program uses only forkIO threads, does the C library really need to be thread-safe? -- John

On 2005-07-20, Simon Marlow
This paper might help shed some light:
Forgot to reply to this. *Very helpful* link, I had always wondered what the bound thread functions in Control.Concurrent were for :-) So let me see if I understand this correctly. Let's say that I have: * A program that uses multiple lightweight Haskell threads (all started with plain forkIO calls) * An event-driven C library, not not thread-aware, with a blocking main loop * GHC 6.4 * My C calls all imported "safe". Now then, if I understand this correctly, that a call to the C main loop will create a new bound OS thread, so it will not interrupt any other forkIO'd threads in Haskell. However, if one of my Haskell-based callbacks creates new threads with forkIO, I could be in trouble; if they make any calls into C, a new bound OS thread would be created for them, and this could wind up causing trouble in C. I would probably need some sort of "global MVar" to synchronize access into the C world. I also have some follow-up questions after looking at the Control.Concurrent API: 1. It seems that there is no function that says "block the current thread until the thread given by ThreadId dies" 2. What is the preferred way to implement a simple lock? With an MVar? Thanks.
participants (2)
-
John Goerzen
-
Simon Marlow