symbolize-1.0.3.0: src/Symbolize/WeakSymbol.hs
{-# LANGUAGE GHC2021 #-}
{-# LANGUAGE UnboxedTuples #-}
{-# LANGUAGE MagicHash #-}
module Symbolize.WeakSymbol (WeakSymbol, new, deref) where
import Data.Array.Byte (ByteArray (ByteArray))
import GHC.Exts (ByteArray#, StableName#, Weak#, deRefWeak#, mkWeak#, makeStableName#)
import GHC.IO (IO (IO))
import Symbolize.Accursed qualified as Accursed
-- Inside the WeakSymbol
-- we keep:
-- - A weak pointer to the underlying ByteArray#.
-- This weak pointer will be invalidated (turn into a 'tombstone')
-- when the final instance of this symbol is GC'd
-- - A `StableName` for the same ByteArray#
-- This ensures we have a stable hash even in the presence of the ByteArray#
-- being moved around by the GC.
-- We never read it after construction,
-- but by keeping it around until the WeakSymbol is removed by the finalizer,
-- we ensure that future calls to `makeStableName` return the same hash.
data WeakSymbol where
WeakSymbol# :: Weak# ByteArray# -> StableName# ByteArray# -> WeakSymbol
-- | Create a new weak symbol
-- based on the given symbol content ByteArray
-- and finalizer to run when the weak symbol
-- is no longer needed.
new :: ByteArray# -> IO () -> IO WeakSymbol
{-# INLINE new #-}
new ba# (IO finalizer#) =
-- SAFETY: This should even be safe
-- in the presence of inlining, CSE and full laziness
--
-- because the result is outwardly pure
-- and the finalizer we use is idempotent
IO $ \s1 -> case mkWeak# ba# ba# finalizer# s1 of
(# s2, weak# #) ->
case makeStableName# ba# s2 of
(# s3, sname# #) ->
(# s3, WeakSymbol# weak# sname# #)
-- | Attempt to get back the containing ByteArray#
-- by looking inside this `WeakSymbol`
--
-- Returns `Nothing` if it was GC'd in the meantime
-- (which may be before, after or concurrently with when the finalizer runs)
deref :: WeakSymbol -> Maybe ByteArray
{-# INLINE deref #-}
deref (WeakSymbol# w _sn) =
-- SAFETY: This should even be safe
-- in the presence of inlining, CSE and full laziness;
Accursed.accursedUnutterablePerformIO $ IO $ \s ->
case deRefWeak# w s of
(# s1, flag, p #) -> case flag of
0# -> (# s1, Nothing #)
_ -> (# s1, Just (ByteArray p) #)