Why does forkIO also create native threads?

I wrote a simple ECHO server like this: ---------------------------------------------------- import Network import System.IO import Control.Concurrent service cs = do ln <- hGetContents cs hPutStr cs ln service cs acceptloop ls = do (cs, host, port) <- accept ls hSetBuffering cs LineBuffering forkIO (service cs) acceptloop ls main = do ls <- listenOn (PortNumber 10061) acceptloop ls ---------------------------------------------------- And tested it with a client that initiates as many parallel connections as possible. The number of connections can just reach to 1000+ on my machine, but I thought it should be more, since forkIO generates lightweight threads. I also examined the number of threads in this process with some tool and it proved there was one native thread per connection. When I replaced the 'forkIO' in the program with 'forkOS', I got an exactly similar result. Have I thought something wrong about forkIO?

It seems that it's not the forkIO that is creating native threads (using GHC
6.10.1, -O flag)
For example, the following code only has 5 threads on my Windows box, while
I fork 10 lightweight threads
import System.IO
import Control.Concurrent
import Control.Monad
entry :: Char -> IO()
entry c = do
forever $ putChar c
main = do
hSetBuffering stdout NoBuffering
mapM_ (forkIO . entry) ['0'..'9']
putStrLn "all forked; press ENTER to quit"
getLine
However, things can get bizarre. When we change the entry function into
entry :: Char -> IO()
entry c = do
putChar c
threadDelay maxBound
Then on my machine 13 native threads *are* created. This might make sense
because its waiting forever, but native threads are also created for the
following code:
entry :: Char -> IO()
entry c = do
putChar c
forever $ threadDelay 0
This feels like overkill, so it seems threadDelay always forks a new native
thread, although this is not documented.
We can also get into the dark corners of GHC's lightweight thread
scheduling: a thread switch only happens when memory is allocated on the GC
head (but this is documented in the API)
So the following code will not work, the runtime seems to get stuck in an
infinite loop after the first thread is created.
entry :: Char -> IO()
entry c = do
putChar c
forever $ return ()
In artificial cases like this you can use yield
entry :: Char -> IO()
entry c = do
putChar c
forever yield
On Mon, Apr 13, 2009 at 10:37 AM, Lych
I wrote a simple ECHO server like this: ---------------------------------------------------- import Network import System.IO import Control.Concurrent
service cs = do ln <- hGetContents cs hPutStr cs ln service cs
acceptloop ls = do (cs, host, port) <- accept ls hSetBuffering cs LineBuffering forkIO (service cs) acceptloop ls
main = do ls <- listenOn (PortNumber 10061) acceptloop ls ----------------------------------------------------
And tested it with a client that initiates as many parallel connections as possible. The number of connections can just reach to 1000+ on my machine, but I thought it should be more, since forkIO generates lightweight threads. I also examined the number of threads in this process with some tool and it proved there was one native thread per connection. When I replaced the 'forkIO' in the program with 'forkOS', I got an exactly similar result. Have I thought something wrong about forkIO? _______________________________________________ Beginners mailing list Beginners@haskell.org http://www.haskell.org/mailman/listinfo/beginners
participants (2)
-
Lych
-
Peter Verswyvelen