pthread 0.1.1 → 0.1.2
raw patch · 8 files changed
+470/−85 lines, 8 filesdep +generic-derivingdep +hspecdep +hspec-discoverdep ~basePVP: major bump suggested
API removals or changes: PVP suggests a major version bump
Dependencies added: generic-deriving, hspec, hspec-discover, pthread
Dependency ranges changed: base
API changes (from Hackage documentation)
- Foreign.Concurrent.PThread: data Key
- Foreign.Concurrent.PThread: getSpecific :: Key -> IO (Ptr a)
- Foreign.Concurrent.PThread: instance Foreign.Storable.Storable Foreign.Concurrent.PThread.Key
- Foreign.Concurrent.PThread: instance GHC.Classes.Eq Foreign.Concurrent.PThread.Key
- Foreign.Concurrent.PThread: instance GHC.Classes.Ord Foreign.Concurrent.PThread.Key
- Foreign.Concurrent.PThread: instance GHC.Show.Show Foreign.Concurrent.PThread.Key
- Foreign.Concurrent.PThread: keyCreate :: FunPtr (Ptr a -> IO ()) -> IO Key
- Foreign.Concurrent.PThread: keyDelete :: Key -> IO ()
- Foreign.Concurrent.PThread: setSpecific :: Key -> Ptr a -> IO ()
+ System.Posix.Thread: Attributes :: DetachState -> CSize -> InheritSched -> SchedParam -> SchedPolicy -> Scope -> Ptr () -> CSize -> Attributes
+ System.Posix.Thread: AttributesMonoid :: First DetachState -> First CSize -> First InheritSched -> First SchedParam -> First SchedPolicy -> First Scope -> First (Ptr ()) -> First CSize -> AttributesMonoid
+ System.Posix.Thread: [$sel:detachState:AttributesMonoid] :: AttributesMonoid -> First DetachState
+ System.Posix.Thread: [$sel:detachState:Attributes] :: Attributes -> DetachState
+ System.Posix.Thread: [$sel:guardSize:AttributesMonoid] :: AttributesMonoid -> First CSize
+ System.Posix.Thread: [$sel:guardSize:Attributes] :: Attributes -> CSize
+ System.Posix.Thread: [$sel:inheritSched:AttributesMonoid] :: AttributesMonoid -> First InheritSched
+ System.Posix.Thread: [$sel:inheritSched:Attributes] :: Attributes -> InheritSched
+ System.Posix.Thread: [$sel:schedParam:AttributesMonoid] :: AttributesMonoid -> First SchedParam
+ System.Posix.Thread: [$sel:schedParam:Attributes] :: Attributes -> SchedParam
+ System.Posix.Thread: [$sel:schedPolicy:AttributesMonoid] :: AttributesMonoid -> First SchedPolicy
+ System.Posix.Thread: [$sel:schedPolicy:Attributes] :: Attributes -> SchedPolicy
+ System.Posix.Thread: [$sel:scope:AttributesMonoid] :: AttributesMonoid -> First Scope
+ System.Posix.Thread: [$sel:scope:Attributes] :: Attributes -> Scope
+ System.Posix.Thread: [$sel:stack:AttributesMonoid] :: AttributesMonoid -> First (Ptr ())
+ System.Posix.Thread: [$sel:stack:Attributes] :: Attributes -> Ptr ()
+ System.Posix.Thread: [$sel:stackSize:AttributesMonoid] :: AttributesMonoid -> First CSize
+ System.Posix.Thread: [$sel:stackSize:Attributes] :: Attributes -> CSize
+ System.Posix.Thread: cancel :: ThreadId -> IO ()
+ System.Posix.Thread: create :: IO (Ptr a) -> IO ThreadId
+ System.Posix.Thread: createKey :: FunPtr (Ptr a -> IO ()) -> IO Key
+ System.Posix.Thread: createKey_ :: IO Key
+ System.Posix.Thread: createWithAttributes :: AttributesMonoid -> IO (Ptr a) -> IO ThreadId
+ System.Posix.Thread: createWithAttributes_ :: AttributesMonoid -> IO () -> IO ThreadId
+ System.Posix.Thread: create_ :: IO () -> IO ThreadId
+ System.Posix.Thread: data Attributes
+ System.Posix.Thread: data AttributesMonoid
+ System.Posix.Thread: data Key
+ System.Posix.Thread: deleteKey :: Key -> IO ()
+ System.Posix.Thread: detach :: ThreadId -> IO ()
+ System.Posix.Thread: exit :: Ptr a -> IO ()
+ System.Posix.Thread: exit_ :: IO ()
+ System.Posix.Thread: getSpecific :: Key -> IO (Ptr a)
+ System.Posix.Thread: instance Foreign.Storable.Storable System.Posix.Thread.Attributes
+ System.Posix.Thread: instance Foreign.Storable.Storable System.Posix.Thread.AttributesMonoid
+ System.Posix.Thread: instance Foreign.Storable.Storable System.Posix.Thread.Key
+ System.Posix.Thread: instance Foreign.Storable.Storable System.Posix.Thread.SchedParam
+ System.Posix.Thread: instance Foreign.Storable.Storable System.Posix.Thread.ThreadId
+ System.Posix.Thread: instance GHC.Base.Monoid System.Posix.Thread.AttributesMonoid
+ System.Posix.Thread: instance GHC.Base.Semigroup System.Posix.Thread.AttributesMonoid
+ System.Posix.Thread: instance GHC.Classes.Eq System.Posix.Thread.DetachState
+ System.Posix.Thread: instance GHC.Classes.Eq System.Posix.Thread.InheritSched
+ System.Posix.Thread: instance GHC.Classes.Eq System.Posix.Thread.Key
+ System.Posix.Thread: instance GHC.Classes.Eq System.Posix.Thread.SchedParam
+ System.Posix.Thread: instance GHC.Classes.Eq System.Posix.Thread.SchedPolicy
+ System.Posix.Thread: instance GHC.Classes.Eq System.Posix.Thread.Scope
+ System.Posix.Thread: instance GHC.Classes.Eq System.Posix.Thread.ThreadId
+ System.Posix.Thread: instance GHC.Classes.Ord System.Posix.Thread.Key
+ System.Posix.Thread: instance GHC.Classes.Ord System.Posix.Thread.ThreadId
+ System.Posix.Thread: instance GHC.Enum.Enum System.Posix.Thread.DetachState
+ System.Posix.Thread: instance GHC.Enum.Enum System.Posix.Thread.InheritSched
+ System.Posix.Thread: instance GHC.Enum.Enum System.Posix.Thread.SchedPolicy
+ System.Posix.Thread: instance GHC.Enum.Enum System.Posix.Thread.Scope
+ System.Posix.Thread: instance GHC.Exception.Type.Exception System.Posix.Thread.ThreadNotBound
+ System.Posix.Thread: instance GHC.Generics.Generic System.Posix.Thread.Attributes
+ System.Posix.Thread: instance GHC.Generics.Generic System.Posix.Thread.AttributesMonoid
+ System.Posix.Thread: instance GHC.Show.Show System.Posix.Thread.Attributes
+ System.Posix.Thread: instance GHC.Show.Show System.Posix.Thread.AttributesMonoid
+ System.Posix.Thread: instance GHC.Show.Show System.Posix.Thread.DetachState
+ System.Posix.Thread: instance GHC.Show.Show System.Posix.Thread.InheritSched
+ System.Posix.Thread: instance GHC.Show.Show System.Posix.Thread.Key
+ System.Posix.Thread: instance GHC.Show.Show System.Posix.Thread.SchedParam
+ System.Posix.Thread: instance GHC.Show.Show System.Posix.Thread.SchedPolicy
+ System.Posix.Thread: instance GHC.Show.Show System.Posix.Thread.Scope
+ System.Posix.Thread: instance GHC.Show.Show System.Posix.Thread.ThreadId
+ System.Posix.Thread: instance GHC.Show.Show System.Posix.Thread.ThreadNotBound
+ System.Posix.Thread: join :: ThreadId -> IO (Ptr a)
+ System.Posix.Thread: myThreadId :: IO ThreadId
+ System.Posix.Thread: setSpecific :: Key -> Ptr a -> IO ()
Files
- CHANGELOG.md +10/−2
- LICENSE +2/−2
- pthread.cabal +21/−5
- src/Foreign/Concurrent/PThread.hsc +0/−76
- src/System/Posix/Thread.hsc +399/−0
- tests/Main.hs +7/−0
- tests/Spec.hs +1/−0
- tests/System/Posix/ThreadSpec.hs +30/−0
CHANGELOG.md view
@@ -4,13 +4,21 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/). -## [0.1.1] - 2017-09-08+## [0.1.2] - 2019-06-17 +### Added++* Compatibility with ghc-8.6.+* Bind API for attributes and basic thread operations.+* Check for -threaded.++## [0.1.1] - 2017-09-28+ ### Changed * Fixed the URL to the pthread repo in the cabal file. -## [0.1] - 2017-09-08+## [0.1] - 2017-09-28 ### Added
LICENSE view
@@ -1,5 +1,5 @@-Copyright EURL Tweag (c) 2017-Copyright LeapYear Tecnologies (c) 2017+Copyright (c) 2017 Tweag I/O Limited+Copyright (c) 2017 LeapYear Tecnologies All rights reserved.
pthread.cabal view
@@ -1,13 +1,13 @@ name: pthread-version: 0.1.1+version: 0.1.2 synopsis: Bindings for the pthread library. homepage: http://github.com/tweag/pthread license: BSD3 license-file: LICENSE author: Tweag I/O maintainer: facundo.dominguez@tweag.io-copyright: 2017 EURL Tweag- 2017 LeapYear Technologies.+copyright: © 2017 EURL Tweag,+ © 2017 LeapYear Technologies. category: FFI, System build-type: Simple cabal-version: >=1.10@@ -23,7 +23,23 @@ hs-source-dirs: src extra-libraries: pthread exposed-modules:- Foreign.Concurrent.PThread+ System.Posix.Thread build-depends:- base > 4.9.0.0 && < 5+ base >= 4.9.0.0 && < 5,+ generic-deriving >= 1.11 default-language: Haskell2010++test-suite spec+ type: exitcode-stdio-1.0+ hs-source-dirs: tests+ main-is: Main.hs+ other-modules:+ System.Posix.ThreadSpec+ Spec+ build-depends:+ base,+ hspec >= 2.2,+ hspec-discover >= 2.2,+ pthread+ default-language: Haskell2010+ ghc-options: -threaded -rtsopts
− src/Foreign/Concurrent/PThread.hsc
@@ -1,76 +0,0 @@--- | PThread bindings--{-# LANGUAGE CApiFFI #-}-{-# LANGUAGE ForeignFunctionInterface #-}-{-# LANGUAGE GeneralizedNewtypeDeriving #-}--- see comment on the imports of Data.Int and Data.Word-{-# OPTIONS_GHC -Wno-unused-imports #-}-module Foreign.Concurrent.PThread- ( -- * thread local storage- Key- , keyCreate- , keyDelete- , setSpecific- , getSpecific- ) where--import Control.Concurrent (isCurrentThreadBound, rtsSupportsBoundThreads)-import Control.Monad ((>=>), unless, when)-import Foreign.C.Types-import Foreign.Marshal.Alloc (alloca)-import Foreign.Ptr-import Foreign.Storable---- These two might be used depending on what the Key representation expands to.-import Data.Int-import Data.Word--#include <pthread.h>---- | Stands for @pthread_key_t@.-newtype Key = Key #{type pthread_key_t}- deriving (Eq, Ord, Show, Storable)--- We check in cbits/checks.c that the size of pthread_key_t fits unsigned int.--foreign import capi unsafe "pthread.h"- pthread_key_create :: Ptr Key -> FunPtr (Ptr a -> IO ()) -> IO CInt---- | Stands for @pthread_key_create@.-keyCreate :: FunPtr (Ptr a -> IO ()) -> IO Key-keyCreate destructor = alloca $ \keyPtr ->- pthread_key_create keyPtr destructor >>= checkReturnCode >> peek keyPtr--foreign import capi unsafe "pthread.h"- pthread_key_delete :: Key -> IO CInt---- | Stands for @pthread_key_delete@.-keyDelete :: Key -> IO ()-keyDelete = pthread_key_delete >=> checkReturnCode--foreign import capi unsafe "pthread.h"- pthread_setspecific :: Key -> Ptr a -> IO CInt---- | Stands for @pthread_setspecific@.-setSpecific :: Key -> Ptr a -> IO ()-setSpecific k v = checkBoundness >> pthread_setspecific k v >>= checkReturnCode--foreign import capi unsafe "pthread.h"- pthread_getspecific :: Key -> IO (Ptr a)---- | Stands for @pthread_getspecific@.-getSpecific :: Key -> IO (Ptr a)-getSpecific k = do- checkBoundness- pthread_getspecific k---- | Yields an error if the calling thread is not bound.-checkBoundness :: IO ()-checkBoundness = when rtsSupportsBoundThreads $ do- bound <- isCurrentThreadBound- unless bound $- fail "pthread: checkBoundness: Calling thread is not bound"---- | Yields an error if the passed integer is not zero.-checkReturnCode :: CInt -> IO ()-checkReturnCode rc = when (rc /= 0) $- fail $ "pthread: checkReturnCode: non-zero return code: " ++ show rc
+ src/System/Posix/Thread.hsc view
@@ -0,0 +1,399 @@+-- | Bindings to the POSIX threads library.+--+-- Requires linking with the @-threaded@ RTS.++{-# LANGUAGE CApiFFI #-}+{-# LANGUAGE DeriveGeneric #-}+{-# LANGUAGE DuplicateRecordFields #-}+{-# LANGUAGE ForeignFunctionInterface #-}+{-# LANGUAGE GeneralizedNewtypeDeriving #-}+{-# LANGUAGE RecordWildCards #-}++-- see comment on the imports of Data.Int and Data.Word+{-# OPTIONS_GHC -Wno-unused-imports #-}++module System.Posix.Thread+ ( -- * Threads+ create+ , create_+ , createWithAttributes+ , createWithAttributes_+ , exit+ , exit_+ , cancel+ , join+ , detach+ , myThreadId+ -- * Attributes+ , Attributes(..)+ , AttributesMonoid(..)+ -- * Thread local storage+ , Key+ , createKey+ , createKey_+ , deleteKey+ , setSpecific+ , getSpecific+ ) where++import Control.Concurrent (isCurrentThreadBound, rtsSupportsBoundThreads)+import Control.Exception (Exception, bracket_, throwIO)+import Control.Monad (forM_, unless, when)+import Data.Monoid (First(..))+import Data.Semigroup (Semigroup(..))+import Foreign.C.Types+import Foreign.C.Error (Errno(..), errnoToIOError)+import Foreign.Marshal.Alloc (alloca)+import Foreign.Ptr (FunPtr, Ptr, castPtr, nullFunPtr, nullPtr)+import Foreign.Storable+import Generics.Deriving.Monoid (memptydefault, mappenddefault)+import GHC.Generics (Generic)+import GHC.Stack (HasCallStack, callStack, getCallStack, prettySrcLoc)++-- These two might be used depending on what the Key representation expands to.+import Data.Int+import Data.Word++#include <pthread.h>++newtype ThreadId = ThreadId #{type pthread_t}+ deriving (Eq, Ord, Show, Storable)++foreign import capi unsafe "pthread.h" pthread_create+ :: Ptr ThreadId -> Ptr AttributesMonoid -> FunPtr (Ptr a -> IO b) -> Ptr a -> IO CInt++foreign import ccall "wrapper" wrap :: (Ptr a -> IO (Ptr b)) -> IO (FunPtr (Ptr a -> IO (Ptr b)))++-- | Create a new thread.+create :: IO (Ptr a) -> IO ThreadId+create action =+ alloca $ \tidPtr -> do+ unless rtsSupportsBoundThreads (error "Use -threaded RTS.")+ fptr <- wrap $ \_ -> action+ throwIfNonZero_ $ pthread_create tidPtr nullPtr fptr nullPtr+ peek tidPtr++-- | Like 'create', but with an 'IO' computation that returns nothing.+create_ :: IO () -> IO ThreadId+create_ action = create (action >> return nullPtr)++foreign import capi unsafe "pthread.h" pthread_attr_init+ :: Ptr AttributesMonoid -> IO CInt+foreign import capi unsafe "pthread.h" pthread_attr_destroy+ :: Ptr AttributesMonoid -> IO CInt++-- | Create a new thread.+createWithAttributes+ :: AttributesMonoid+ -> IO (Ptr a) -- ^ Created thread runs this IO computation.+ -> IO ThreadId+createWithAttributes attrs action =+ alloca $ \tidPtr ->+ alloca $ \attrsPtr -> do+ unless rtsSupportsBoundThreads (error "Use -threaded RTS.")+ bracket_+ (throwIfNonZero_ $ pthread_attr_init attrsPtr)+ (throwIfNonZero_ $ pthread_attr_destroy attrsPtr) $ do+ poke attrsPtr attrs+ fptr <- wrap $ \_ -> action+ throwIfNonZero_ $ pthread_create tidPtr attrsPtr fptr nullPtr+ peek tidPtr++-- | Like 'createWithAttributes', but with an 'IO' computation that returns+-- nothing.+createWithAttributes_ :: AttributesMonoid -> IO () -> IO ThreadId+createWithAttributes_ attrs action =+ createWithAttributes attrs (action >> return nullPtr)++foreign import capi safe "pthread.h" pthread_exit :: Ptr a -> IO ()++-- | Terminate calling thread.+exit :: Ptr a -> IO ()+exit = pthread_exit++-- | Like 'exit', but don't return anything.+exit_ :: IO ()+exit_ = exit nullPtr++foreign import capi safe "pthread.h" pthread_cancel :: ThreadId -> IO CInt++-- | Send a cancellation request to a thread.+cancel :: ThreadId -> IO ()+cancel tid = throwIfNonZero_ $ pthread_cancel tid++foreign import capi safe "pthread.h" pthread_join+ :: ThreadId -> Ptr (Ptr a) -> IO CInt++-- | Join with a terminated thread.+join :: ThreadId -> IO (Ptr a)+join tid = alloca $ \ptr -> do+ throwIfNonZero_ $ pthread_join tid ptr+ peek ptr++foreign import capi unsafe "pthread.h" pthread_detach :: ThreadId -> IO CInt++-- | Detach a thread.+detach :: ThreadId -> IO ()+detach tid = throwIfNonZero_ $ pthread_detach tid++foreign import capi unsafe "pthread.h" pthread_self :: IO ThreadId++-- | Obtain ID of the calling thread.+myThreadId :: IO ThreadId+myThreadId = pthread_self++data DetachState = Detached | Joinable+ deriving (Eq, Show)++instance Enum DetachState where+ toEnum #{const PTHREAD_CREATE_DETACHED} = Detached+ toEnum #{const PTHREAD_CREATE_JOINABLE} = Joinable+ toEnum _ = error "Invalid detach state attribute value"++ fromEnum Detached = #{const PTHREAD_CREATE_DETACHED}+ fromEnum Joinable = #{const PTHREAD_CREATE_JOINABLE}++data InheritSched = InheritSched | ExplicitSched+ deriving (Eq, Show)++instance Enum InheritSched where+ toEnum #{const PTHREAD_INHERIT_SCHED} = InheritSched+ toEnum #{const PTHREAD_EXPLICIT_SCHED} = ExplicitSched+ toEnum _ = error "Invalid inherit-scheduler attribute value"++ fromEnum InheritSched = #{const PTHREAD_INHERIT_SCHED}+ fromEnum ExplicitSched = #{const PTHREAD_EXPLICIT_SCHED}++data SchedParam = SchedParam+ { schedPriority :: Int32+ } deriving (Eq, Show)++instance Storable SchedParam where+ sizeOf _ = #{size struct sched_param}+ alignment _ = #{alignment struct sched_param}+ peek ptr =+ SchedParam <$>+ (fromIntegral <$> (#{peek struct sched_param, sched_priority} ptr :: IO CInt))+ poke ptr SchedParam{..} =+ #{poke struct sched_param, sched_priority} ptr (fromIntegral schedPriority :: CInt)++data SchedPolicy = SchedFIFO | SchedRR | SchedOther+ deriving (Eq, Show)++instance Enum SchedPolicy where+ toEnum #{const SCHED_FIFO} = SchedFIFO+ toEnum #{const SCHED_RR} = SchedRR+ toEnum #{const SCHED_OTHER} = SchedOther+ toEnum _ = error "Invalid scheduling policy attribute value"++ fromEnum SchedFIFO = #{const SCHED_FIFO}+ fromEnum SchedRR = #{const SCHED_RR}+ fromEnum SchedOther = #{const SCHED_OTHER}++data Scope = ScopeSystem | ScopeProcess+ deriving (Eq, Show)++instance Enum Scope where+ toEnum #{const PTHREAD_SCOPE_SYSTEM} = ScopeSystem+ toEnum #{const PTHREAD_SCOPE_PROCESS} = ScopeProcess+ toEnum _ = error "Invalid scope attribute value"++ fromEnum ScopeSystem = #{const PTHREAD_SCOPE_SYSTEM}+ fromEnum ScopeProcess = #{const PTHREAD_SCOPE_PROCESS}++-- | Thread attributes.+data Attributes = Attributes+ { detachState :: DetachState+ , guardSize :: CSize+ , inheritSched :: InheritSched+ , schedParam :: SchedParam+ , schedPolicy :: SchedPolicy+ , scope :: Scope+ , stack :: Ptr ()+ , stackSize :: CSize+ } deriving (Generic, Show)++-- | Partial set of thread attributes. Think of it as a diff to apply to the+-- default attributes object.+data AttributesMonoid = AttributesMonoid+ { detachState :: First DetachState+ , guardSize :: First CSize+ , inheritSched :: First InheritSched+ , schedParam :: First SchedParam+ , schedPolicy :: First SchedPolicy+ , scope :: First Scope+ , stack :: First (Ptr ())+ , stackSize :: First CSize+ } deriving (Generic, Show)++instance Semigroup AttributesMonoid where+ (<>) = mappenddefault++instance Monoid AttributesMonoid where+ mempty = memptydefault+ mappend = (<>)++monoidFromAttributes :: Attributes -> AttributesMonoid+monoidFromAttributes Attributes{..} =+ AttributesMonoid+ { detachState = return detachState+ , guardSize = return guardSize+ , inheritSched = return inheritSched+ , schedParam = return schedParam+ , schedPolicy = return schedPolicy+ , scope = return scope+ , stack = return stack+ , stackSize = return stackSize+ }++foreign import capi unsafe "pthread.h" pthread_attr_getdetachstate+ :: Ptr Attributes -> Ptr CInt -> IO CInt+foreign import capi unsafe "pthread.h" pthread_attr_getguardsize+ :: Ptr Attributes -> Ptr CSize -> IO CInt+foreign import capi unsafe "pthread.h" pthread_attr_getinheritsched+ :: Ptr Attributes -> Ptr CInt -> IO CInt+foreign import capi unsafe "pthread.h" pthread_attr_getschedparam+ :: Ptr Attributes -> Ptr SchedParam -> IO CInt+foreign import capi unsafe "pthread.h" pthread_attr_getschedpolicy+ :: Ptr Attributes -> Ptr CInt -> IO CInt+foreign import capi unsafe "pthread.h" pthread_attr_getscope+ :: Ptr Attributes -> Ptr CInt -> IO CInt+foreign import capi unsafe "pthread.h" pthread_attr_getstack+ :: Ptr Attributes -> Ptr (Ptr ()) -> Ptr CSize -> IO CInt++foreign import capi unsafe "pthread.h" pthread_attr_setdetachstate+ :: Ptr AttributesMonoid -> CInt -> IO CInt+foreign import capi unsafe "pthread.h" pthread_attr_setguardsize+ :: Ptr AttributesMonoid -> CSize -> IO CInt+foreign import capi unsafe "pthread.h" pthread_attr_setinheritsched+ :: Ptr AttributesMonoid -> CInt -> IO CInt+foreign import capi unsafe "pthread.h" pthread_attr_setschedparam+ :: Ptr AttributesMonoid -> Ptr SchedParam -> IO CInt+foreign import capi unsafe "pthread.h" pthread_attr_setschedpolicy+ :: Ptr AttributesMonoid -> CInt -> IO CInt+foreign import capi unsafe "pthread.h" pthread_attr_setscope+ :: Ptr AttributesMonoid -> CInt -> IO CInt+foreign import capi unsafe "pthread.h" pthread_attr_setstack+ :: Ptr AttributesMonoid -> Ptr () -> CSize -> IO CInt+foreign import capi unsafe "pthread.h" pthread_attr_setstacksize+ :: Ptr AttributesMonoid -> CSize -> IO CInt++instance Storable AttributesMonoid where+ sizeOf _ = #{size pthread_attr_t}+ alignment _ = #{alignment pthread_attr_t}++ peek attr = monoidFromAttributes <$> peek (castPtr attr)++ poke attr AttributesMonoid{..} = do+ forM_ detachState $ \x ->+ throwIfNonZero_ $ pthread_attr_setdetachstate attr (enum x)+ forM_ guardSize $ \x ->+ throwIfNonZero_ $ pthread_attr_setguardsize attr x+ forM_ inheritSched $ \x ->+ throwIfNonZero_ $ pthread_attr_setinheritsched attr (enum x)+ forM_ schedParam $ \x ->+ throwIfNonZero_ $ alloca $ \sp -> do+ poke sp x+ pthread_attr_setschedparam attr sp+ forM_ schedPolicy $ \x ->+ throwIfNonZero_ $ pthread_attr_setschedpolicy attr (enum x)+ forM_ scope $ \x ->+ throwIfNonZero_ $ pthread_attr_setscope attr (enum x)+ case (stack, stackSize) of+ (First (Just x), First (Just y)) ->+ throwIfNonZero_ $ pthread_attr_setstack attr x y+ (First Nothing, First (Just y)) ->+ throwIfNonZero_ $ pthread_attr_setstacksize attr y+ _ -> return ()+ where+ enum :: Enum a => a -> CInt+ enum = fromIntegral . fromEnum++instance Storable Attributes where+ sizeOf _ = #{size pthread_attr_t}+ alignment _ = #{alignment pthread_attr_t}++ peek attr = do+ detachState <- enum <$> alloca (\x -> pthread_attr_getdetachstate attr x >> peek x)+ guardSize <- alloca (\x -> pthread_attr_getguardsize attr x >> peek x)+ inheritSched <- enum <$> alloca (\x -> pthread_attr_getinheritsched attr x >> peek x)+ schedParam <- alloca (\x -> pthread_attr_getschedparam attr x >> peek x)+ schedPolicy <- enum <$> alloca (\x -> pthread_attr_getschedpolicy attr x >> peek x)+ scope <- enum <$> alloca (\x -> pthread_attr_getscope attr x >> peek x)+ (stack, stackSize) <-+ alloca $ \x -> alloca $ \y -> do+ _ <- pthread_attr_getstack attr x y+ (,) <$> peek x <*> peek y+ return Attributes{..}+ where+ enum :: Enum a => CInt -> a+ enum = toEnum . fromIntegral++ poke attr Attributes{..} = poke (castPtr attr) (monoidFromAttributes Attributes{..})++-- | Opaque objects used to locate thread-specific data.+newtype Key = Key #{type pthread_key_t}+ deriving (Eq, Ord, Show, Storable)+-- We check in cbits/checks.c that the size of pthread_key_t fits unsigned int.++foreign import capi unsafe "pthread.h"+ pthread_key_create :: Ptr Key -> FunPtr (Ptr a -> IO ()) -> IO CInt++-- | Thread-specific data key creation.+createKey+ :: FunPtr (Ptr a -> IO ()) -- ^ Finalizer+ -> IO Key+createKey destructor = alloca $ \keyPtr -> do+ throwIfNonZero_ $ pthread_key_create keyPtr destructor+ peek keyPtr++-- | Like 'createKey', but with no finalizer.+createKey_ :: IO Key+createKey_ = createKey nullFunPtr++foreign import capi unsafe "pthread.h"+ pthread_key_delete :: Key -> IO CInt++-- | Thread-specific data key deletion.+deleteKey :: Key -> IO ()+deleteKey k = throwIfNonZero_ $ pthread_key_delete k++foreign import capi unsafe "pthread.h"+ pthread_setspecific :: Key -> Ptr a -> IO CInt++-- | Associate a thread-specific /value/ with a /key/ obtained via a previous+-- call to 'createKey'.+setSpecific :: Key -> Ptr a -> IO ()+setSpecific k v = do+ checkBoundness+ throwIfNonZero_ $ pthread_setspecific k v++foreign import capi unsafe "pthread.h"+ pthread_getspecific :: Key -> IO (Ptr a)++-- | Return the value currently bound to the specified key on behalf of the+-- calling thread.+getSpecific :: Key -> IO (Ptr a)+getSpecific k = do+ checkBoundness+ pthread_getspecific k++data ThreadNotBound = ThreadNotBound++instance Exception ThreadNotBound++instance Show ThreadNotBound where+ show _ = "Calling thread is not bound"++-- | Yields an error if the calling thread is not bound.+checkBoundness :: IO ()+checkBoundness = when rtsSupportsBoundThreads $ do+ bound <- isCurrentThreadBound+ unless bound $ throwIO ThreadNotBound++-- | Yields an error if the passed integer is not zero.+throwIfNonZero_ :: HasCallStack => IO CInt -> IO ()+throwIfNonZero_ m = m >>= \rc -> when (rc /= 0) $+ ioError (errnoToIOError (prettySrcLoc loc) (Errno rc) Nothing Nothing)+ where+ (_, loc):_ = getCallStack callStack
+ tests/Main.hs view
@@ -0,0 +1,7 @@+module Main where++import qualified Spec+import Test.Hspec++main :: IO ()+main = hspec Spec.spec
+ tests/Spec.hs view
@@ -0,0 +1,1 @@+{-# OPTIONS_GHC -F -pgmF hspec-discover -optF --module-name=Spec #-}
+ tests/System/Posix/ThreadSpec.hs view
@@ -0,0 +1,30 @@+{-# LANGUAGE ScopedTypeVariables #-}++module System.Posix.ThreadSpec (spec) where++import Control.Monad (forM_, replicateM, replicateM_)+import Foreign.Ptr (Ptr)+import Foreign.Marshal.Alloc (alloca)+import qualified System.Posix.Thread as Posix+import Test.Hspec++spec :: Spec+spec = do+ describe "create" $ do+ it "creates 1 threads successfully" $ do+ tid <- Posix.create_ (return ())+ Posix.detach tid+ it "creates 1000 threads successfully" $ do+ replicateM_ 1000 $ do+ tid <- Posix.create_ (return ())+ Posix.detach tid+ describe "join" $ do+ it "Joins 1000 threads successfully" $ do+ alloca $ \(ptr :: Ptr Int) -> do+ tids <- replicateM 1000 $ Posix.create (return ptr)+ forM_ tids $ \tid -> Posix.join tid `shouldReturn` ptr+ describe "exit" $ do+ it "Exits 1000 threads successfully" $ do+ replicateM_ 1000 $ do+ tid <- Posix.create_ Posix.exit_+ Posix.detach tid