[Haskell-cafe] Is Data.Pool not thread-safe or have I done something silly?
Jeroen Bransen
jeroen at chordify.net
Sun Mar 14 14:26:53 UTC 2021
Hi Cody,
I think part of your confusion may come from the naming of your
functions. Your 'acquire' function is passed as first argument to
createPool, and thus is a 'create' function rather than an acquire
function. As your pool only ever has a single resource, I would expect
that it would be called only once, because once a resource has been
created it can be reused by all threads.
With stackage lts-16.15 I get exactly the behaviour I would expect on my
machine:
acquire 0 - TimeSpec {sec = 221871, nsec = 95964800}
ThreadId 6: processing 0 - TimeSpec {sec = 221881, nsec = 97250800}
ThreadId 8: processing 0 - TimeSpec {sec = 221891, nsec = 98211800}
ThreadId 10: processing 0 - TimeSpec {sec = 221901, nsec = 99347300}
ThreadId 12: processing 0 - TimeSpec {sec = 221911, nsec = 100904500}
ThreadId 14: processing 0 - TimeSpec {sec = 221921, nsec = 102292000}
anything else?
acquire 0 - TimeSpec {sec = 221921, nsec = 442620100}
anything else?
anything else?
First a resource is created, then every 10 seconds a thread completed,
and finally the resource is freed (your logging in release also prints
acquire, but the second instance is from release). I can't run GHC 8.10
yet so not sure what happens there.
Regards,
Jeroen Bransen
Op 13-3-2021 om 02:09 schreef Cody Gman:
> I wrote some code with a Data.Pool that has 1 stripe, 1 max resource, and then basically did `Async.replicateConcurrently_ . withResource $ \res -> f res`.
>
> I expect withResource to block in each of those threads until the first thread spawned is done with that Resource and releases.
>
> To be clear, I get output like:
>
> ```shell
> /tmp $ stack exec --resolver lts-17.5 --package resource-pool --package stm --package hslogger --package time --package concurrent-output -- ghc -O2 -threaded -fwarn-unused-imports testpool.hs && ./testpool
> acquire 0 - .81428
> acquire 1 - .81431
> acquire 2 - .81438
> acquire 3 - .81440
> acquire 4 - .81448
> ThreadId 8: processing 1 - .82460
> ThreadId 17: processing 4 - .82461
> ThreadId 11: processing 2 - .82464
> ThreadId 14: processing 3 - .82464
> ThreadId 5: processing 0 - .82465
> anything else?
> release 4 - .14427
> release 3 - .14430
> release 2 - .14431
> release 1 - .14431
> release 0 - .14432
> anything else?
> anything else?
>
> ```
>
> I expect output like:
>
> ```shell
> /tmp $ stack exec --resolver lts-17.5 --package resource-pool --package stm --package hslogger --package time --package concurrent-output -- ghc -O2 -threaded -fwarn-unused-imports testpool.hs && ./testpool
> [1 of 1] Compiling Main ( testpool.hs, testpool.o )
> Linking testpool ...
> acquire 0
> ThreadId 5: processing 0 - .01129
> release 0
> acquire 1 -- I thought this would have blocked until 0 was released
> ThreadId 8: processing 1 - .01120
> release 1
> acquire 2
> ThreadId 11: processing 2 - .01123
> release 2
> acquire 3
> ThreadId 14: processing 3 - .01129
> release 3
> acquire 4
> ThreadId 17: processing 4 - .01129
> release 4
> anything else?
> anything else?
> anything else?
> ```
>
> Here is the code:
>
> ```
> #!/usr/bin/env stack
> -- stack script --resolver lts-17.5 --package resource-pool --package stm --package hslogger --package time --package concurrent-output
>
> import Control.Concurrent
> import Control.Concurrent.Async
> import Control.Concurrent.STM
> import Data.Pool
> import Data.Time
> import System.Console.Concurrent
> import System.Clock
>
> main :: IO ()
> main = do
> counter <- newTVarIO 0
> let acquire = do
> k <- atomically $ do
> k <- readTVar counter
> writeTVar counter (k + 1)
> return k
> now <- getTime Monotonic
> outputConcurrent ("acquire " ++ show k ++ " - "++ show now ++ "\n")
> return k
> release k = do
> now <- getTime Monotonic
> outputConcurrent ("acquire " ++ show k ++ " - "++ show now ++ "\n")
>
> withConcurrentOutput $ do
> -- create a pool that only allows 1 resource
> pool <- createPool acquire release 1 500 1
> replicateConcurrently_ 5 $ do
> useResourceFor (seconds 10) pool
>
> -- Why do you need these to see the release messages?
> putStrLn "anything else?" >> threadDelay (seconds 5)
> putStrLn "anything else?" >> threadDelay (seconds 5)
> putStrLn "anything else?" >> threadDelay (seconds 5)
>
> useResourceFor waitSeconds pool = withResource pool $ \i -> do
> threadDelay waitSeconds
> tid <- myThreadId
> now <- getTime Monotonic
> outputConcurrent $ show tid <> ": " <> " processing " <> show i <> " - " <> show now <> "\n"
>
> seconds = (* 1000000)
> ```
>
> I'm hoping someone could help explain what I did wrong or confirm it's a bug.
>
> Thanks,
>
> Cody
> _______________________________________________
> Haskell-Cafe mailing list
> To (un)subscribe, modify options or view archives go to:
> http://mail.haskell.org/cgi-bin/mailman/listinfo/haskell-cafe
> Only members subscribed via the mailman list are allowed to post.
--
Jeroen Bransen
Back-end Developer at Chordify
--
<https://chordify.net>
More information about the Haskell-Cafe
mailing list