-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathIntSupply.hs
86 lines (80 loc) · 2.28 KB
/
IntSupply.hs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
{-# LANGUAGE BangPatterns #-}
{-# LANGUAGE BlockArguments #-}
{-# LANGUAGE MagicHash #-}
{-# LANGUAGE UnboxedTuples #-}
-- | This module provides a simple, efficient supply of integers using atomic fetch-and-add.
--
-- To use this module, first create an @IntSupply@. This is often done once at the top level of an application, in
-- global scope.
--
-- > import IntSupply (IntSupply)
-- > import IntSupply qualified
-- > import System.IO.Unsafe (unsafePerformIO)
-- >
-- > myIntSupply :: IntSupply
-- > myIntSupply = unsafePerformIO IntSupply.new
-- > {-# NOINLINE myIntSupply #-}
--
-- Next, call @IntSupply.next@ on the supply, which will return 0, then 1, and so on.
--
-- > > IntSupply.next myIntSupply
-- > 0
-- > > IntSupply.next myIntSupply
-- > 1
--
-- If desired, you can reset the count to 0.
--
-- > > IntSupply.reset myIntSupply
-- > > IntSupply.next myIntSupply
-- > 0
--
-- On a 64-bit machine, for many applications, these integers can be treated as effectively unique: even if
-- 1,000,000,000 integers were generated per second, it would still take over 580 years to wrap around.
--
-- On a 32-bit machine, more care must be taken, of course: even if only 1,000 integers were generated per second, it
-- would only take 50 days to wrap around.
module IntSupply
( IntSupply,
new,
next,
reset,
)
where
import Data.Bits (finiteBitSize)
import GHC.Base
( IO (IO),
Int (I#),
MutableByteArray#,
RealWorld,
atomicWriteIntArray#,
fetchAddIntArray#,
newByteArray#,
writeIntArray#,
)
-- | A thread-safe supply of integers.
data IntSupply
= IntSupply (MutableByteArray# RealWorld)
-- | Create a supply of integers.
new :: IO IntSupply
new =
IO \s0 ->
case newByteArray# size s0 of
(# s1, supply #) ->
(# writeIntArray# supply 0# 0# s1, IntSupply supply #)
where
!(I# size) =
finiteBitSize (undefined :: Int) `div` 8
{-# INLINEABLE new #-}
-- | Get the next integer from a supply of integers.
next :: IntSupply -> IO Int
next (IntSupply supply) =
IO \s0 ->
case fetchAddIntArray# supply 0# 1# s0 of
(# s1, n #) -> (# s1, I# n #)
{-# INLINEABLE next #-}
-- | Reset a supply of integers to 0.
reset :: IntSupply -> IO ()
reset (IntSupply arr#) =
IO \s0 ->
(# atomicWriteIntArray# arr# 0# 0# s0, () #)
{-# INLINEABLE reset #-}