packages feed

ekg-core-0.1.2.0: Data/Atomic.hs

{-# LANGUAGE BangPatterns
           , CPP
           , ForeignFunctionInterface
           , MagicHash
           , UnboxedTuples
           #-}
-- | An atomic integer value. All operations are thread safe.
module Data.Atomic
    (
      Atomic
    , new
    , read
    , write
    , inc
    , dec
    , add
    , subtract
    ) where

#include "MachDeps.h"
#ifndef SIZEOF_HSINT
#error "MachDeps.h didn't define SIZEOF_HSINT"
#endif

import Prelude hiding (read, subtract)

import GHC.Int

#if SIZEOF_HSINT == 8

-- 64-bit imports
import GHC.IO
import GHC.Prim

#else

-- 32-bit imports
import Data.IORef

#endif


-- 64-bit machine, Int ~ Int64, do it the fast way:
#if SIZEOF_HSINT == 8

#if MIN_VERSION_base(4,17,0)
int64ToInt :: Int64# -> Int#
int64ToInt = int64ToInt#

intToInt64 :: Int# -> Int64#
intToInt64 = intToInt64#
#else
int64ToInt :: Int# -> Int#
int64ToInt i = i

intToInt64 :: Int# -> Int#
intToInt64 i = i
#endif

-- | A mutable, atomic integer.
data Atomic = C (MutableByteArray# RealWorld)

-- | Create a new, zero initialized, atomic.
new :: Int64 -> IO Atomic
new (I64# n64) = IO $ \s ->
    case newByteArray# SIZEOF_HSINT# s of { (# s1, mba #) ->
    case atomicWriteIntArray# mba 0# (int64ToInt n64) s1 of { s2 ->
    (# s2, C mba #) }}

read :: Atomic -> IO Int64
read (C mba) = IO $ \s ->
    case atomicReadIntArray# mba 0# s of { (# s1, n #) ->
    (# s1, I64# (intToInt64 n) #)}

-- | Set the atomic to the given value.
write :: Atomic -> Int64 -> IO ()
write (C mba) (I64# n64) = IO $ \s ->
    case atomicWriteIntArray# mba 0# (int64ToInt n64) s of { s1 ->
    (# s1, () #) }

-- | Increase the atomic by the given amount.
add :: Atomic -> Int64 -> IO ()
add (C mba) (I64# n64) = IO $ \s ->
    case fetchAddIntArray# mba 0# (int64ToInt n64) s of { (# s1, _ #) ->
    (# s1, () #) }

-- | Decrease the atomic by the given amount.
subtract :: Atomic -> Int64 -> IO ()
subtract (C mba) (I64# n64) = IO $ \s ->
    case fetchSubIntArray# mba 0# (int64ToInt n64) s of { (# s1, _ #) ->
    (# s1, () #) }

#else

-- 32-bit machine, Int ~ Int32, fall back to IORef. This could be replaced with
-- faster implementations for specific 32-bit machines in the future, but the
-- idea is to preserve 64-bit width for counters.

newtype Atomic = C (IORef Int64)

-- | Create a new, zero initialized, atomic.
new :: Int64 -> IO Atomic
new = fmap C . newIORef

read :: Atomic -> IO Int64
read (C ior) = readIORef ior

-- | Set the atomic to the given value.
write :: Atomic -> Int64 -> IO ()
write (C ior) !i = atomicWriteIORef ior i

-- | Increase the atomic by the given amount.
add :: Atomic -> Int64 -> IO ()
add (C ior) !i = atomicModifyIORef' ior (\(!n) -> (n+i, ()))

-- | Decrease the atomic by the given amount.
subtract :: Atomic -> Int64 -> IO ()
subtract (C ior) !i = atomicModifyIORef' ior (\(!n) -> (n-i, ()))

#endif

-- | Increase the atomic by one.
inc :: Atomic -> IO ()
inc atomic = add atomic 1

-- | Decrease the atomic by one.
dec :: Atomic -> IO ()
dec atomic = subtract atomic 1