From 88f389fc1807aac92b4b026c2b09b2ec6266d05b Mon Sep 17 00:00:00 2001 From: Simon Marlow Date: Wed, 30 Mar 2011 10:05:04 +0000 Subject: [PATCH 1/1] Export the affinity API from Control.Concurrent: forkOn and friends. forkOn :: Int -> IO () -> IO ThreadId forkOnWithUnmask :: Int -> ((forall a . IO a -> IO a) -> IO ()) -> IO ThreadId getNumCapabilities :: IO Int threadCapability :: ThreadId -> IO (Int, Bool) Following discussion on the libraries list, I renamed forkOnIO to forkOn. In due course we might want to also rename forkIO to fork. I left the Int argument as it is, it's quite useful to be able to specify a number to be interpreted modulo the actual number of processors. I also used the term "capability" consistently. It might not be the best choice, but we have to pick something. --- Control/Concurrent.hs | 6 +++++ GHC/Conc.lhs | 3 ++- GHC/Conc/Sync.lhs | 62 +++++++++++++++++++++++++++++++------------------ 3 files changed, 47 insertions(+), 24 deletions(-) diff --git a/Control/Concurrent.hs b/Control/Concurrent.hs index 732255b..62a30b4 100644 --- a/Control/Concurrent.hs +++ b/Control/Concurrent.hs @@ -40,6 +40,12 @@ module Control.Concurrent ( throwTo, #endif + -- ** Threads with affinity + forkOn, + forkOnWithUnmask, + getNumCapabilities, + threadCapability, + -- * Scheduling -- $conc_scheduling diff --git a/GHC/Conc.lhs b/GHC/Conc.lhs index 11a9324..de96b2c 100644 --- a/GHC/Conc.lhs +++ b/GHC/Conc.lhs @@ -32,9 +32,10 @@ module GHC.Conc , forkIO -- :: IO a -> IO ThreadId , forkIOUnmasked , forkIOWithUnmask + , forkOn , forkOnIO -- :: Int -> IO a -> IO ThreadId , forkOnIOUnmasked - , forkOnIOWithUnmask + , forkOnWithUnmask , numCapabilities -- :: Int , getNumCapabilities -- :: IO Int , numSparks -- :: IO Int diff --git a/GHC/Conc/Sync.lhs b/GHC/Conc/Sync.lhs index 027744b..af69a63 100644 --- a/GHC/Conc/Sync.lhs +++ b/GHC/Conc/Sync.lhs @@ -41,9 +41,10 @@ module GHC.Conc.Sync , forkIO -- :: IO a -> IO ThreadId , forkIOUnmasked , forkIOWithUnmask - , forkOnIO -- :: Int -> IO a -> IO ThreadId + , forkOn -- :: Int -> IO a -> IO ThreadId + , forkOnIO -- DEPRECATED , forkOnIOUnmasked - , forkOnIOWithUnmask + , forkOnWithUnmask , numCapabilities -- :: Int , getNumCapabilities -- :: IO Int , numSparks -- :: IO Int @@ -222,35 +223,50 @@ forkIOWithUnmask :: ((forall a . IO a -> IO a) -> IO ()) -> IO ThreadId forkIOWithUnmask io = forkIO (io unsafeUnmask) {- | -Like 'forkIO', but lets you specify on which CPU the thread is -created. Unlike a `forkIO` thread, a thread created by `forkOnIO` -will stay on the same CPU for its entire lifetime (`forkIO` threads -can migrate between CPUs according to the scheduling policy). -`forkOnIO` is useful for overriding the scheduling policy when you -know in advance how best to distribute the threads. - -The `Int` argument specifies the CPU number; it is interpreted modulo -the value returned by 'getNumCapabilities'. +Like 'forkIO', but lets you specify on which processor the thread +should run. Unlike a `forkIO` thread, a thread created by `forkOn` +will stay on the same processor for its entire lifetime (`forkIO` +threads can migrate between processors according to the scheduling +policy). `forkOn` is useful for overriding the scheduling policy when +you know in advance how best to distribute the threads. + +The `Int` argument specifies a /capability number/ (see +'getNumCapabilities'). Typically capabilities correspond to physical +processors, but the exact behaviour is implementation-dependent. The +value passed to 'forkOn' is interpreted modulo the total number of +capabilities as returned by 'getNumCapabilities'. + +GHC note: the number of capabilities is specified by the @+RTS -N@ +option when the program is started. Capabilities can be fixed to +actual processor cores with @+RTS -qa@ if the underlying operating +system supports that, although in practice this is usually unnecessary +(and may actually degrade perforamnce in some cases - experimentation +is recommended). -} -forkOnIO :: Int -> IO () -> IO ThreadId -forkOnIO (I# cpu) action = IO $ \ s -> +forkOn :: Int -> IO () -> IO ThreadId +forkOn (I# cpu) action = IO $ \ s -> case (forkOn# cpu action_plus s) of (# s1, tid #) -> (# s1, ThreadId tid #) where action_plus = catchException action childHandler -{-# DEPRECATED forkOnIOUnmasked "use forkOnIOWithUnmask instead" #-} --- | This function is deprecated; use 'forkOnIOWIthUnmask' instead +{-# DEPRECATED forkOnIO "renamed to forkOn" #-} +-- | This function is deprecated; use 'forkOn' instead +forkOnIO :: Int -> IO () -> IO ThreadId +forkOnIO = forkOn + +{-# DEPRECATED forkOnIOUnmasked "use forkOnWithUnmask instead" #-} +-- | This function is deprecated; use 'forkOnWIthUnmask' instead forkOnIOUnmasked :: Int -> IO () -> IO ThreadId -forkOnIOUnmasked cpu io = forkOnIO cpu (unsafeUnmask io) +forkOnIOUnmasked cpu io = forkOn cpu (unsafeUnmask io) -- | Like 'forkIOWithUnmask', but the child thread is pinned to the --- given CPU, as with 'forkOnIO'. -forkOnIOWithUnmask :: Int -> ((forall a . IO a -> IO a) -> IO ()) -> IO ThreadId -forkOnIOWithUnmask cpu io = forkOnIO cpu (io unsafeUnmask) +-- given CPU, as with 'forkOn'. +forkOnWithUnmask :: Int -> ((forall a . IO a -> IO a) -> IO ()) -> IO ThreadId +forkOnWithUnmask cpu io = forkOn cpu (io unsafeUnmask) -- | the value passed to the @+RTS -N@ flag. This is the number of -- Haskell threads that can run truly simultaneously at any given --- time, and is typically set to the number of physical CPU cores on +-- time, and is typically set to the number of physical processor cores on -- the machine. -- -- Strictly speaking it is better to use 'getNumCapabilities', because @@ -262,12 +278,12 @@ numCapabilities = unsafePerformIO $ getNumCapabilities {- | Returns the number of Haskell threads that can run truly simultaneously (on separate physical processors) at any given time. -The CPU number passed to `forkOnIO` is interpreted modulo this +The number passed to `forkOn` is interpreted modulo this value. An implementation in which Haskell threads are mapped directly to OS threads might return the number of physical processor cores in -the machine, and 'forkOnIO' would be implemented using the OS's +the machine, and 'forkOn' would be implemented using the OS's affinity facilities. An implementation that schedules Haskell threads onto a smaller number of OS threads (like GHC) would return the number of such OS threads that can be running simultaneously. @@ -470,7 +486,7 @@ threadStatus (ThreadId t) = IO $ \s -> -- | returns the number of the capability on which the thread is currently -- running, and a boolean indicating whether the thread is locked to -- that capability or not. A thread is locked to a capability if it --- was created with @forkOnIO@. +-- was created with @forkOn@. threadCapability :: ThreadId -> IO (Int, Bool) threadCapability (ThreadId t) = IO $ \s -> case threadStatus# t s of -- 1.7.10.4