Skip to content

Commit 787a196

Browse files
mpickeringBodigrim
authored andcommitted
Add semWaitInterruptible
The semWaitInterruptible function allows us to start a thread that blocks on the semaphore but can be interrupted. (cherry picked from commit 9c50f37)
1 parent bffb886 commit 787a196

File tree

3 files changed

+58
-2
lines changed

3 files changed

+58
-2
lines changed

System/Posix/Semaphore.hsc

Lines changed: 18 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
{-# LANGUAGE Safe #-}
22
{-# LANGUAGE CApiFFI #-}
3+
{-# LANGUAGE InterruptibleFFI #-}
34
-----------------------------------------------------------------------------
45
-- |
56
-- Module : System.Posix.Semaphore
@@ -16,7 +17,7 @@
1617

1718
module System.Posix.Semaphore
1819
(OpenSemFlags(..), Semaphore(),
19-
semOpen, semUnlink, semWait, semTryWait, semThreadWait,
20+
semOpen, semUnlink, semWait, semWaitInterruptible, semTryWait, semThreadWait,
2021
semPost, semGetValue)
2122
where
2223

@@ -86,6 +87,20 @@ semWait (Semaphore fptr) = withForeignPtr fptr semWait'
8687
where semWait' sem = throwErrnoIfMinus1Retry_ "semWait" $
8788
sem_wait sem
8889

90+
-- | Lock the semaphore, blocking until it becomes available.
91+
--
92+
-- Unlike 'semWait', this wait operation can be interrupted with
93+
-- an asynchronous exception (e.g. a call to 'throwTo' from another thread).
94+
semWaitInterruptible :: Semaphore -> IO Bool
95+
semWaitInterruptible (Semaphore fptr) = withForeignPtr fptr semWait'
96+
where semWait' sem =
97+
do res <- sem_wait_interruptible sem
98+
if res == 0 then return True
99+
else do errno <- getErrno
100+
if errno == eINTR
101+
then return False
102+
else throwErrno "semWaitInterrruptible"
103+
89104
-- | Attempt to lock the semaphore without blocking. Immediately return
90105
-- False if it is not available.
91106
semTryWait :: Semaphore -> IO Bool
@@ -145,9 +160,10 @@ foreign import capi safe "semaphore.h sem_close"
145160
sem_close :: Ptr () -> IO CInt
146161
foreign import capi safe "semaphore.h sem_unlink"
147162
sem_unlink :: CString -> IO CInt
148-
149163
foreign import capi safe "semaphore.h sem_wait"
150164
sem_wait :: Ptr () -> IO CInt
165+
foreign import capi interruptible "semaphore.h sem_wait"
166+
sem_wait_interruptible :: Ptr () -> IO CInt
151167
foreign import capi safe "semaphore.h sem_trywait"
152168
sem_trywait :: Ptr () -> IO CInt
153169
foreign import capi safe "semaphore.h sem_post"

tests/SemaphoreInterrupt.hs

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
module Main (main) where
2+
3+
import Control.Concurrent
4+
import Control.Monad
5+
import Data.IORef
6+
import System.Posix
7+
8+
main :: IO ()
9+
main = do
10+
11+
sem <- semOpen "/test" OpenSemFlags {semCreate = True, semExclusive = False} stdFileMode 0
12+
ref <- newIORef False
13+
_ <- forkIO $ do
14+
res <- semWaitInterruptible sem
15+
writeIORef ref res
16+
threadDelay 100000 -- 100 ms
17+
semPost sem
18+
threadDelay 100000 -- 100 ms
19+
succ1 <- readIORef ref
20+
unless succ1 $
21+
error "SemaphoreInterrupt: semWaitInterruptible failed"
22+
23+
writeIORef ref False
24+
tid <- forkIO $ do
25+
res <- semWaitInterruptible sem
26+
writeIORef ref res
27+
threadDelay 100000 -- 100 ms
28+
killThread tid
29+
threadDelay 100000 -- 100 ms
30+
succ2 <- readIORef ref
31+
when succ2 $
32+
error "SemaphoreInterrupt: semWaitInterruptible not interrupted"

unix.cabal

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -270,3 +270,11 @@ test-suite Semaphore002
270270
default-language: Haskell2010
271271
build-depends: base, unix
272272
ghc-options: -Wall -threaded
273+
274+
test-suite SemaphoreInterrupt
275+
hs-source-dirs: tests
276+
main-is: SemaphoreInterrupt.hs
277+
type: exitcode-stdio-1.0
278+
default-language: Haskell2010
279+
build-depends: base, unix
280+
ghc-options: -Wall -threaded

0 commit comments

Comments
 (0)