atomic-primops-foreign (empty) → 0.6
raw patch · 5 files changed
+171/−0 lines, 5 filesdep +HUnitdep +basedep +bits-atomicsetup-changed
Dependencies added: HUnit, base, bits-atomic, test-framework, test-framework-hunit, time
Files
- Data/Atomics/Counter/Foreign.hs +79/−0
- LICENSE +30/−0
- Setup.hs +2/−0
- atomic-primops-foreign.cabal +42/−0
- testing/Main.hs +18/−0
+ Data/Atomics/Counter/Foreign.hs view
@@ -0,0 +1,79 @@+{-# LANGUAGE BangPatterns #-}++-- | This implementation stores an unboxed counter and uses FFI operations to modify+-- its contents. It has the advantage that it can use true fetch-and-add operations.+-- It has the disadvantage of extra overhead due to FFI calls.+--+-- For more documentation, see the module "Data.Atomics.Counter", which exports+-- the same interface as this module.++module Data.Atomics.Counter.Foreign+ (AtomicCounter, CTicket,+ newCounter, readCounterForCAS, readCounter, peekCTicket,+ writeCounter, casCounter, incrCounter, incrCounter_)+ where+import Control.Monad (void)+import Data.Bits.Atomic+import Foreign.ForeignPtr+import Foreign.Storable++-- | The type of mutable atomic counters.+type AtomicCounter = ForeignPtr Int++-- | You should not depend on this type. It varies between different implementations+-- of atomic counters.+type CTicket = Int++{-# INLINE newCounter #-}+-- | Create a new counter initialized to the given value.+newCounter :: Int -> IO AtomicCounter+newCounter n = do x <- mallocForeignPtr+ writeCounter x n+ -- Do we need a write barrier here?+ return x++{-# INLINE incrCounter #-}+-- | Increment the counter by a given amount.+-- Returns the original value before the increment.+-- +-- Note that UNLIKE with boxed implementations of counters, where increment is+-- based on CAS, this increment is /O(1)/. Fetch-and-add does not require a retry+-- loop like CAS.+incrCounter :: Int -> AtomicCounter -> IO Int+incrCounter bump r = withForeignPtr r$ \r' -> addAndFetch r' bump++{-# INLINE incrCounter_ #-}+-- | An alternate version for when you don't care about the old value.+incrCounter_ :: Int -> AtomicCounter -> IO ()+incrCounter_ bump r = withForeignPtr r$ \r' -> void (addAndFetch r' bump)++{-# INLINE readCounterForCAS #-}+-- | Just like the "Data.Atomics" CAS interface, this routine returns an opaque+-- ticket that can be used in CAS operations.+readCounterForCAS :: AtomicCounter -> IO CTicket+readCounterForCAS = readCounter++{-# INLINE peekCTicket #-}+-- | Opaque tickets cannot be constructed, but they can be destructed into values.+peekCTicket :: CTicket -> Int+peekCTicket x = x++{-# INLINE readCounter #-}+-- | Equivalent to `readCounterForCAS` followed by `peekCTicket`.+readCounter :: AtomicCounter -> IO Int+readCounter r = withForeignPtr r peek ++{-# INLINE writeCounter #-}+-- | Make a non-atomic write to the counter. No memory-barrier.+writeCounter :: AtomicCounter -> Int -> IO ()+writeCounter r !new = withForeignPtr r $ \r' -> poke r' new++{-# INLINE casCounter #-}+-- | Compare and swap for the counter ADT.+casCounter :: AtomicCounter -> CTicket -> Int -> IO (Bool, CTicket)+casCounter r !tick !new = withForeignPtr r $ \r' -> do+ cur <- compareAndSwap r' tick new+ if cur==tick + then return (True,new)+ else return (False,cur)+-- return (b==tick, b)
+ LICENSE view
@@ -0,0 +1,30 @@+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.++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+OWNER 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.
+ Setup.hs view
@@ -0,0 +1,2 @@+import Distribution.Simple+main = defaultMain
+ atomic-primops-foreign.cabal view
@@ -0,0 +1,42 @@+Name: atomic-primops-foreign+Version: 0.6+License: BSD3+License-file: LICENSE+Author: Ryan Newton+Maintainer: rrnewton@gmail.com+Category: Data+Build-type: Simple+Cabal-version: >=1.8+HomePage: https://github.com/rrnewton/haskell-lockfree/wiki+Bug-Reports: https://github.com/rrnewton/haskell-lockfree/issues++-- 0.6 -- Factored out of parent module to separate dependencies.++Synopsis: An atomic counter implemented using the FFI.+Description:++Library+ exposed-modules: Data.Atomics.Counter.Foreign+ ghc-options: -O2 -funbox-strict-fields+ build-depends: bits-atomic >= 0.1.3++ -- This can go back further than the atomic-primops package in supporting old GHC's:+ -- For now let's say GHC 7.0 through 7.8:+ build-depends: base >= 4.3.0.0 && <= 4.7.0.0+ CC-Options: -Wall ++Test-Suite test-atomic-primops-foreign+ type: exitcode-stdio-1.0+ main-is: testing/Main.hs+ hs-source-dirs: testing/ ./++ ghc-options: -threaded -rtsopts -with-rtsopts=-N4+ build-depends: bits-atomic+ build-depends: base >= 4.3.0.0 && <= 4.7.0.0, + -- For Testing:+ time, HUnit, test-framework, test-framework-hunit++Source-Repository head+ Type: git+ Location: git://github.com/rrnewton/haskell-lockfree.git+ Subdir: atomic-primops-foreign/
+ testing/Main.hs view
@@ -0,0 +1,18 @@++module Main where++import GHC.Conc+import Test.Framework (Test, defaultMain, testGroup)+import qualified CounterForeign+import Control.Monad (when)++----------------------------------------++main :: IO ()+main = do+ -- TEMP: Fixing this at four processors because it takes a REALLY long time at larger numbers:+ -- It does 248 test cases and takes 55s at -N16...+ -- numcap <- getNumProcessors+ let numcap = 4+ when (numCapabilities /= numcap) $ setNumCapabilities numcap+ defaultMain CounterForeign.tests