-- |
-- Copyright: (C) 2013 Amgen, Inc.
--
-- Facilities to get Haskell's garbage collector to manage the liveness of
-- values allocated on the R heap. By default, R values remain live so long as
-- the current region is extant. The R garbage collector may only free them
-- after the end of the region. Sometimes, this discipline incurs too high of
-- a memory usage and nested regions are not always a solution.
--
-- This module enables registering a callback with the GHC garbage collector. In
-- this way, when the GHC garbage collector detects that a value is no longer
-- live, we can notify the R garbage collector of this fact. The R garbage
-- collector is then free to deallocate the memory associated with the value
-- soon after that.
--
-- This module hence offers an alternative, more flexible memory management
-- discipline, at a performance cost. In particular, collections of many small,
-- short-lived objects are best managed using regions.

module Language.R.GC
  ( automatic
  , automaticSome
  ) where

import Control.Memory.Region
import Control.Monad.R.Class
import Control.Exception
import Foreign.R (SomeSEXP(..))
import qualified Foreign.R as R
import System.Mem.Weak (addFinalizer)

-- | Declare memory management for this value to be automatic. That is, the
-- memory associated with it may be freed as soon as the garbage collector
-- notices that it is safe to do so.
--
-- Values with automatic memory management are tagged with the global region.
-- The reason is that just like for other global values, deallocation of the
-- value can never be observed. Indeed, it is a mere "optimization" to
-- deallocate the value sooner - it would still be semantically correct to never
-- deallocate it at all.
automatic :: MonadR m => R.SEXP s a -> m (R.SEXP G a)
automatic :: forall (m :: * -> *) s (a :: SEXPTYPE).
MonadR m =>
SEXP s a -> m (SEXP G a)
automatic SEXP s a
s = forall (m :: * -> *) a. MonadR m => IO a -> m a
io forall a b. (a -> b) -> a -> b
$ forall a. IO a -> IO a
mask_ forall a b. (a -> b) -> a -> b
$ do
    forall s (a :: SEXPTYPE). SEXP s a -> IO ()
R.preserveObject forall {r}. SEXP r a
s'
    forall {r}. SEXP r a
s' forall key. key -> IO () -> IO ()
`addFinalizer` (forall s (a :: SEXPTYPE). SEXP s a -> IO ()
R.releaseObject (forall s (a :: SEXPTYPE) r. SEXP s a -> SEXP r a
R.unsafeRelease forall {r}. SEXP r a
s'))
    forall (m :: * -> *) a. Monad m => a -> m a
return forall {r}. SEXP r a
s'
  where
    s' :: SEXP r a
s' = forall s (a :: SEXPTYPE) r. SEXP s a -> SEXP r a
R.unsafeRelease SEXP s a
s

-- | 'automatic' for 'SomeSEXP'.
automaticSome :: MonadR m => R.SomeSEXP s -> m (R.SomeSEXP G)
automaticSome :: forall (m :: * -> *) s. MonadR m => SomeSEXP s -> m (SomeSEXP G)
automaticSome (SomeSEXP SEXP s a
s) = forall (m :: * -> *) a. MonadR m => IO a -> m a
io  forall a b. (a -> b) -> a -> b
$ forall a. IO a -> IO a
mask_ forall a b. (a -> b) -> a -> b
$ do
    forall s (a :: SEXPTYPE). SEXP s a -> IO ()
R.preserveObject forall {r}. SEXP r a
s'
    forall {r}. SEXP r a
s' forall key. key -> IO () -> IO ()
`addFinalizer` (forall s (a :: SEXPTYPE). SEXP s a -> IO ()
R.releaseObject forall {r}. SEXP r a
s')
    forall (m :: * -> *) a. Monad m => a -> m a
return forall a b. (a -> b) -> a -> b
$ forall s (a :: SEXPTYPE). SEXP s a -> SomeSEXP s
SomeSEXP forall {r}. SEXP r a
s'
  where
    s' :: SEXP r a
s' = forall s (a :: SEXPTYPE) r. SEXP s a -> SEXP r a
R.unsafeRelease SEXP s a
s