clang-pure-0.1.0.0: src/Language/C/Clang/Internal/Refs.hs
{-
Copyright 2014 Google Inc. All Rights Reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-}
module Language.C.Clang.Internal.Refs where
import Control.Concurrent.MVar
import GHC.ForeignPtr (unsafeForeignPtrToPtr)
import Foreign hiding (newForeignPtr)
import Foreign.Concurrent
import System.IO.Unsafe
type Finalizer = IO ()
data NodeState = NodeState
{ destructable :: !Bool
, refCount :: !Int
}
type family RefOf n
class Clang n where
deref :: n -> (Ptr (RefOf n) -> IO a) -> IO a
unsafeToPtr :: n -> Ptr (RefOf n)
class Clang n => Parent n where
incCount :: n -> IO ()
decCount :: n -> IO ()
type family ParentOf n
class (Clang n, Parent (ParentOf n)) => Child n where
parent :: n -> ParentOf n
data Root a = Root
{ nodePtr :: ForeignPtr a
, nodeState :: MVar NodeState
, trueFinalize :: Finalizer
}
newRoot :: Ptr a -> Finalizer -> IO (Root a)
newRoot ptr fin = do
nsv <- newMVar $ NodeState { destructable = False, refCount = 0 }
nptr <- newForeignPtr ptr $ modifyMVar_ nsv $ \ns ->
if refCount ns == 0
then fin >> return ns
else return $ ns { destructable = True }
return $ Root nptr nsv fin
instance Parent (Root a) where
incCount r = modifyMVar_ (nodeState r) $ \ns -> return ns { refCount = refCount ns + 1 }
decCount r = modifyMVar_ (nodeState r) $ \ns -> do
if refCount ns == 1 && destructable ns
then trueFinalize r >> return ns { refCount = 0, destructable = False }
else return ns { refCount = refCount ns - 1 }
type instance RefOf (Root a) = a
instance Clang (Root a) where
deref r = withForeignPtr (nodePtr r)
unsafeToPtr = unsafeForeignPtrToPtr . nodePtr
data Node p a = Node p (Root a)
newNode :: Parent p => p -> (Ptr (RefOf p) -> IO ( Ptr a, Finalizer )) -> IO (Node p a)
newNode prn f = deref prn $ \pptr -> do
( cptr, cfin ) <- f pptr
incCount prn
rt <- newRoot cptr (cfin >> decCount prn)
return $ Node prn rt
instance Clang p => Parent (Node p a) where
incCount (Node _ n) = incCount n
decCount (Node _ n) = decCount n
type instance ParentOf (Node p a) = p
instance Parent p => Child (Node p a) where
parent (Node p _) = p
type instance RefOf (Node p a) = a
instance Clang p => Clang (Node p a) where
deref (Node p n) f = deref p $ \_ -> deref n f
unsafeToPtr (Node _ n) = unsafeToPtr n
data Leaf p a = Leaf p (ForeignPtr a)
newLeaf :: Parent p => p -> (Ptr (RefOf p) -> IO ( Ptr a, Finalizer )) -> IO (Leaf p a)
newLeaf prn f = deref prn $ \pptr -> do
( cptr, cfin ) <- f pptr
incCount prn
rt <- newForeignPtr cptr (cfin >> decCount prn)
return $ Leaf prn rt
type instance ParentOf (Leaf p a) = p
instance Parent p => Child (Leaf p a) where
parent (Leaf p _) = p
type instance RefOf (Leaf p a) = a
instance Clang p => Clang (Leaf p a) where
deref (Leaf p n) f = deref p $ \_ -> withForeignPtr n f
unsafeToPtr (Leaf _ n) = unsafeForeignPtrToPtr n
pointerEq :: Clang n => n -> n -> Bool
pointerEq r r' = unsafeToPtr r == unsafeToPtr r'
pointerCompare :: Clang n => n -> n -> Ordering
pointerCompare r r' = unsafeToPtr r `compare` unsafeToPtr r'
uderef :: Clang r => r -> (Ptr (RefOf r) -> IO a) -> a
uderef r f = unsafePerformIO $ deref r f