From 8b0eb7c91a1ad16a67f25cbcd6f8391b39f73bfd Mon Sep 17 00:00:00 2001 From: Jens Nolte <git@queezle.net> Date: Fri, 2 Sep 2022 01:41:51 +0200 Subject: [PATCH] Add draft for MonadSTM and limiting STM capabilities Co-authored-by: Jan Beinke <git@janbeinke.com> --- cabal.project | 1 + flake.nix | 3 + quasar/quasar.cabal | 1 + stm-ltd/src/Control/Concurrent/STM/Class.hs | 131 ++++++++++++++++++++ stm-ltd/stm-ltd.cabal | 96 ++++++++++++++ 5 files changed, 232 insertions(+) create mode 100644 stm-ltd/src/Control/Concurrent/STM/Class.hs create mode 100644 stm-ltd/stm-ltd.cabal diff --git a/cabal.project b/cabal.project index 5ee18b8..054da09 100644 --- a/cabal.project +++ b/cabal.project @@ -1,2 +1,3 @@ packages: quasar + stm-ltd diff --git a/flake.nix b/flake.nix index bb423ac..a9ff650 100644 --- a/flake.nix +++ b/flake.nix @@ -24,12 +24,14 @@ quasar = pkgs.haskellPackages.quasar; quasar_ghc92 = (getHaskellPackages "ghc92.").quasar; quasar_ghc94 = (getHaskellPackages "ghc94.").quasar; + stm-ltd = pkgs.haskellPackages.stm-ltd; } ); overlay = final: prev: { haskell = prev.haskell // { packageOverrides = hfinal: hprev: prev.haskell.packageOverrides hfinal hprev // { + stm-ltd = hfinal.callCabal2nix "stm-ltd" ./stm-ltd {}; quasar = hfinal.callCabal2nix "quasar" ./quasar {}; }; }; @@ -41,6 +43,7 @@ in pkgs.mkShell { inputsFrom = [ self.packages.${system}.quasar.env + self.packages.${system}.stm-ltd.env ]; packages = [ pkgs.cabal-install diff --git a/quasar/quasar.cabal b/quasar/quasar.cabal index 4d33432..a71b688 100644 --- a/quasar/quasar.cabal +++ b/quasar/quasar.cabal @@ -79,6 +79,7 @@ library heaps, mtl, stm, + stm-ltd, time, transformers, transformers-base, diff --git a/stm-ltd/src/Control/Concurrent/STM/Class.hs b/stm-ltd/src/Control/Concurrent/STM/Class.hs new file mode 100644 index 0000000..b53543b --- /dev/null +++ b/stm-ltd/src/Control/Concurrent/STM/Class.hs @@ -0,0 +1,131 @@ +{-# LANGUAGE StandaloneKindSignatures #-} +{-# LANGUAGE UndecidableInstances #-} + +module Control.Concurrent.STM.Class ( + MonadSTM, + liftSTM, + MonadSTM'(..), + CanBlock, + CanThrow, + STM', + runSTM', + unsafeLimitSTM, +) where + +import Control.Applicative +import Control.Concurrent.STM (STM, TVar) +import Control.Concurrent.STM qualified as STM +import Control.Monad.Catch +import Control.Monad.Trans.Reader +import Control.Monad.Trans.Writer +import Data.Kind (Type, Constraint) +import Prelude + +data BlockMode = CanBlock +data ThrowMode = CanThrow + +type CanBlock :: BlockMode +type CanBlock = 'CanBlock + +type CanThrow :: ThrowMode +type CanThrow = 'CanThrow + +type STM' :: BlockMode -> ThrowMode -> Type -> Type +newtype STM' b t a = STM' (STM a) + deriving newtype (Functor, Applicative, Monad) + +instance MonadThrow (STM' b CanThrow) where + throwM ex = STM' (throwM ex) + +instance MonadCatch (STM' b CanThrow) where + catch = catchSTM + +instance Semigroup a => Semigroup (STM' b t a) where + (<>) = liftA2 (<>) + +instance Monoid a => Monoid (STM' b t a) where + mempty = pure mempty + + +type MonadSTM' :: BlockMode -> ThrowMode -> (Type -> Type) -> Constraint +class MonadSTM' b t m | m -> b, m -> t where + liftSTM' :: STM' b t a -> m a + + +type MonadSTM = MonadSTM' CanBlock CanThrow + +liftSTM :: MonadSTM m => STM a -> m a +liftSTM fn = liftSTM' (unsafeLimitSTM fn) + + +instance MonadSTM' CanBlock CanThrow STM where + liftSTM' = runSTM' + +instance MonadSTM' b t (STM' b t) where + liftSTM' = id + +instance MonadSTM' b t m => MonadSTM' b t (ReaderT r m) where + liftSTM' = undefined + +instance MonadSTM' b t m => MonadSTM' b t (WriterT w m) where + liftSTM' = undefined + + +--class MonadRetry m where +-- retry :: m a +-- +--instance MonadSTM' CanBlock t m => MonadRetry m where +-- retry = unsafeLimitSTM STM.retry + + +runSTM' :: STM' b t a -> STM a +runSTM' (STM' fn) = fn + + + +unsafeLimitSTM :: (MonadSTM' b t m) => STM a -> m a +unsafeLimitSTM fn = liftSTM' (STM' fn) + + +readTVar :: MonadSTM' b t m => TVar a -> m a +readTVar var = unsafeLimitSTM (STM.readTVar var) + +retry :: MonadSTM' CanBlock t m => m a +retry = unsafeLimitSTM STM.retry + +throwSTM :: (MonadSTM' b CanThrow m, Exception e) => e -> m a +throwSTM = unsafeLimitSTM . STM.throwSTM + +catchSTM :: (MonadSTM' b t m, Exception e) => STM' b CanThrow a -> (e -> STM' b t a) -> m a +catchSTM (STM' fx) fn = unsafeLimitSTM $ catch (unsafeLimitSTM fx) (\ex -> runSTM' (fn ex)) + + +--foobar :: STM' CanBlock t () +--foobar = do +-- var <- unsafeLimitSTM $ STM.newTVar True +-- _x <- readTVar var +-- retry +-- +--foobar' :: STM' CanBlock CanThrow () +--foobar' = do +-- catchSTM (throwM (userError "foobar")) (\(_ :: SomeException) -> traceM "caught") +-- retry +-- +--catchphrase :: STM' CanBlock t () +--catchphrase = catchSTM (throwM (userError "foobar")) (\(_ :: SomeException) -> traceM "caught") +-- +--rethrow :: STM' b CanThrow () +--rethrow = catchSTM (throwM (userError "foobar")) (\(ex :: SomeException) -> throwM ex) +-- +--foobar_' :: ReaderT Int (STM' CanBlock CanThrow) () +--foobar_' = do +-- retry +-- +--foobar'' :: STM () +--foobar'' = do +-- runSTM' retry +-- +--foobar''' :: ReaderT Int STM () +--foobar''' = do +-- _var <- liftSTM (STM.newTVar False) +-- retry diff --git a/stm-ltd/stm-ltd.cabal b/stm-ltd/stm-ltd.cabal new file mode 100644 index 0000000..d85c6b3 --- /dev/null +++ b/stm-ltd/stm-ltd.cabal @@ -0,0 +1,96 @@ +cabal-version: 3.0 + +name: stm-ltd +version: 0.1.0.0 +license: BSD-2-Clause +build-type: Simple + +--source-repository head +-- type: git +-- location: https://github.com/?/stm-ltd.git + +common shared-properties + default-extensions: + AllowAmbiguousTypes + BangPatterns + BlockArguments + ConstraintKinds + DataKinds + DefaultSignatures + DeriveAnyClass + DeriveGeneric + DerivingStrategies + DisambiguateRecordFields + DuplicateRecordFields + ExistentialQuantification + FlexibleContexts + FlexibleInstances + FunctionalDependencies + GADTs + GeneralizedNewtypeDeriving + ImportQualifiedPost + InstanceSigs + LambdaCase + -- Enable once 9.0.1 is required + --LexicalNegation + MultiParamTypeClasses + NamedFieldPuns + NoImplicitPrelude + NumericUnderscores + OverloadedStrings + PolyKinds + RankNTypes + ScopedTypeVariables + StandaloneDeriving + TupleSections + TypeApplications + TypeFamilies + TypeOperators + ViewPatterns + default-language: Haskell2010 + ghc-options: + -Weverything + -Wno-all-missed-specialisations + -Wno-missing-safe-haskell-mode + -Wno-missing-kind-signatures + -Wno-missing-import-lists + -Wno-unsafe + -Werror=incomplete-patterns + -Werror=missing-fields + -Werror=missing-home-modules + -Werror=missing-methods + +common shared-executable-properties + import: shared-properties + ghc-options: + -threaded + -rtsopts + "-with-rtsopts=-N -I0" + +library + import: shared-properties + build-depends: + base, + exceptions, + stm, + transformers, + exposed-modules: + Control.Concurrent.STM.Class + -- Control.Concurrent.STM.Class.Unsafe + other-modules: + -- Control.Concurrent.STM.Class.Internal + hs-source-dirs: + src + +--test-suite stm-ltd-test +-- import: shared-executable-properties +-- type: exitcode-stdio-1.0 +-- build-depends: +-- base, +-- hspec, +-- stm-ltd +-- main-is: Spec.hs +-- other-modules: +-- Control.Concurrent.STM.ClassSpec +-- hs-source-dirs: +-- test -- GitLab