haskell-gi-base-0.20: Data/GI/Base/ManagedPtr.hs
{-# LANGUAGE FlexibleContexts, ScopedTypeVariables #-}
-- For HasCallStack compatibility
{-# LANGUAGE ImplicitParams, KindSignatures, ConstraintKinds #-}
-- | We wrap most objects in a "managed pointer", which is basically a
-- 'ForeignPtr' of the appropriate type together with a notion of
-- "disowning", which means not running the finalizers passed upon
-- construction of the object upon garbage collection. The routines in
-- this module deal with the memory management of such managed
-- pointers.
module Data.GI.Base.ManagedPtr
(
-- * Managed pointers
newManagedPtr
, newManagedPtr'
, withManagedPtr
, maybeWithManagedPtr
, withManagedPtrList
, unsafeManagedPtrGetPtr
, unsafeManagedPtrCastPtr
, touchManagedPtr
, disownManagedPtr
-- * Safe casting
, castTo
, unsafeCastTo
-- * Wrappers
, newObject
, wrapObject
, unrefObject
, disownObject
, newBoxed
, wrapBoxed
, copyBoxedPtr
, freeBoxed
, disownBoxed
, wrapPtr
, newPtr
, copyPtr
) where
#if !MIN_VERSION_base(4,8,0)
import Control.Applicative ((<$>))
#endif
import Control.Monad (when, void)
import Data.Coerce (coerce)
import Data.IORef (newIORef, readIORef, writeIORef, IORef)
import Foreign.C (CInt(..))
import Foreign.Ptr (Ptr, FunPtr, castPtr, nullPtr)
import Foreign.ForeignPtr (FinalizerPtr, touchForeignPtr, newForeignPtr_)
import qualified Foreign.Concurrent as FC
import Foreign.ForeignPtr.Unsafe (unsafeForeignPtrToPtr)
import Data.GI.Base.BasicTypes
import Data.GI.Base.Utils
import System.IO (hPutStrLn, stderr)
#if MIN_VERSION_base(4,9,0)
import GHC.Stack (HasCallStack, prettyCallStack, callStack)
#elif MIN_VERSION_base(4,8,1)
import GHC.Stack (CallStack)
import GHC.Exts (Constraint)
type HasCallStack = ((?callStack :: CallStack) :: Constraint)
#else
import GHC.Exts (Constraint)
type HasCallStack = (() :: Constraint)
#endif
-- | Thin wrapper over `Foreign.Concurrent.newForeignPtr`.
newManagedPtr :: Ptr a -> IO () -> IO (ManagedPtr a)
newManagedPtr ptr finalizer = do
let ownedFinalizer :: IORef Bool -> IO ()
ownedFinalizer boolRef = do
owned <- readIORef boolRef
when owned finalizer
isOwnedRef <- newIORef True
fPtr <- FC.newForeignPtr ptr (ownedFinalizer isOwnedRef)
return $ ManagedPtr {
managedForeignPtr = fPtr
, managedPtrIsOwned = isOwnedRef
}
foreign import ccall "dynamic"
mkFinalizer :: FinalizerPtr a -> Ptr a -> IO ()
-- | Version of `newManagedPtr` taking a `FinalizerPtr` and a
-- corresponding `Ptr`, as in `Foreign.ForeignPtr.newForeignPtr`.
newManagedPtr' :: FinalizerPtr a -> Ptr a -> IO (ManagedPtr a)
newManagedPtr' finalizer ptr = newManagedPtr ptr (mkFinalizer finalizer ptr)
-- | Thin wrapper over `Foreign.Concurrent.newForeignPtr_`.
newManagedPtr_ :: Ptr a -> IO (ManagedPtr a)
newManagedPtr_ ptr = do
isOwnedRef <- newIORef True
fPtr <- newForeignPtr_ ptr
return $ ManagedPtr {
managedForeignPtr = fPtr
, managedPtrIsOwned = isOwnedRef
}
-- | Do not run the finalizers upon garbage collection of the `ManagedPtr`.
disownManagedPtr :: forall a. ManagedPtrNewtype a => a -> IO (Ptr a)
disownManagedPtr managed = do
ptr <- unsafeManagedPtrGetPtr managed
writeIORef (managedPtrIsOwned c) False
return ptr
where c = coerce managed :: ManagedPtr ()
-- | Perform an IO action on the 'Ptr' inside a managed pointer.
withManagedPtr :: ManagedPtrNewtype a => a -> (Ptr a -> IO c) -> IO c
withManagedPtr managed action = do
ptr <- unsafeManagedPtrGetPtr managed
result <- action ptr
touchManagedPtr managed
return result
-- | Like `withManagedPtr`, but accepts a `Maybe` type. If the passed
-- value is `Nothing` the inner action will be executed with a
-- `nullPtr` argument.
maybeWithManagedPtr :: ManagedPtrNewtype a => Maybe a -> (Ptr a -> IO c) -> IO c
maybeWithManagedPtr Nothing action = action nullPtr
maybeWithManagedPtr (Just managed) action = do
ptr <- unsafeManagedPtrGetPtr managed
result <- action ptr
touchManagedPtr managed
return result
-- | Perform an IO action taking a list of 'Ptr' on a list of managed
-- pointers.
withManagedPtrList :: ManagedPtrNewtype a => [a] -> ([Ptr a] -> IO c) -> IO c
withManagedPtrList managedList action = do
ptrs <- mapM unsafeManagedPtrGetPtr managedList
result <- action ptrs
mapM_ touchManagedPtr managedList
return result
-- | Return the 'Ptr' in a given managed pointer. As the name says,
-- this is potentially unsafe: the given 'Ptr' may only be used
-- /before/ a call to 'touchManagedPtr'. This function is of most
-- interest to the autogenerated bindings, for hand-written code
-- 'withManagedPtr' is almost always a better choice.
unsafeManagedPtrGetPtr :: (HasCallStack, ManagedPtrNewtype a) => a -> IO (Ptr a)
unsafeManagedPtrGetPtr = unsafeManagedPtrCastPtr
-- | Same as 'unsafeManagedPtrGetPtr', but is polymorphic on the
-- return type.
unsafeManagedPtrCastPtr :: forall a b. (HasCallStack, ManagedPtrNewtype a) =>
a -> IO (Ptr b)
unsafeManagedPtrCastPtr m = do
let c = coerce m :: ManagedPtr ()
ptr = (castPtr . unsafeForeignPtrToPtr . managedForeignPtr) c
owned <- readIORef (managedPtrIsOwned c)
when (not owned) (notOwnedWarning ptr)
return ptr
-- | Print a warning when we try to access a disowned foreign ptr.
notOwnedWarning :: HasCallStack => Ptr a -> IO ()
notOwnedWarning ptr = do
hPutStrLn stderr ("Accessing a disowned pointer <" ++ show ptr
++ ">, this may lead to crashes.\n"
++ callstack)
where
#if MIN_VERSION_base(4,9,0)
callstack = prettyCallStack (callStack)
#else
callstack = "<CallStack only available with GHC 8.0>"
#endif
-- | Ensure that the 'Ptr' in the given managed pointer is still alive
-- (i.e. it has not been garbage collected by the runtime) at the
-- point that this is called.
touchManagedPtr :: forall a. ManagedPtrNewtype a => a -> IO ()
touchManagedPtr m = let c = coerce m :: ManagedPtr ()
in (touchForeignPtr . managedForeignPtr) c
-- Safe casting machinery
foreign import ccall unsafe "check_object_type"
c_check_object_type :: Ptr o -> CGType -> CInt
-- | Cast to the given type, checking that the cast is valid. If it is
-- not, we return `Nothing`. Usage:
--
-- > maybeWidget <- castTo Widget label
castTo :: forall o o'. (GObject o, GObject o') =>
(ManagedPtr o' -> o') -> o -> IO (Maybe o')
castTo constructor obj =
withManagedPtr obj $ \objPtr -> do
GType t <- gobjectType (undefined :: o')
if c_check_object_type objPtr t /= 1
then return Nothing
else Just <$> newObject constructor objPtr
-- | Cast to the given type, assuming that the cast will succeed. This
-- function will call `error` if the cast is illegal.
unsafeCastTo :: forall o o'. (HasCallStack, GObject o, GObject o') =>
(ManagedPtr o' -> o') -> o -> IO o'
unsafeCastTo constructor obj =
withManagedPtr obj $ \objPtr -> do
GType t <- gobjectType (undefined :: o')
if c_check_object_type objPtr t /= 1
then do
srcType <- gobjectType obj >>= gtypeName
destType <- gobjectType (undefined :: o') >>= gtypeName
error $ "unsafeCastTo :: invalid conversion from " ++ srcType ++ " to "
++ destType ++ " requested."
else newObject constructor objPtr
-- Reference counting for constructors
foreign import ccall "&dbg_g_object_unref"
ptr_to_g_object_unref :: FunPtr (Ptr a -> IO ())
foreign import ccall "g_object_ref" g_object_ref ::
Ptr a -> IO (Ptr a)
-- | Construct a Haskell wrapper for a 'GObject', increasing its
-- reference count.
newObject :: (GObject a, GObject b) => (ManagedPtr a -> a) -> Ptr b -> IO a
newObject constructor ptr = do
void $ g_object_ref ptr
fPtr <- newManagedPtr' ptr_to_g_object_unref $ castPtr ptr
return $! constructor fPtr
foreign import ccall "g_object_ref_sink" g_object_ref_sink ::
Ptr a -> IO (Ptr a)
-- | Same as 'newObject', but we take ownership of the object. Newly
-- created 'GObject's are typically floating, so we use
-- <https://developer.gnome.org/gobject/stable/gobject-The-Base-Object-Type.html#g-object-ref-sink g_object_ref_sink>.
-- Notice that the
-- semantics here are a little bit subtle: some objects (such as
-- GtkWindow, see the code about "user_ref_count" in gtkwindow.c in
-- the gtk+ distribution) are created /without/ the floating flag,
-- since they own a reference to themselves. So, wrapping them is
-- really about adding a ref. If we add the ref, when Haskell drops
-- the last ref to the 'GObject' it will /g_object_unref/, and the
-- window will /g_object_unref/ itself upon destruction, so by the end
-- we don't leak memory. If we don't add the ref, there will be two
-- /g_object_unrefs/ acting on the object (one from Haskell and one from
-- the GtkWindow destroy) when the object is destroyed and the second
-- one will give a segfault.
--
-- This is the story for GInitiallyUnowned objects (e.g. anything that
-- is a descendant from GtkWidget). For objects that are not initially
-- floating (i.e. not descendents of GInitiallyUnowned) we simply take
-- control of the reference.
wrapObject :: forall a b. (GObject a, GObject b) =>
(ManagedPtr a -> a) -> Ptr b -> IO a
wrapObject constructor ptr = do
when (gobjectIsInitiallyUnowned (undefined :: a)) $
void $ g_object_ref_sink ptr
fPtr <- newManagedPtr' ptr_to_g_object_unref $ castPtr ptr
return $! constructor fPtr
foreign import ccall "dbg_g_object_unref"
dbg_g_object_unref :: Ptr a -> IO ()
-- | Decrease the reference count of the given 'GObject'. The memory
-- associated with the object may be released if the reference count
-- reaches 0.
unrefObject :: GObject a => a -> IO ()
unrefObject obj = withManagedPtr obj dbg_g_object_unref
-- | Print some debug info (if the right environment valiable is set)
-- about the object being disowned.
foreign import ccall "dbg_g_object_disown"
dbg_g_object_disown :: Ptr a -> IO ()
-- | Disown a GObject, that is, do not unref the associated foreign
-- GObject when the Haskell object gets garbage collected. Returns the
-- pointer to the underlying GObject.
disownObject :: GObject a => a -> IO (Ptr b)
disownObject obj = withManagedPtr obj $ \ptr -> do
dbg_g_object_disown ptr
castPtr <$> disownManagedPtr obj
foreign import ccall "boxed_free_helper" boxed_free_helper ::
CGType -> Ptr a -> IO ()
foreign import ccall "g_boxed_copy" g_boxed_copy ::
CGType -> Ptr a -> IO (Ptr a)
-- | Construct a Haskell wrapper for the given boxed object. We make a
-- copy of the object.
newBoxed :: forall a. BoxedObject a => (ManagedPtr a -> a) -> Ptr a -> IO a
newBoxed constructor ptr = do
GType gtype <- boxedType (undefined :: a)
ptr' <- g_boxed_copy gtype ptr
fPtr <- newManagedPtr ptr' (boxed_free_helper gtype ptr')
return $! constructor fPtr
-- | Like 'newBoxed', but we do not make a copy (we "steal" the passed
-- object, so now it is managed by the Haskell runtime).
wrapBoxed :: forall a. BoxedObject a => (ManagedPtr a -> a) -> Ptr a -> IO a
wrapBoxed constructor ptr = do
GType gtype <- boxedType (undefined :: a)
fPtr <- newManagedPtr ptr (boxed_free_helper gtype ptr)
return $! constructor fPtr
-- | Like 'copyBoxed', but acting directly on a pointer, instead of a
-- managed pointer.
copyBoxedPtr :: forall a. BoxedObject a => Ptr a -> IO (Ptr a)
copyBoxedPtr ptr = do
GType gtype <- boxedType (undefined :: a)
g_boxed_copy gtype ptr
foreign import ccall "g_boxed_free" g_boxed_free ::
CGType -> Ptr a -> IO ()
-- | Free the memory associated with a boxed object
freeBoxed :: forall a. BoxedObject a => a -> IO ()
freeBoxed boxed = do
GType gtype <- boxedType (undefined :: a)
ptr <- unsafeManagedPtrGetPtr boxed
g_boxed_free gtype ptr
touchManagedPtr boxed
-- | Disown a boxed object, that is, do not free the associated
-- foreign GBoxed when the Haskell object gets garbage
-- collected. Returns the pointer to the underlying `BoxedObject`.
disownBoxed :: BoxedObject a => a -> IO (Ptr a)
disownBoxed = disownManagedPtr
-- | Wrap a pointer, taking ownership of it.
wrapPtr :: WrappedPtr a => (ManagedPtr a -> a) -> Ptr a -> IO a
wrapPtr constructor ptr = do
fPtr <- case wrappedPtrFree of
Nothing -> newManagedPtr_ ptr
Just finalizer -> newManagedPtr' finalizer ptr
return $! constructor fPtr
-- | Wrap a pointer, making a copy of the data.
newPtr :: WrappedPtr a => (ManagedPtr a -> a) -> Ptr a -> IO a
newPtr constructor ptr = do
ptr' <- wrappedPtrCopy ptr
fPtr <- case wrappedPtrFree of
Nothing -> newManagedPtr_ ptr
Just finalizer -> newManagedPtr' finalizer ptr'
return $! constructor fPtr
-- | Make a copy of a wrapped pointer using @memcpy@ into a freshly
-- allocated memory region of the given size.
copyPtr :: WrappedPtr a => Int -> Ptr a -> IO (Ptr a)
copyPtr size ptr = do
ptr' <- wrappedPtrCalloc
memcpy ptr' ptr size
return ptr'