Skip to content

Commit 39c330c

Browse files
bgamariBodigrim
authored andcommitted
semaphore: Teach semThreadWait to use semWait with threaded RTS
semThreadWait uses a rather atrocious polling loop to avoid blocking, which we block the entire program when using the non-threaded runtime. However, this is unnecessary in the threaded runtime, where we can instead simply block in semWait. Take advantage of this. Fixes #253.
1 parent f76875d commit 39c330c

File tree

4 files changed

+40
-4
lines changed

4 files changed

+40
-4
lines changed

System/Posix/Semaphore.hsc

Lines changed: 16 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,13 @@ import Foreign.Marshal
3939
import Foreign.Storable
4040
#endif
4141

42+
#if __GLASGOW_HASKELL__ >= 902
43+
import System.Posix.Internals (hostIsThreaded)
44+
#else
45+
hostIsThreaded :: Bool
46+
hostIsThreaded = False
47+
#endif
48+
4249
data OpenSemFlags = OpenSemFlags { semCreate :: Bool,
4350
-- ^ If true, create the semaphore if it
4451
-- does not yet exist.
@@ -96,9 +103,15 @@ semTryWait (Semaphore fptr) = withForeignPtr fptr semTrywait'
96103
-- semWait, this will block only the current thread rather than the
97104
-- entire process.
98105
semThreadWait :: Semaphore -> IO ()
99-
semThreadWait sem = do res <- semTryWait sem
100-
(if res then return ()
101-
else ( do { yield; semThreadWait sem } ))
106+
semThreadWait sem
107+
-- N.B. semWait can be safely used in the case of the threaded runtime, where
108+
-- the safe foreign call will be performed in its own thread, thereby not
109+
-- blocking the process.
110+
| hostIsThreaded = semWait sem
111+
| otherwise = do
112+
res <- semTryWait sem
113+
if res then return ()
114+
else do yield >> semThreadWait sem
102115

103116
-- | Unlock the semaphore.
104117
semPost :: Semaphore -> IO ()

test-wasm32-wasi.mjs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ const my_execFile = util.promisify(child_process.execFile);
88
let warns_count = 0;
99
for (const f of await fs.promises.readdir("tests")) {
1010
// odd linker errors
11-
if (f === "Semaphore001.hs") continue;
11+
if (f.startsWith('Semaphore')) continue;
1212
// Find self-contained test cases (aka doesn't rely on tasty)
1313
if (!f.endsWith(".hs")) continue;
1414
const s = await fs.promises.readFile(`tests/${f}`, "utf-8");

tests/Semaphore002.hs

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
module Main (main) where
2+
3+
import Control.Concurrent
4+
import System.Posix
5+
6+
main :: IO ()
7+
main = do
8+
sem <- semOpen "/test" OpenSemFlags {semCreate = True, semExclusive = False} stdFileMode 0
9+
forkIO $ do
10+
threadDelay (1000*1000)
11+
semPost sem
12+
13+
-- This should succeed after 1 second.
14+
semThreadWait sem
15+
semPost sem

unix.cabal

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -262,3 +262,11 @@ test-suite Semaphore001
262262
default-language: Haskell2010
263263
build-depends: base, unix
264264
ghc-options: -Wall
265+
266+
test-suite Semaphore002
267+
hs-source-dirs: tests
268+
main-is: Semaphore002.hs
269+
type: exitcode-stdio-1.0
270+
default-language: Haskell2010
271+
build-depends: base, unix
272+
ghc-options: -Wall -threaded

0 commit comments

Comments
 (0)