Re: [Haskell-cafe] concurrency vs. I/O in GHC

From: Donn Cave
[I wrote initially, ...]
As I have migrated more of my application into Haskell, I find that I/O in one thread effectively blocks other threads.
Resolved - the SSL_read external I/O function needs to be "safe".
If anyone is listening, I would very much like for there to be a mechanism by which external functions can be called "unsafe"-ly, but without blocking all other Haskell threads. I have code that does this: calcUpdate a b c = do expensiveComputation a b expensiveComputation a c "expensiveComputation" mutates the second argument (b or c here), but does not call back into Haskell or update "a" and I would very much like to run these two in parallel, however it's not possible if "expensiveComputation" is unsafe. If I make "expensiveComputation" safe, the extra time necessary for safe-ness outweighs the gains from parallelization for even reasonably-sized inputs (the expensiveComputation is called 100k's of times or more). My current plan is to handle the parallelization in C, but I'd prefer to do it in Haskell if possible. John

Hello John, Monday, October 18, 2010, 8:15:42 PM, you wrote:
If anyone is listening, I would very much like for there to be a mechanism by which external functions can be called "unsafe"-ly, but without blocking all other Haskell threads. I have code that does this:
+RTS -N2 -- Best regards, Bulat mailto:Bulat.Ziganshin@gmail.com

On Fri, Oct 22, 2010 at 6:16 PM, Bulat Ziganshin
Hello John,
Monday, October 18, 2010, 8:15:42 PM, you wrote:
If anyone is listening, I would very much like for there to be a mechanism by which external functions can be called "unsafe"-ly, but without blocking all other Haskell threads. I have code that does this:
+RTS -N2
This doesn't work, which was why the OP asked in the first place. When a thread calls an unsafe foreign function, it blocks everything until that function returns.

On 10/23/10 7:54 AM, John Lato wrote:
On Fri, Oct 22, 2010 at 6:16 PM, Bulat Ziganshin
mailto:bulat.ziganshin@gmail.com> wrote: Hello John,
Monday, October 18, 2010, 8:15:42 PM, you wrote:
> If anyone is listening, I would very much like for there to be a > mechanism by which external functions can be called "unsafe"-ly, but > without blocking all other Haskell threads. I have code that does this:
+RTS -N2
This doesn't work, which was why the OP asked in the first place. When a thread calls an unsafe foreign function, it blocks everything until that function returns.
Is that true? The last time we discussed this in Haskell Cafe the conclusion I drew from the discussion was that unsafe foreign functions block the current thread but not any other thread. Cheers, Greg

On 23/10/10 17:42, Gregory Crosswhite wrote:
On 10/23/10 7:54 AM, John Lato wrote:
On Fri, Oct 22, 2010 at 6:16 PM, Bulat Ziganshin
mailto:bulat.ziganshin@gmail.com> wrote: Hello John,
Monday, October 18, 2010, 8:15:42 PM, you wrote:
If anyone is listening, I would very much like for there to be a mechanism by which external functions can be called "unsafe"-ly, but without blocking all other Haskell threads. I have code that does this:
+RTS -N2
This doesn't work, which was why the OP asked in the first place. When a thread calls an unsafe foreign function, it blocks everything until that function returns.
Is that true? The last time we discussed this in Haskell Cafe the conclusion I drew from the discussion was that unsafe foreign functions block the current thread but not any other thread.
The conclusion I drew was that "unsafe" foreign functions block the current "capability" (OS thread) and any "threads" (Haskell forkIO etc) currently scheduled on that capability, but other capabilities and threads continue executing as normal. Thanks, Claude -- http://claudiusmaximus.goto10.org

On 10/23/10 12:57 PM, Claude Heiland-Allen wrote:
On 23/10/10 17:42, Gregory Crosswhite wrote:
On 10/23/10 7:54 AM, John Lato wrote:
On Fri, Oct 22, 2010 at 6:16 PM, Bulat Ziganshin
This doesn't work, which was why the OP asked in the first place. When a thread calls an unsafe foreign function, it blocks everything until that function returns.
Is that true? The last time we discussed this in Haskell Cafe the conclusion I drew from the discussion was that unsafe foreign functions block the current thread but not any other thread.
The conclusion I drew was that "unsafe" foreign functions block the current "capability" (OS thread) and any "threads" (Haskell forkIO etc) currently scheduled on that capability, but other capabilities and threads continue executing as normal.
Yes, that is what I really meant to say; thank you for using the correct words. :-) Cheers, Greg

Hello Claude, Saturday, October 23, 2010, 11:57:23 PM, you wrote:
Is that true? The last time we discussed this in Haskell Cafe the conclusion I drew from the discussion was that unsafe foreign functions block the current thread but not any other thread.
The conclusion I drew was that "unsafe" foreign functions block the current "capability" (OS thread) and any "threads" (Haskell forkIO etc) currently scheduled on that capability, but other capabilities and threads continue executing as normal.
yes, it blocks entire capability but afaik only one haskell thread can be scheduled on some capability at any concrete moment -- Best regards, Bulat mailto:Bulat.Ziganshin@gmail.com

Quoth Claude Heiland-Allen
The conclusion I drew was that "unsafe" foreign functions block the current "capability" (OS thread) and any "threads" (Haskell forkIO etc) currently scheduled on that capability, but other capabilities and threads continue executing as normal.
If a trivial test program would help, here I call the sleep() function, which I believe on a POSIX platform suspends the thread until receipt of a SIGALRM. If "unsafe", during the execution of sleep() in one thread, Haskell execution will be blocked in the other, so they will alternate. If "safe", the two sleep intervals will overlap. I believe we all now expect that, but if it does come as a surprise, I hope someone will test it on a more common platform. +RTS -N2 makes no difference. Donn Cave, donn@avvanta.com ----------------------------------- {-# LANGUAGE ForeignFunctionInterface #-} module Main (main) where import Control.Concurrent (forkOS) import Foreign import Foreign.C foreign import ccall unsafe "sleep" sleep :: CInt -> IO CInt rep :: (CInt -> IO CInt) -> CInt -> IO () rep f s = do print s f s rep f s main = do forkOS $ rep sleep 3 rep sleep 1

On 23/10/10 23:17, Donn Cave wrote:
Quoth Claude Heiland-Allen
, ... The conclusion I drew was that "unsafe" foreign functions block the current "capability" (OS thread) and any "threads" (Haskell forkIO etc) currently scheduled on that capability, but other capabilities and threads continue executing as normal.
... until GC time when all capabilities must be ready. (?)
If a trivial test program would help, here I call the sleep() function, which I believe on a POSIX platform suspends the thread until receipt of a SIGALRM.
I wrote a program which shows some interesting behaviour: ----8<---- {-# LANGUAGE ForeignFunctionInterface #-} module Main (main) where import GHC.Conc (forkOnIO, numCapabilities) import Control.Concurrent (threadDelay) import Foreign.C (CInt) import System.Environment (getArgs) foreign import ccall unsafe "sleep" sleep :: CInt -> IO CInt delayer :: Int -> IO () delayer n = do print ("delayer", n) threadDelay 100000 -- 10Hz delayer n sleeper :: Int -> IO () sleeper n = do print ("sleeper", n) _ <- sleep 1 -- 1Hz sleeper n main :: IO () main = do m <- (read . head) `fmap` getArgs mapM_ (\n -> forkOnIO n $ delayer n) [1 .. numCapabilities] mapM_ (\n -> forkOnIO n $ sleeper n) [1 .. numCapabilities - m] threadDelay 100000000 -- 100s ----8<---- $ ghc --version The Glorious Glasgow Haskell Compilation System, version 6.12.3 $ uname -a Linux zebimus 2.6.32-25-generic #44-Ubuntu SMP Fri Sep 17 20:05:27 UTC 2010 x86_64 GNU/Linux $ ghc -O2 -Wall -threaded --make DelayedSleep.hs $ ./DelayedSleep +RTS -N4 -S -RTS 3 [snip] ----8<---- By interesting I mean there is lots of output from the delayer threads on capabilities without sleeper threads (as you would expect), with the delayer threads on capabilities also having sleeper threads being much less frequent (as you might also expect). But then there are some long pauses where there is no output from any thread: my hypothesis is that the whole runtime is blocked waiting for all threads to be ready for GC (because +RTS -S shows some GC stats after the end of those pauses). Claude -- http://claudiusmaximus.goto10.org

-----BEGIN PGP SIGNED MESSAGE----- Hash: SHA1 On 10/22/10 19:16 , Bulat Ziganshin wrote:
Monday, October 18, 2010, 8:15:42 PM, you wrote:
If anyone is listening, I would very much like for there to be a mechanism by which external functions can be called "unsafe"-ly, but without blocking all other Haskell threads. I have code that does this:
+RTS -N2
I think they mean "please don't conflate `reentrant' with `blocking' in the FFI". - -- brandon s. allbery [linux,solaris,freebsd,perl] allbery@kf8nh.com system administrator [openafs,heimdal,too many hats] allbery@ece.cmu.edu electrical and computer engineering, carnegie mellon university KF8NH -----BEGIN PGP SIGNATURE----- Version: GnuPG v2.0.10 (Darwin) Comment: Using GnuPG with Mozilla - http://enigmail.mozdev.org/ iEYEARECAAYFAkzDKmIACgkQIn7hlCsL25WHMwCgktq4XC3Exdij33maBxN9Vu8p jlcAoJhasAIqVbSYo79z+IrY3zp9GVOR =ZmOh -----END PGP SIGNATURE-----

On 10/23/10 2:33 PM, Brandon S Allbery KF8NH wrote:
I think they mean "please don't conflate `reentrant' with `blocking' in the FFI".
Not knowing much about the guts of GHC's implementation of the FFI, I wonder if there would actually be an implementational difference in distinguishing blocking calls vs calls with (potentially) callbacks, or if it's just a terminology problem. Also, we should be distinguishing between C functions which are non-reentrant ---and so we'd want to block all other GHC threads from calling it before the first invocation returns[1]---, vs when the GHC runtime doesn't get itself into a reenterable state before invoking foreign calls. [1] Presumably this should be considered a library problem (e.g., use locks) rather than an FFI problem per se. -- Live well, ~wren
participants (7)
-
Brandon S Allbery KF8NH
-
Bulat Ziganshin
-
Claude Heiland-Allen
-
Donn Cave
-
Gregory Crosswhite
-
John Lato
-
wren ng thornton