1 {-# LANGUAGE ExistentialQuantification #-}
2 module Mate.MemoryManager (evacuateList, AllocationManager(heapSize, performCollection),
3 TwoSpace, initTwoSpace) where
5 import qualified Foreign.Marshal.Alloc as Alloc
7 import Foreign.Storable
8 import Foreign.Marshal.Utils
11 import Control.Monad.State
12 import Control.Applicative
16 class AllocationManager a where
18 -- | allocates n bytes in current space to space (may be to space or gen0 space)
19 mallocBytes :: Int -> StateT a IO (Ptr b)
21 -- | performs full gc and which is reflected in mem managers state
22 performCollection :: (RefObj b) => [b] -> StateT a IO ()
24 heapSize :: StateT a IO Int
26 data TwoSpace = TwoSpace { fromBase :: IntPtr,
30 fromExtreme :: IntPtr,
33 instance AllocationManager TwoSpace where
34 mallocBytes = mallocBytes'
35 performCollection = performCollection'
37 heapSize = do space <- get
38 return $ fromIntegral $ toHeap space - fromIntegral (toBase space)
41 performCollection' :: (RefObj a) => [a] -> StateT TwoSpace IO ()
42 performCollection' roots = do oldState <- get
45 lift (performCollectionIO newState roots)
46 -- [todo hs]: patch gc roots
48 -- [todo hs] this is slow. merge phases to eliminate list with refs
49 performCollectionIO :: (AllocationManager b, RefObj a) => b -> [a] -> IO ()
50 performCollectionIO manager refs = do lifeRefs <- liftM concat $ mapM (markTree'' marked mark []) refs
51 evacuateList lifeRefs manager
54 switchSpaces :: TwoSpace -> TwoSpace
55 switchSpaces old = old { fromHeap = toHeap old,
56 toHeap = fromBase old,
57 fromBase = toBase old,
58 toBase = fromBase old,
59 fromExtreme = toExtreme old,
60 toExtreme = fromExtreme old }
63 mallocBytes' :: Int -> StateT TwoSpace IO (Ptr b)
64 mallocBytes' bytes = do state <- get
65 let end = toHeap state + ptrToIntPtr (nullPtr `plusPtr` bytes) -- not really? FUUU
66 -- actually i would like to use an existential within TwoSpace but this requires
67 -- pattern matchingt at call site http://stackoverflow.com/questions/10192663/why-cant-existential-types-use-record-syntax which is i think even slower.
68 if end <= toExtreme state then alloc state end else fail
69 where alloc :: TwoSpace -> IntPtr -> StateT TwoSpace IO (Ptr b)
70 alloc state end = do let ptr = toHeap state
71 put $ state { toHeap = end }
72 liftIO (putStrLn $ "Allocated obj: " ++ show ptr)
73 liftIO (return $ intPtrToPtr ptr)
74 fail = error "no space left in two space (mallocBytes')"
77 evacuate' :: (RefObj a, AllocationManager b) => [a] -> StateT b IO ()
78 evacuate' = mapM_ evacuate''
80 evacuate'' :: (RefObj a, AllocationManager b) => a -> StateT b IO ()
81 evacuate'' obj = do (size,payload) <- liftIO ((,) <$> size obj <*> payload obj)
83 newPtr <- mallocBytes size
84 liftIO (putStrLn ("evacuating: " ++ show obj ++ " and set: " ++ show newPtr))
85 -- copy data over and leave notice
86 liftIO (copyBytes newPtr (intPtrToPtr payload) size >>
87 setNewRef obj (cast newPtr))
89 evacuateList :: (RefObj a, AllocationManager b) => [a] -> b -> IO ()
90 evacuateList objs = evalStateT (evacuate' objs)
93 initTwoSpace :: Int -> IO TwoSpace
94 initTwoSpace size = do printf "initializing TwoSpace memory manager with %d bytes.\n" size
95 fromSpace <- Alloc.mallocBytes size
96 toSpace <- Alloc.mallocBytes size
97 if fromSpace /= nullPtr && toSpace /= nullPtr
98 then return $ buildToSpace fromSpace toSpace
99 else error "Could not initialize TwoSpace memory manager (malloc returned null ptr)\n"
100 where buildToSpace from to = let fromBase' = ptrToIntPtr from
101 toBase' = ptrToIntPtr to
102 fromExtreme' = ptrToIntPtr $ from `plusPtr` size
103 toExtreme' = ptrToIntPtr $ to `plusPtr` size
104 in TwoSpace { fromBase = fromBase', toBase = toBase',
105 fromHeap = fromBase', toHeap = toBase',
106 fromExtreme = fromExtreme', toExtreme = toExtreme' }