-- 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
import Ki.Internal.Prelude

-- | 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 #-}
