packages feed

companion (empty) → 0.1.0

raw patch · 6 files changed

+161/−0 lines, 6 filesdep +basedep +riosetup-changed

Dependencies added: base, rio

Files

+ CHANGELOG.md view
@@ -0,0 +1,11 @@+# Changelog for `companion`
+
+All notable changes to this project will be documented in this file.
+
+The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
+and this project adheres to the
+[Haskell Package Versioning Policy](https://pvp.haskell.org/).
+
+## 0.1.0 - 2023-07-07
+
+* Spin out module `Pantry.Internal.Companion` from package `pantry-0.8.3`.
+ LICENSE view
@@ -0,0 +1,28 @@+BSD 3-Clause License
+
+Copyright (c) 2015-2023, Stack contributors
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+1. Redistributions of source code must retain the above copyright notice, this
+   list of conditions and the following disclaimer.
+
+2. Redistributions in binary form must reproduce the above copyright notice,
+   this list of conditions and the following disclaimer in the documentation
+   and/or other materials provided with the distribution.
+
+3. Neither the name of the copyright holder nor the names of its
+   contributors may be used to endorse or promote products derived from
+   this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ README.md view
@@ -0,0 +1,3 @@+# companion
+
+A Haskell library to provide companion threads.
+ Setup.hs view
@@ -0,0 +1,2 @@+import Distribution.Simple
+main = defaultMain
+ companion.cabal view
@@ -0,0 +1,39 @@+cabal-version: 1.12
++-- This file has been generated from package.yaml by hpack version 0.35.2.+--+-- see: https://github.com/sol/hpack++name:           companion+version:        0.1.0+synopsis:       A Haskell library to provide companion threads.+description:    Please see the README on GitHub at <https://github.com/commercialhaskell/companion#readme>+category:       Concurrency+homepage:       https://github.com/commercialhaskell/companion#readme+bug-reports:    https://github.com/commercialhaskell/companion/issues+author:         Michael Snoyman+maintainer:     Mike Pilgrem <public@pilgrem.com>+copyright:      2018-2023 FP Complete+license:        BSD3+license-file:   LICENSE+build-type:     Simple+extra-source-files:+    README.md+    CHANGELOG.md++source-repository head+  type: git+  location: https://github.com/commercialhaskell/companion++library+  exposed-modules:+      Control.Concurrent.Companion+  other-modules:+      Paths_companion+  hs-source-dirs:+      src+  ghc-options: -Wall+  build-depends:+      base >=4.12 && <5+    , rio+  default-language: Haskell2010
+ src/Control/Concurrent/Companion.hs view
@@ -0,0 +1,78 @@+{-# LANGUAGE NoImplicitPrelude #-}
+{-# LANGUAGE RankNTypes        #-}
+
+-- | Companion threads, such as for printing messages saying we're still busy.
+module Control.Concurrent.Companion
+  ( withCompanion
+  , onCompanionDone
+  , Companion
+  , Delay
+  , StopCompanion
+  ) where
+
+import           RIO
+
+-- | A companion thread which can perform arbitrary actions as well as delay.
+type Companion m = Delay -> m ()
+
+-- | Delay the given number of microseconds. If 'StopCompanion' is triggered
+-- before the timer completes, a 'CompanionDone' exception will be thrown (which
+-- is caught internally by 'withCompanion').
+type Delay = forall mio. MonadIO mio => Int -> mio ()
+
+-- | Tell the 'Companion' to stop. The next time 'Delay' is called, or if a
+-- 'Delay' is currently blocking, the 'Companion' thread will exit with a
+-- 'CompanionDone' exception.
+type StopCompanion m = m ()
+
+-- | When a delay was interrupted because we're told to stop, perform this
+-- action.
+onCompanionDone
+  :: MonadUnliftIO m
+  => m () -- ^ the delay
+  -> m () -- ^ action to perform
+  -> m ()
+onCompanionDone theDelay theAction =
+  theDelay `withException` \CompanionDone -> theAction
+
+-- | Internal exception used by 'withCompanion' to allow short-circuiting of the
+-- 'Companion'. Should not be used outside of this module.
+data CompanionDone = CompanionDone
+  deriving (Show, Typeable)
+
+instance Exception CompanionDone
+
+-- | Keep running the 'Companion' action until either the inner action completes
+-- or calls the 'StopCompanion' action. This can be used to give the user status
+-- information while running a long running operations.
+withCompanion ::
+     forall m a. MonadUnliftIO m
+  => Companion m
+  -> (StopCompanion m -> m a)
+  -> m a
+withCompanion companion inner = do
+  -- Variable to indicate 'Delay'ing should result in a 'CompanionDone'
+  -- exception.
+  shouldStopVar <- newTVarIO False
+  let -- Relatively simple: set shouldStopVar to True
+      stopCompanion = atomically $ writeTVar shouldStopVar True
+
+      delay :: Delay
+      delay usec = do
+        -- Register a delay with the runtime system
+        delayDoneVar <- registerDelay usec
+        join $ atomically $
+          -- Delay has triggered, keep going
+          (pure () <$ (readTVar delayDoneVar >>= checkSTM)) <|>
+          -- Time to stop the companion, throw a 'CompanionDone' exception
+          -- immediately
+          (throwIO CompanionDone <$ (readTVar shouldStopVar >>= checkSTM))
+
+  -- Run the 'Companion' and inner action together
+  runConcurrently $
+    -- Ignore a 'CompanionDone' exception from the companion, that's expected
+    -- behavior
+    Concurrently (companion delay `catch` \CompanionDone -> pure ()) *>
+    -- Run the inner action, giving it the 'StopCompanion' action, and
+    -- ensuring it is called regardless of exceptions.
+    Concurrently (inner stopCompanion `finally` stopCompanion)