From a5da3ba990ea54389dcd9fa45687b359ac543946 Mon Sep 17 00:00:00 2001 From: Mitchell Rosen Date: Mon, 27 Nov 2023 20:54:40 -0500 Subject: [PATCH] depend on `int-supply` --- ki/CHANGELOG.md | 20 ++++++++----- ki/ki.cabal | 2 +- ki/src/Ki/Internal/Counter.hs | 56 ----------------------------------- ki/src/Ki/Internal/Scope.hs | 15 +++++----- 4 files changed, 21 insertions(+), 72 deletions(-) delete mode 100644 ki/src/Ki/Internal/Counter.hs diff --git a/ki/CHANGELOG.md b/ki/CHANGELOG.md index 63bd062..b397dc2 100644 --- a/ki/CHANGELOG.md +++ b/ki/CHANGELOG.md @@ -1,8 +1,12 @@ -## [1.0.1.1] - 2023-10-10 +## [1.0.1.2] - Unreleased + +- Refactor: depend on (rather than inline) `int-supply` package + +## [1.0.1.1] - October 10, 2023 - Compat: support GHC 9.8.1 -## [1.0.1.0] - 2023-04-03 +## [1.0.1.0] - April 3, 2023 - Change [#25](https://github.com/awkward-squad/ki/pull/25): Attempting to fork a thread in a closing scope now acts as if it were a child being terminated due to the scope closing. Previously, attempting to fork a thread in a closing @@ -10,16 +14,16 @@ - Change [#27](https://github.com/awkward-squad/ki/pull/27): Calling `awaitAll` on a closed scope now returns `()` instead of blocking forever. -## [1.0.0.2] - 2023-01-25 +## [1.0.0.2] - January 25, 2023 - Bugfix [#20](https://github.com/awkward-squad/ki/pull/20): previously, a child thread could deadlock when attempting to propagate an exception to its parent -## [1.0.0.1] - 2022-08-14 +## [1.0.0.1] - August 14, 2022 - Compat: support GHC 9.4.1 -## [1.0.0] - 2022-06-30 +## [1.0.0] - June 30, 2022 - Breaking: Remove `Context` type, `Ki.Implicit` module, and the ability to soft-cancel a `Scope`. - Breaking: Remove `Duration` type and its associated API, including `waitFor` and `awaitFor`. @@ -40,17 +44,17 @@ - Performance: Use atomic fetch-and-add rather than a `TVar` to track internal child thread ids. -## [0.2.0] - 2020-12-17 +## [0.2.0] - December 17, 2020 - Breaking: Remove `ThreadFailed` exception wrapper. - Breaking: Rename `cancelScope` to `cancel`. -## [0.1.0.1] - 2020-11-30 +## [0.1.0.1] - November 30, 2020 - Misc: Replace `AtomicCounter` with `Int` to drop the `atomic-primops` dependency. - Bounds: Lower `cabal-version` from 3.0 to 2.2 because `stack` cannot parse 3.0. -## [0.1.0] - 2020-11-11 +## [0.1.0] - November 11, 2020 - Initial release. diff --git a/ki/ki.cabal b/ki/ki.cabal index 8db524a..f74c0bd 100644 --- a/ki/ki.cabal +++ b/ki/ki.cabal @@ -83,12 +83,12 @@ library import: component build-depends: containers ^>= 0.6 || ^>= 0.7, + int-supply ^>= 1.0.0, exposed-modules: Ki hs-source-dirs: src other-modules: Ki.Internal.ByteCount - Ki.Internal.Counter Ki.Internal.IO Ki.Internal.Scope Ki.Internal.Thread diff --git a/ki/src/Ki/Internal/Counter.hs b/ki/src/Ki/Internal/Counter.hs deleted file mode 100644 index a0ee9ec..0000000 --- a/ki/src/Ki/Internal/Counter.hs +++ /dev/null @@ -1,56 +0,0 @@ --- Some code modified from the atomic-primops library; license included below. --- Copyright (c)2012-2013, Ryan R. Newton --- --- All rights reserved. --- --- Redistribution and use in source and binary forms, with or without --- modification, are permitted provided that the following conditions are met: --- --- * Redistributions of source code must retain the above copyright --- notice, this list of conditions and the following disclaimer. --- --- * 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. --- --- * Neither the name of Ryan R. Newton nor the names of other --- contributors may be used to endorse or promote products derived --- from this software without specific prior written permission. -{-# LANGUAGE MagicHash #-} -{-# LANGUAGE UnboxedTuples #-} - -module Ki.Internal.Counter - ( Counter, - newCounter, - incrCounter, - ) -where - -import Data.Bits -import GHC.Base - --- | A thread-safe counter implemented with atomic fetch-and-add. -data Counter - = Counter (MutableByteArray# RealWorld) - --- | Create a new counter initialized to 0. -newCounter :: IO Counter -newCounter = - IO \s0# -> - case newByteArray# size s0# of - (# s1#, arr# #) -> - case writeIntArray# arr# 0# 0# s1# of - s2# -> (# s2#, Counter arr# #) - where - !(I# size) = - finiteBitSize (undefined :: Int) `div` 8 -{-# INLINE newCounter #-} - --- | Increment a counter and return the value prior to incrementing. -incrCounter :: Counter -> IO Int -incrCounter (Counter arr#) = - IO \s0# -> - case fetchAddIntArray# arr# 0# 1# s0# of - (# s1#, n# #) -> (# s1#, I# n# #) -{-# INLINE incrCounter #-} diff --git a/ki/src/Ki/Internal/Scope.hs b/ki/src/Ki/Internal/Scope.hs index 2dccd5f..8c65f7a 100644 --- a/ki/src/Ki/Internal/Scope.hs +++ b/ki/src/Ki/Internal/Scope.hs @@ -48,7 +48,8 @@ import GHC.Conc import GHC.Conc.Sync (readTVarIO) import GHC.IO (unsafeUnmask) import Ki.Internal.ByteCount -import Ki.Internal.Counter +import IntSupply (IntSupply) +import qualified IntSupply import Ki.Internal.IO ( IOResult (..), UnexceptionalIO (..), @@ -84,8 +85,8 @@ data Scope = Scope childExceptionVar :: {-# UNPACK #-} !(MVar SomeException), -- The set of child threads that are currently running, each keyed by a monotonically increasing int. childrenVar :: {-# UNPACK #-} !(TVar (IntMap ThreadId)), - -- The counter that holds the (int) key to use for the next child thread. - nextChildIdCounter :: {-# UNPACK #-} !Counter, + -- The supply that holds the (int) key to use for the next child thread. + nextChildIdSupply :: {-# UNPACK #-} !IntSupply, -- The id of the thread that created the scope, which is considered the parent of all threads created within it. parentThreadId :: {-# UNPACK #-} !ThreadId, statusVar :: {-# UNPACK #-} !(TVar ScopeStatus) @@ -210,16 +211,16 @@ allocateScope :: IO Scope allocateScope = do childExceptionVar <- newEmptyMVar childrenVar <- newTVarIO IntMap.Lazy.empty - nextChildIdCounter <- newCounter + nextChildIdSupply <- IntSupply.new parentThreadId <- myThreadId statusVar <- newTVarIO 0 - pure Scope {childExceptionVar, childrenVar, nextChildIdCounter, parentThreadId, statusVar} + pure Scope {childExceptionVar, childrenVar, nextChildIdSupply, parentThreadId, statusVar} -- Spawn a thread in a scope, providing it its child id and a function that sets the masking state to the requested -- masking state. The given action is called with async exceptions interruptibly masked. spawn :: Scope -> ThreadOptions -> (Tid -> (forall x. IO x -> IO x) -> UnexceptionalIO ()) -> IO ThreadId spawn - Scope {childrenVar, nextChildIdCounter, statusVar} + Scope {childrenVar, nextChildIdSupply, statusVar} ThreadOptions {affinity, allocationLimit, label, maskingState = requestedChildMaskingState} action = do -- Interruptible mask is enough so long as none of the STM operations below block. @@ -236,7 +237,7 @@ spawn Closing -> throwSTM ScopeClosing Closed -> throwSTM (ErrorCall "ki: scope closed") - childId <- incrCounter nextChildIdCounter + childId <- IntSupply.next nextChildIdSupply childThreadId <- forkWithAffinity affinity do