Skip to content

Concurrency primitimes and thread safety #34

@tomjaguarpaw

Description

@tomjaguarpaw

We don't have any Bluefin-specific concurrency primitives and just rely on MonadUnliftIO to give us access to IO currency primitives. This seems dangerous, firstly in light of #29, but also because we could actually use Bluefin's type system to forbid thread-unsafe access to resources.

By way of comparison, effectful seems fairly lax. See haskell-effectful/effectful#292.

#!/usr/bin/env cabal
{- cabal:
  build-depends: base, effectful==2.5.1.0, async
-}
{-# LANGUAGE GHC2021 #-}
{-# LANGUAGE DataKinds #-}
{-# LANGUAGE LambdaCase #-}

import Control.Concurrent
import Control.Concurrent.Async
import Data.IORef
import Effectful
import Effectful.Dispatch.Dynamic
import Effectful.State.Dynamic

evalState ::
  (IOE :> es) =>
  s ->
  Eff (State s : es) a ->
  Eff es a
evalState s0 m = do
  v <- liftIO (newIORef s0)
  reinterpret id (ioState v) m

ioState ::
  (IOE :> es) =>
  IORef s ->
  LocalEnv localEs es ->
  State s (Eff localEs) a ->
  Eff es a
ioState v env = \case
  Get -> liftIO (readIORef v)
  Put s -> liftIO (writeIORef v s)
  State f -> liftIO $ do
    s <- readIORef v
    let (r, s') = f s
    writeIORef v s
    pure r
  StateM _ -> error "Dunno"

useStateConcurrently ::
  (State Int :> es, IOE :> es) => Eff es ()
useStateConcurrently = do
    withEffToIO (ConcUnlift Persistent Unlimited) $
      \effToIO -> do
        concurrently
          ( effToIO $ do
              liftIO (threadDelay 500)
              s <- get @Int
              put (s + 1)
          )
          ( effToIO $ do
              s <- get @Int
              liftIO (threadDelay 1000)
              put (s * 2)
          )

    (liftIO . print) =<< get @Int

-- We "want" the result to be either
--
-- - 12 (== (5 + 1) * 2), or
--
-- - 11 (== (5 * 2) + 1)
--
-- but we get
--
-- % cabal run test-effectful-thread-unsafe.hs
-- 10
main :: IO ()
main = runEff $ do
  evalState @_ @Int 5 $ do
    useStateConcurrently

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions