[I've updated the "Semantics for foreign threads" document by re-ordering the sections a bit. It'd benefit from having a bit more formal syntax. No one has commented a single word on the operational semantics. I don't know whether that's because it's so clear that no discussion is needed, or so opaque that no discussion is possible.] | > I must admit that I can't remember the | > exact semantic distinction between those "safe" and "threadsafe" | | The problem is, nobody does... the original implementation didn't work | in all cases. The original implementation made "safe" calls block all | other haskell threads in some cases, and crashed in other cases. | "Threadsafe" means that calling the foreign import shouldn't block or | otherwise disturb other haskell threads. "Safe" means... well... almost | nobody seems to know, and still fewer people agree on it. | In the current "HEAD", there is no difference between threadsafe and | safe. If someone comes up with a clear specification of why and how | "safe" should be different from threadsafe, things might change again. Indeed, the semantics in the "semantics of bound threads" document makes no distinction between "safe" and "threadsafe" either. The original intention was this: a threadsafe foreign call must not block Haskell threads, even if the foreign call blocks in foreign land. a safe call is not required to obey this constraint The motivation was that thread-safety might require more admin (e.g. relinquishing the lock on the main Haskell heap), and this admin might be costly. A side consequence of GHC's implementation (albeit not of the above specification) is that no Haskell threads progress during a safe call (unless it provokes a call-back..?). I recall that some people actually started to rely on this, though it was never intended as part of the spec. I think Wolfgang is saying that the apparent efficiency gain of not requiring thread-safety is illusory, and so we can abolish the safe/threadsafe distinction. I think that would be a very worthwhile gain. Does anyone disagree with this? Simon
Simon Peyton-Jones wrote:
[I've updated the "Semantics for foreign threads" document by re-ordering the sections a bit. It'd benefit from having a bit more formal syntax.
That should be just a matter of copying from the ffi spec and adding an additional specialid... I had hoped to avoid learning how to use yet another TeX package... but OK, I'll do that.
No one has commented a single word on the operational semantics. I don't know whether that's because it's so clear that no discussion is needed, or so opaque that no discussion is possible.]
I spent about half an hour staring at a printout, before I saw that it was all perfectly logical if I corrected one typo. Now I think there are no typos left in the formal semantics, so it should need less staring... Anyway, do you think the proposal has been discussed enough for me to start working on a prototype implementation? About threadsafe/safe:
I think Wolfgang is saying that the apparent efficiency gain of not requiring thread-safety is illusory, [..]
In GHC, we might be able to save some time, but only for calls without call-back. As soon as there's a call-back, I can't see how we can save any time at all.
[...] and so we can abolish the safe/threadsafe distinction. I think that would be a very worthwhile gain. Does anyone disagree with this?
Kill it kill it kill it! We don't need that distinction. However, it might be worthwhile for other implementations of Haskell, but we can't tell, because GHC is the only one that supports "threadsafe" at the moment. Does anyone plan to add support for multiple OS threads to Hugs or NHC? If someone wants to keep "safe" in the FFI spec as an "optimization hint", then that's fine for me, but something has to be done about the misleading naming ("safe" is NOT SAFE) and an optimization hint should never be the default. And it should be made absolutely clear that an implementation may treat everything as "threadsafe". Cheers, Wolfgang
Does anyone plan to add support for multiple OS threads to Hugs or NHC?
I think it will depend a bit on the complexity so let me sketch how I think it can be implemented. First let me outline my current understanding of what 'bound' means. Consider the following scenario: Haskell program is running in OS thread 't1' Haskell program calls C function 'foo'. 'foo' forks a new OS thread 't2'. In parallel: 't1' calls Haskell function 'f1' and 't2' calls Haskell function 'f2' 'f1' calls C function 'g1' 'f2' calls C function 'g2' My understanding is that 'bound' requires that 'g1' be executed by thread 't1' and that 'g2' be executed by thread 't2'. It would be nice if 'f1' and 'f2' could run simultaneously but the ffi is not going to impose that on us. If 'f1' were to block on an MVar, 'f2' could start running and vice-versa. While 'g1' is running, 'f2' can run and while 'g2' is running, 'f1' can run. Based on this understanding, I believe that single-threaded runtimes could easily implement 'bound' by doing nothing more than using a lock to ensure that at most one OS thread executes Haskell code at once. Thus, a global lock would have to be acquired when a bound function is called or when a thread starts running and the lock would be released when a thread stops running (completes, calls out to C or blocks). This sounds pretty simple (a few tricky corner cases to get right but no major upheaval in the runtime systems) and the locking requirements are quite modest (so, hopefully, portable) so I think an implementation is pretty likely to happen. Timescale will depend on when people find time or money to do it. -- Alastair Reid alastair@reid-consulting-uk.ltd.uk Reid Consulting (UK) Limited http://www.reid-consulting-uk.ltd.uk/alastair/
Alastair Reid wrote:
Does anyone plan to add support for multiple OS threads to Hugs or NHC?
I think it will depend a bit on the complexity so let me sketch how I think it can be implemented.
First let me outline my current understanding of what 'bound' means. Consider the following scenario:
Haskell program is running in OS thread 't1' Haskell program calls C function 'foo'. 'foo' forks a new OS thread 't2'. In parallel: 't1' calls Haskell function 'f1' and 't2' calls Haskell function 'f2' 'f1' calls C function 'g1' 'f2' calls C function 'g2'
My understanding is that 'bound' requires that 'g1' be executed by thread 't1' and that 'g2' be executed by thread 't2'. It would be nice if 'f1' and 'f2' could run simultaneously but the ffi is not going to impose that on us. If 'f1' were to block on an MVar, 'f2' could start running and vice-versa. While 'g1' is running, 'f2' can run and while 'g2' is running, 'f1' can run.
Yes, if the Haskell functions 'f1' and 'f2' are both exported using "bound", that is foreign export bound "f1" f1 :: something foreign export bound "f2" f2 :: something
Based on this understanding, I believe that single-threaded runtimes could easily implement 'bound' by doing nothing more than using a lock to ensure that at most one OS thread executes Haskell code at once.
That was the general intention. Things might get more complex though if you want non-blocking foreign calls (a.k.a. "threadsafe"). Then you could either always create one OS thread for every Haskell thread, or use a slightly more complex scheme (as GHC does, but I'm sure there are other ways of doing this). Implementing "threadsafe" is basically independent from implementing the bound threads proposal, i.e. you can implement one without implementing the other (GHC currently has "threadsafe", but "bound threads" are still science fiction). Cheers, Wolfgang
Hi all, I have just spend some time reading through all the discussions and the new "threads" document and I would like to propose the addition of a new library function.
forkOS :: IO () -> IO ThreadID
The function "forkOS" forks a new Haskell thread that runs in a new OS (or native) thread. With this, I also propose that "forkIO" always runs a Haskell thread in the same OS thread that the current Haskell thread runs in. (i.e. "forkIO": same OS thread, "forkOS": new OS thread) Using the new primitive, we can view the new "threadsafe" keyword as syntactic sugar:
foreign import threadsafe foo :: Int -> IO Int
===>
foo :: Int -> IO Int foo i = threadSafe (primFoo i)
foreign import "foo" primFoo :: IO Int
where
threadSafe :: IO a -> IO a threadSafe io = do result <-newEmptyMVarforkOS (do{ x <-io; putMVar result x }) getMVar result
Note that "forkOS" can use thread pooling in its implementation. The advantage of a separate function "forkOS" is that we put control back to the users hands, as a programmer can be very specific about which Haskell threads are part of a certain OS thread and can be specific about the OS thread that is used to run a foreign function. On other words, it is absolutely clear to which OS thread a Haskell thread is bound. In this respect, it helps to have another function that runs a Haskell thread in a specific OS thread.
getOSThread :: ThreadID -> OSThreadID forkIOIn :: OSThreadID -> IO () -> IO ThreadID
I have the feeling that it is not difficult to implement "forkOS" and family once the runtime system has been upgraded to support multiple OS threads. Wolfgang, you seem to be the expert on the OS thread area, would it be hard? ---- I am not saying that we should discard the "threadsafe" keyword as it might be a useful shorthand, but I think that it is in general a mistake to try to keep the management of OS threads implicit -- don't use new keywords, add combinators to implement them! I feel that the following has happened; urk, we need some way of keeping haskell threads running while calling C; we add "threadsafe"; whoops, sometimes a function expects that it is run in the same OS thread; we add "bound"; whoops, sometimes functions expect to be run from a specific OS thread... unsolved?? Before we know it, we have added tons of new keywords to solve the wrong problem. Maybe it is time to take a step back and use a somewhat lower level model with two fork variants: "forkIO" (in the same OS thread) and "forkOS" (in a new OS thread). It seems that none of the above problems occur when having explicit control. In general it seems that OS threads are a resource that is too subtle to be managed automatically as they have a profound impact on how libraries are used and applications are structured. All the best, Daan. "worse is better" :-)
participants (4)
-
Alastair Reid -
Daan Leijen -
Simon Peyton-Jones -
Wolfgang Thaller