-- |
-- 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 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 s = io $ do
    R.preserveObject s'
    s' `addFinalizer` (R.releaseObject (R.unsafeRelease s'))
    return s'
  where
    s' = R.unsafeRelease s

-- | 'automatic' for 'SomeSEXP'.
automaticSome :: MonadR m => R.SomeSEXP s -> m (R.SomeSEXP G)
automaticSome (SomeSEXP s) = io  $ do
    R.preserveObject s'
    s' `addFinalizer` (R.releaseObject s')
    return $ SomeSEXP s'
  where
    s' = R.unsafeRelease s