1
- {-# LANGUAGE CPP #-}
1
+ {-# LANGUAGE CPP #-}
2
+ {-# LANGUAGE LambdaCase #-}
3
+ {-# LANGUAGE RecordWildCards #-}
2
4
module Data.Arena (
3
5
ArenaManager ,
4
6
newArenaManager ,
@@ -15,74 +17,169 @@ module Data.Arena (
15
17
withUnmanagedArena ,
16
18
) where
17
19
18
- import Control.DeepSeq (NFData (.. ), rwhnf )
20
+ import Control.DeepSeq (NFData (.. ))
21
+ import Control.Exception (assert )
19
22
import Control.Monad.Primitive
23
+ import Data.Bits (complement , popCount , (.&.) )
20
24
import Data.Primitive.ByteArray
21
25
import Data.Primitive.MutVar
26
+ import Data.Primitive.MVar
27
+ import Data.Primitive.PrimVar
22
28
23
29
#ifdef NO_IGNORE_ASSERTS
24
- import Control.Monad (forM_ )
25
30
import Data.Word (Word8 )
26
31
#endif
27
32
28
- data ArenaManager s = ArenaManager
33
+ data ArenaManager s = ArenaManager ( MutVar s [ Arena s ])
29
34
30
35
newArenaManager :: PrimMonad m => m (ArenaManager (PrimState m ))
31
36
newArenaManager = do
32
- _toUseTheConstraint <- newMutVar ' x '
33
- return ArenaManager
37
+ m <- newMutVar []
38
+ return $ ArenaManager m
34
39
35
40
-- | For use in bencmark environments
36
41
instance NFData (ArenaManager s ) where
37
- rnf _ = ()
42
+ rnf ( ArenaManager ! _) = ()
38
43
39
- -- TODO: this is debug implementation
40
- -- we retain all the allocated arrays,
41
- -- so we can scramble them at the end.
42
- data Arena s = Arena (MutVar s [MutableByteArray s ])
44
+ data Arena s = Arena
45
+ { curr :: ! (MVar s (Block s )) -- current block, also acts as a lock
46
+ , free :: ! (MutVar s [Block s ])
47
+ , full :: ! (MutVar s [Block s ])
48
+ }
49
+
50
+ data Block s = Block ! (PrimVar s Int ) ! (MutableByteArray s )
43
51
44
52
instance NFData (Arena s ) where
45
- rnf (Arena mvar ) = rwhnf mvar
53
+ rnf (Arena ! _ ! _ ! _ ) = ()
46
54
47
55
type Size = Int
48
56
type Offset = Int
49
57
type Alignment = Int
50
58
59
+ blockSize :: Int
60
+ blockSize = 0x100000
61
+
62
+ newBlock :: PrimMonad m => m (Block (PrimState m ))
63
+ newBlock = do
64
+ off <- newPrimVar 0
65
+ mba <- newAlignedPinnedByteArray blockSize 4096
66
+ return (Block off mba)
67
+
51
68
withArena :: PrimMonad m => ArenaManager (PrimState m ) -> (Arena (PrimState m ) -> m a ) -> m a
52
69
withArena am f = do
53
- a <- newArena am
54
- x <- f a
55
- closeArena a
56
- pure x
70
+ a <- newArena am
71
+ x <- f a
72
+ closeArena am a
73
+ pure x
57
74
58
75
newArena :: PrimMonad m => ArenaManager (PrimState m ) -> m (Arena (PrimState m ))
59
- newArena _ = do
60
- mvar <- newMutVar []
61
- pure $! (Arena mvar)
76
+ newArena (ArenaManager arenas) = do
77
+ marena <- atomicModifyMutVar' arenas $ \ case
78
+ [] -> ([] , Nothing )
79
+ (x: xs) -> (xs, Just x)
62
80
63
- closeArena :: PrimMonad m => Arena (PrimState m ) -> m ()
64
- #ifdef NO_IGNORE_ASSERTS
65
- closeArena (Arena mvar) = do
66
- -- scramble the allocated bytearrays,
67
- -- they shouldn't be in use anymore!
68
- mbas <- readMutVar mvar
69
- forM_ mbas $ \ mba -> do
70
- size <- getSizeofMutableByteArray mba
71
- setByteArray mba 0 size (0x77 :: Word8 )
81
+ case marena of
82
+ Just arena -> return arena
83
+ Nothing -> do
84
+ curr <- newBlock >>= newMVar
85
+ free <- newMutVar []
86
+ full <- newMutVar []
87
+ return Arena {.. }
88
+
89
+ closeArena :: PrimMonad m => ArenaManager (PrimState m ) -> Arena (PrimState m ) -> m ()
90
+ closeArena (ArenaManager arenas) arena = do
91
+ scrambleArena arena
92
+
93
+ -- reset the arena to clear state
94
+ resetArena arena
95
+
96
+ atomicModifyMutVar' arenas $ \ xs -> (arena : xs, () )
97
+
98
+
99
+
100
+ scrambleArena :: PrimMonad m => Arena (PrimState m ) -> m ()
101
+ #ifndef NO_IGNORE_ASSERTS
102
+ scrambleArena _ = return ()
72
103
#else
73
- closeArena _ = pure ()
104
+ scrambleArena Arena {.. } = do
105
+ readMVar curr >>= scrambleBlock
106
+ readMutVar full >>= mapM_ scrambleBlock
107
+ readMutVar free >>= mapM_ scrambleBlock
108
+
109
+ scrambleBlock :: PrimMonad m => Block (PrimState m ) -> m ()
110
+ scrambleBlock (Block _ mba) = do
111
+ size <- getSizeofMutableByteArray mba
112
+ setByteArray mba 0 size (0x77 :: Word8 )
74
113
#endif
75
114
76
- -- | Create unmanaged arena
115
+ -- | Reset arena, i.e. return used blocks to free list.
116
+ resetArena :: PrimMonad m => Arena (PrimState m ) -> m ()
117
+ resetArena Arena {.. } = do
118
+ Block off mba <- takeMVar curr
119
+
120
+ -- reset current block
121
+ writePrimVar off 0
122
+
123
+ -- move full block to free blocks.
124
+ -- block's offset will be reset in 'newBlockWithFree'
125
+ full' <- atomicModifyMutVar' full $ \ xs -> ([] , xs)
126
+ atomicModifyMutVar' free $ \ xs -> (full' <> xs, () )
127
+
128
+ putMVar curr $! Block off mba
129
+
130
+ -- | Create unmanaged arena.
77
131
--
78
132
-- Never use this in non-tests code.
79
133
withUnmanagedArena :: PrimMonad m => (Arena (PrimState m ) -> m a ) -> m a
80
134
withUnmanagedArena k = do
81
135
mgr <- newArenaManager
82
136
withArena mgr k
83
137
138
+ -- | Allocate a slice of mutable byte array from the arena.
84
139
allocateFromArena :: PrimMonad m => Arena (PrimState m )-> Size -> Alignment -> m (Offset , MutableByteArray (PrimState m ))
85
- allocateFromArena (Arena mvar) ! size ! alignment = do
86
- mba <- newAlignedPinnedByteArray size alignment
87
- atomicModifyMutVar' mvar $ \ mbas -> (mba : mbas, () )
88
- return (0 , mba)
140
+ allocateFromArena ! arena ! size ! alignment =
141
+ assert (popCount alignment == 1 ) $ -- powers of 2
142
+ assert (size <= blockSize) $ -- not too large allocations
143
+ allocateFromArena' arena size alignment
144
+
145
+ -- TODO!? this is not async exception safe
146
+ allocateFromArena' :: PrimMonad m => Arena (PrimState m )-> Size -> Alignment -> m (Offset , MutableByteArray (PrimState m ))
147
+ allocateFromArena' arena@ Arena { .. } ! size ! alignment = do
148
+ -- take current block, lock the arena
149
+ curr'@ (Block off mba) <- takeMVar curr
150
+
151
+ off' <- readPrimVar off
152
+ let ! ali = alignment - 1
153
+ let ! off'' = (off' + ali) .&. complement ali -- ceil towards next alignment
154
+ let ! end = off'' + size
155
+ if end <= blockSize
156
+ then do
157
+ -- fits into current block:
158
+ -- * update offset
159
+ writePrimVar off end
160
+ -- * release lock
161
+ putMVar curr curr'
162
+ -- * return data
163
+ return (off'', mba)
164
+
165
+ else do
166
+ -- doesn't fit into current block:
167
+ -- * move current block into full
168
+ atomicModifyMutVar' full (\ xs -> (curr' : xs, () ))
169
+ -- * allocate new block
170
+ new <- newBlockWithFree free
171
+ -- * set new block as current, release the lock
172
+ putMVar curr new
173
+ -- * go again
174
+ allocateFromArena' arena size alignment
175
+
176
+ -- | Allocate new block, possibly taking it from a free list
177
+ newBlockWithFree :: PrimMonad m => MutVar (PrimState m ) [Block (PrimState m )] -> m (Block (PrimState m ))
178
+ newBlockWithFree free = do
179
+ free' <- readMutVar free
180
+ case free' of
181
+ [] -> newBlock
182
+ x@ (Block off _): xs -> do
183
+ writePrimVar off 0
184
+ writeMutVar free xs
185
+ return x
0 commit comments