packages feed

concurrent-supply 0.1.5 → 0.1.7

raw patch · 6 files changed

+202/−160 lines, 6 filesdep +concurrent-supplydep +containersdep ~basePVP ok

version bump matches the API change (PVP)

Dependencies added: concurrent-supply, containers

Dependency ranges changed: base

API changes (from Hackage documentation)

Files

CHANGELOG.markdown view
@@ -1,3 +1,7 @@+0.1.7+-----+* `-fno-full-laziness` was added to work around changes in the GHC inliner.+ 0.1.5 ----- * Claim to be Trustworthy
− Control/Concurrent/Supply.hs
@@ -1,154 +0,0 @@-{-# LANGUAGE MagicHash, UnboxedTuples, CPP #-}-#if defined(__GLASGOW_HASKELL__) && __GLASGOW_HASKELL__ >= 702-{-# LANGUAGE Trustworthy #-}-#endif--------------------------------------------------------------------------------- |--- Module      :  Control.Concurrent.Supply--- Copyright   :  (C) 2011 Edward Kmett,--- License     :  BSD-style (see the file LICENSE)------ Maintainer  :  Edward Kmett <ekmett@gmail.com>--- Stability   :  provisional--- Portability :  portable------ A fast unique identifier supply with local pooling and replay--- support.------ One often has a desire to generate a bunch of integer identifiers within--- a single process that are unique within that process. You could use--- UUIDs, but they can be expensive to generate; you don't want to have--- your threads contending for a single external counter if the identifier--- is not going to be used outside the process.------ @concurrent-supply@ builds a tree-like structure which can be split; you--- can make smaller unique supplies and then you allocate from your supplies--- locally. Internally it pulls from a unique supply one block at a time as--- you walk into parts of the tree that haven't been explored.---------------------------------------------------------------------------------module Control.Concurrent.Supply-  ( Supply-  -- * Variables-  , newSupply-  , freshId-  , splitSupply-  -- * Unboxed API-  , freshId#-  , splitSupply#-  ) where--import Data.Hashable-import Data.IORef-import Data.Functor ((<$>))-import Data.Monoid-import GHC.IO (unsafeDupablePerformIO)-import GHC.Types (Int(..))-import GHC.Prim (Int#)--infixr 5 :--data Stream a = a :- Stream a--instance Functor Stream where-  fmap f (a :- as) = f a :- fmap f as--extract :: Stream a -> a-extract (a :- _) = a--units :: Stream ()-units = () :- units--data Block = Block Int !(Stream Block)--instance Eq Block where-  Block a (Block b _ :- _) == Block c (Block d _ :- _) = a == c && b == d--instance Ord Block where-  Block a (Block b _ :- _) `compare` Block c (Block d _ :- _) = compare a c `mappend` compare b d--instance Show Block where-  showsPrec d (Block a (Block b _ :- _)) = showParen (d >= 10) $-    showString "Block " . showsPrec 10 a . showString " (Block " . showsPrec 10 b . showString " ... :- ...)"--instance Hashable Block where-  hashWithSalt s (Block a (Block b _ :- _)) = s `hashWithSalt` a `hashWithSalt` b--blockSize :: Int-blockSize = 1024-{-# INLINE blockSize #-}---- Minimum size to be worth splitting a supply rather than just CAS'ing twice to avoid multiple subsequent biased splits-minSplitSupplySize :: Int-minSplitSupplySize = 32 -- based on sqrt blockSize-{-# INLINE minSplitSupplySize #-}--blockCounter :: IORef Int-blockCounter = unsafeDupablePerformIO $ newIORef 0-{-# NOINLINE blockCounter #-}--modifyBlock :: () -> IO Int-modifyBlock _ = atomicModifyIORef blockCounter $ \ i -> let i' = i + blockSize in i' `seq` (i', i)-{-# NOINLINE modifyBlock #-}--gen :: () -> Block-gen x = Block (unsafeDupablePerformIO (modifyBlock x)) (gen <$> units)-{-# NOINLINE gen #-}--newBlock :: IO Block-newBlock = return $ gen ()-{-# NOINLINE newBlock #-}--splitBlock# :: Block -> (# Block, Block #)-splitBlock# (Block i (x :- xs)) = (# x, Block i xs #)-{-# INLINE splitBlock# #-}---- | A user managed globally unique variable supply.-data Supply = Supply {-# UNPACK #-} !Int {-# UNPACK #-} !Int Block-  deriving (Eq,Ord,Show)--instance Hashable Supply where-  hashWithSalt s (Supply i j b) = s `hashWithSalt` i `hashWithSalt` j `hashWithSalt` b--blockSupply :: Block -> Supply-blockSupply (Block i bs) = Supply i (i + blockSize - 1) (extract bs)-{-# INLINE blockSupply #-}---- | Grab a new supply. Any two supplies obtained with newSupply are guaranteed to return--- disjoint sets of identifiers. Replaying the same sequence of operations on the same--- Supply will yield the same results.-newSupply :: IO Supply-newSupply = blockSupply <$> newBlock-{-# INLINE newSupply #-}---- | Obtain a fresh Id from a Supply.-freshId :: Supply -> (Int, Supply)-freshId s = case freshId# s of-  (# i, s' #) -> (I# i, s')-{-# INLINE freshId #-}---- | Split a supply into two supplies that will return disjoint identifiers-splitSupply :: Supply -> (Supply, Supply)-splitSupply s = case splitSupply# s of-  (# l, r #) -> (l, r)-{-# INLINE splitSupply #-}---- | An unboxed version of freshId-freshId# :: Supply -> (# Int#, Supply #)-freshId# (Supply i@(I# i#) j b)-  | i /= j = (# i#, Supply (i + 1) j b #)-  | otherwise = case b of-    Block k b' -> (# i#, Supply k (k + blockSize - 1) (extract b') #)-{-# INLINE freshId# #-}---- | An unboxed version of splitSupply-splitSupply# :: Supply -> (# Supply, Supply #)-splitSupply# (Supply i k b) = case splitBlock# b of-    (# bl, br #)-      | k - i >= minSplitSupplySize-      , j <- i + div (k - i) 2 ->-        (# Supply i j bl, Supply (j + 1) k br #)-      | Block x (l :- r :- _) <- bl-      , y <- x + div blockSize 2-      , z <- x + blockSize - 1 ->-        (# Supply x (y - 1) l, Supply y z r #)-{-# INLINE splitSupply# #-}
LICENSE view
@@ -1,4 +1,4 @@-Copyright 2011 Edward Kmett+Copyright 2011-2013 Edward Kmett  All rights reserved. @@ -12,10 +12,6 @@ 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 author nor the names of his contributors-   may be used to endorse or promote products derived from this software-   without specific prior written permission.  THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
concurrent-supply.cabal view
@@ -1,6 +1,6 @@ name:          concurrent-supply category:      Concurrency, Parallelism-version:       0.1.5+version:       0.1.7 license:       BSD3 cabal-version: >= 1.10 license-file:  LICENSE@@ -22,8 +22,13 @@   type: git   location: git://github.com/ekmett/concurrent-supply.git +flag test-properties+  manual: True+  default: True+ library   default-language: Haskell2010+  hs-source-dirs: src   other-extensions: MagicHash, UnboxedTuples   exposed-modules:     Control.Concurrent.Supply@@ -32,3 +37,18 @@     base     >= 4 && < 5,     hashable >= 1.1 && < 1.3,     ghc-prim >= 0.2 && < 0.4++test-suite properties+  type: exitcode-stdio-1.0+  main-is: properties.hs+  default-language: Haskell2010+  ghc-options: -w -threaded -rtsopts -with-rtsopts=-N+  hs-source-dirs: test++  if !flag(test-properties)+    buildable: False+  else+    build-depends:+      base,+      concurrent-supply,+      containers
+ src/Control/Concurrent/Supply.hs view
@@ -0,0 +1,155 @@+{-# LANGUAGE MagicHash, UnboxedTuples, CPP #-}+#if defined(__GLASGOW_HASKELL__) && __GLASGOW_HASKELL__ >= 702+{-# LANGUAGE Trustworthy #-}+#endif+{-# OPTIONS_GHC -fno-full-laziness #-}+-----------------------------------------------------------------------------+-- |+-- Module      :  Control.Concurrent.Supply+-- Copyright   :  (C) 2011-2013 Edward Kmett+-- License     :  BSD-style (see the file LICENSE)+--+-- Maintainer  :  Edward Kmett <ekmett@gmail.com>+-- Stability   :  provisional+-- Portability :  portable+--+-- A fast unique identifier supply with local pooling and replay+-- support.+--+-- One often has a desire to generate a bunch of integer identifiers within+-- a single process that are unique within that process. You could use+-- UUIDs, but they can be expensive to generate; you don't want to have+-- your threads contending for a single external counter if the identifier+-- is not going to be used outside the process.+--+-- @concurrent-supply@ builds a tree-like structure which can be split; you+-- can make smaller unique supplies and then you allocate from your supplies+-- locally. Internally it pulls from a unique supply one block at a time as+-- you walk into parts of the tree that haven't been explored.+--+----------------------------------------------------------------------------+module Control.Concurrent.Supply+  ( Supply+  -- * Variables+  , newSupply+  , freshId+  , splitSupply+  -- * Unboxed API+  , freshId#+  , splitSupply#+  ) where++import Data.Hashable+import Data.IORef+import Data.Functor ((<$>))+import Data.Monoid+import GHC.IO (unsafeDupablePerformIO, unsafePerformIO)+import GHC.Types (Int(..))+import GHC.Prim (Int#)++infixr 5 :-+data Stream a = a :- Stream a++instance Functor Stream where+  fmap f (a :- as) = f a :- fmap f as++extract :: Stream a -> a+extract (a :- _) = a++units :: Stream ()+units = () :- units+{-# NOINLINE units #-}++data Block = Block Int !(Stream Block)++instance Eq Block where+  Block a (Block b _ :- _) == Block c (Block d _ :- _) = a == c && b == d++instance Ord Block where+  Block a (Block b _ :- _) `compare` Block c (Block d _ :- _) = compare a c `mappend` compare b d++instance Show Block where+  showsPrec d (Block a (Block b _ :- _)) = showParen (d >= 10) $+    showString "Block " . showsPrec 10 a . showString " (Block " . showsPrec 10 b . showString " ... :- ...)"++instance Hashable Block where+  hashWithSalt s (Block a (Block b _ :- _)) = s `hashWithSalt` a `hashWithSalt` b++blockSize :: Int+blockSize = 1024+{-# INLINE blockSize #-}++-- Minimum size to be worth splitting a supply rather than just CAS'ing twice to avoid multiple subsequent biased splits+minSplitSupplySize :: Int+minSplitSupplySize = 32 -- based on sqrt blockSize+{-# INLINE minSplitSupplySize #-}++blockCounter :: IORef Int+blockCounter = unsafePerformIO (newIORef 0)+{-# NOINLINE blockCounter #-}++modifyBlock :: a -> IO Int+modifyBlock _ = atomicModifyIORef blockCounter $ \ i -> let i' = i + blockSize in i' `seq` (i', i)+{-# NOINLINE modifyBlock #-}++gen :: a -> Block+gen x = Block (unsafeDupablePerformIO (modifyBlock x)) (gen <$> units)+{-# NOINLINE gen #-}++newBlock :: IO Block+newBlock = return $! gen ()+{-# NOINLINE newBlock #-}++splitBlock# :: Block -> (# Block, Block #)+splitBlock# (Block i (x :- xs)) = (# x, Block i xs #)+{-# INLINE splitBlock# #-}++-- | A user managed globally unique variable supply.+data Supply = Supply {-# UNPACK #-} !Int {-# UNPACK #-} !Int Block+  deriving (Eq,Ord,Show)++instance Hashable Supply where+  hashWithSalt s (Supply i j b) = s `hashWithSalt` i `hashWithSalt` j `hashWithSalt` b++blockSupply :: Block -> Supply+blockSupply (Block i bs) = Supply i (i + blockSize - 1) (extract bs)+{-# INLINE blockSupply #-}++-- | Grab a new supply. Any two supplies obtained with newSupply are guaranteed to return+-- disjoint sets of identifiers. Replaying the same sequence of operations on the same+-- Supply will yield the same results.+newSupply :: IO Supply+newSupply = blockSupply <$> newBlock+{-# INLINE newSupply #-}++-- | Obtain a fresh Id from a Supply.+freshId :: Supply -> (Int, Supply)+freshId s = case freshId# s of+  (# i, s' #) -> (I# i, s')+{-# INLINE freshId #-}++-- | Split a supply into two supplies that will return disjoint identifiers+splitSupply :: Supply -> (Supply, Supply)+splitSupply s = case splitSupply# s of+  (# l, r #) -> (l, r)+{-# INLINE splitSupply #-}++-- | An unboxed version of freshId+freshId# :: Supply -> (# Int#, Supply #)+freshId# (Supply i@(I# i#) j b)+  | i /= j = (# i#, Supply (i + 1) j b #)+  | otherwise = (# i#, blockSupply b #)+{-# INLINE freshId# #-}++-- | An unboxed version of splitSupply+splitSupply# :: Supply -> (# Supply, Supply #)+splitSupply# (Supply i k b) = case splitBlock# b of+    (# bl, br #)+      | k - i >= minSplitSupplySize+      , j <- i + div (k - i) 2 ->+        (# Supply i j bl, Supply (j + 1) k br #)+      | Block x (l :- r :- _) <- bl+      , y <- x + div blockSize 2+      , z <- x + blockSize - 1 ->+        (# Supply x (y - 1) l, Supply y z r #)+{-# INLINE splitSupply# #-}
+ test/properties.hs view
@@ -0,0 +1,21 @@+module Main where++import Control.Monad+import Control.Concurrent.Supply+import Data.Set+import System.Exit++reps = 2049++main = do+  supply <- newSupply+  let ids = loop supply reps+  let n = size (fromList ids)+  when (n == reps) exitSuccess+  putStrLn $ "Only " ++ show n ++ " out of " ++ show reps ++ " supplied identifiers are distinct"+  print ids+  exitFailure+  where+    loop s 0 = []+    loop s n = let (fId,s')  = freshId s+               in fId : loop s' (n-1)