@@ -5,6 +5,15 @@
5{-# LANGUAGE ForeignFunctionInterface #-}
5{-# LANGUAGE ForeignFunctionInterface #-}
6{-# LANGUAGE JavaScriptFFI #-}
6{-# LANGUAGE JavaScriptFFI #-}
9+-- | 'FastWeak' is a weak pointer to some value, and 'FastWeakTicket' ensures the value
10+-- referred to by a 'FastWeak' stays live while the ticket is held (live).
12+-- On GHC or GHCJS when not built with the @fast-weak@ cabal flag, 'FastWeak' is a wrapper
13+-- around the simple version of 'System.Mem.Weak.Weak' where the key and value are the same.
15+-- On GHCJS when built with the @fast-weak@ cabal flag, 'FastWeak' is implemented directly
16+-- in JS using @h$FastWeak@ and @h$FastWeakTicket@ which are a nonstandard part of the GHCJS RTS.
8module Reflex.FastWeak
17module Reflex.FastWeak
9 ( FastWeakTicket
18 ( FastWeakTicket
10 , FastWeak
19 , FastWeak
@@ -35,29 +44,57 @@ import System.Mem.Weak
37#ifdef GHCJS_FAST_WEAK
46#ifdef GHCJS_FAST_WEAK
47+-- | A 'FastWeak' which has been promoted to a strong reference. 'getFastWeakTicketValue'
48+-- can be used to get the referred to value without fear of @Nothing,
49+-- and 'getFastWeakTicketWeak' can be used to get the weak version.
51+-- Implemented by way of special support in the GHCJS RTS, @h$FastWeakTicket@.
38newtype FastWeakTicket a = FastWeakTicket JSVal
52newtype FastWeakTicket a = FastWeakTicket JSVal
54+-- | A reference to some value which can be garbage collected if there are only
55+-- weak references to the value left.
57+-- 'getFastWeakValue' can be used to try and obtain a strong reference to the value.
59+-- The value in a @FastWeak@ can also be kept alive by obtaining a 'FastWeakTicket' using
60+-- 'getFastWeakTicket' if the value hasn't been collected yet.
62+-- Implemented by way of special support in the GHCJS RTS, @h$FastWeak@.
40newtype FastWeak a = FastWeak JSVal
63newtype FastWeak a = FastWeak JSVal
42-- Just designed to mirror JSVal, so that we can unsafeCoerce between the two
65-- Just designed to mirror JSVal, so that we can unsafeCoerce between the two
43data Val a = Val { unVal :: a }
66data Val a = Val { unVal :: a }
45−-- | Coerce a JSVal that represents the heap object of a value of type 'a' into a value of type 'a'
68+-- | Coerce a JSVal that represents the heap object of a value of type @a@ into a value of type @a@
46unsafeFromRawJSVal :: JSVal -> a
69unsafeFromRawJSVal :: JSVal -> a
47unsafeFromRawJSVal v = unVal (unsafeCoerce v)
70unsafeFromRawJSVal v = unVal (unsafeCoerce v)
72+-- | Coerce a heap object of type @a@ into a 'JSVal' which represents that object.
49unsafeToRawJSVal :: a -> JSVal
73unsafeToRawJSVal :: a -> JSVal
50unsafeToRawJSVal v = unsafeCoerce (Val v)
74unsafeToRawJSVal v = unsafeCoerce (Val v)
76+-- | A 'FastWeak' which has been promoted to a strong reference. 'getFastWeakTicketValue'
77+-- can be used to get the referred to value without fear of @Nothing@,
78+-- and 'getFastWeakTicketWeak' can be used to get the weak version.
52data FastWeakTicket a = FastWeakTicket
79data FastWeakTicket a = FastWeakTicket
53 { _fastWeakTicket_val :: !a
80 { _fastWeakTicket_val :: !a
54 , _fastWeakTicket_weak :: {-# UNPACK #-} !(Weak a)
81 , _fastWeakTicket_weak :: {-# UNPACK #-} !(Weak a)
84+-- | A reference to some value which can be garbage collected if there are only weak references to the value left.
86+-- 'getFastWeakValue' can be used to try and obtain a strong reference to the value.
88+-- The value in a @FastWeak@ can also be kept alive by obtaining a 'FastWeakTicket' using 'getFastWeakTicket'
89+-- if the value hasn't been collected yet.
91+-- Synonymous with 'Weak'.
57type FastWeak a = Weak a
92type FastWeak a = Weak a
95+-- | Return the @a@ kept alive by the given 'FastWeakTicket'.
60−-- This needs to be in IO so we know that we've relinquished the ticket
97+-- This needs to be in IO so we know that we've relinquished the ticket.
61getFastWeakTicketValue :: FastWeakTicket a -> IO a
98getFastWeakTicketValue :: FastWeakTicket a -> IO a
62#ifdef GHCJS_FAST_WEAK
99#ifdef GHCJS_FAST_WEAK
63getFastWeakTicketValue t = do
100getFastWeakTicketValue t = do
@@ -69,6 +106,8 @@ foreign import javascript unsafe "$r = $1.val;" js_ticketVal :: FastWeakTicket a
69getFastWeakTicketValue = return . _fastWeakTicket_val
106getFastWeakTicketValue = return . _fastWeakTicket_val
109+-- | Get the value referred to by a 'FastWeak' if it hasn't yet been collected,
110+-- or @Nothing@ if it has been collected.
72getFastWeakValue :: FastWeak a -> IO (Maybe a)
111getFastWeakValue :: FastWeak a -> IO (Maybe a)
73#ifdef GHCJS_FAST_WEAK
112#ifdef GHCJS_FAST_WEAK
74getFastWeakValue w = do
113getFastWeakValue w = do
@@ -84,6 +123,9 @@ foreign import javascript unsafe "$r = ($1.ticket === null) ? null : $1.ticket.v
84getFastWeakValue = deRefWeak
123getFastWeakValue = deRefWeak
126+-- | Try to create a 'FastWeakTicket' for the given 'FastWeak' which will ensure the value referred
127+-- remains alive. Returns @Just@ if the value hasn't been collected
128+-- and a ticket can therefore be obtained, @Nothing@ if it's been collected.
87getFastWeakTicket :: forall a. FastWeak a -> IO (Maybe (FastWeakTicket a))
129getFastWeakTicket :: forall a. FastWeak a -> IO (Maybe (FastWeakTicket a))
88#ifdef GHCJS_FAST_WEAK
130#ifdef GHCJS_FAST_WEAK
89getFastWeakTicket w = do
131getFastWeakTicket w = do
@@ -103,8 +145,11 @@ getFastWeakTicket w = do
106−-- I think it's fine if this is lazy - it'll retain the 'a', but so would the output; we just need to make sure it's forced before we start relying on the associated FastWeak to actually be weak
148+-- | Create a 'FastWeakTicket' directly from a value, creating a 'FastWeak' in the process
149+-- which can be obtained with 'getFastWeakTicketValue'.
107mkFastWeakTicket :: a -> IO (FastWeakTicket a)
150mkFastWeakTicket :: a -> IO (FastWeakTicket a)
151+-- I think it's fine if this is lazy - it'll retain the 'a', but so would the output; we just need to make sure it's forced before we start relying on the
152+-- associated FastWeak to actually be weak
108#ifdef GHCJS_FAST_WEAK
153#ifdef GHCJS_FAST_WEAK
109mkFastWeakTicket v = js_fastWeakTicket (unsafeToRawJSVal v)
154mkFastWeakTicket v = js_fastWeakTicket (unsafeToRawJSVal v)
@@ -119,11 +164,16 @@ mkFastWeakTicket v = do
167+-- | Demote a 'FastWeakTicket'; which ensures the value is alive, to a 'FastWeak' which doesn't.
168+-- Note that unless the ticket for the same 'FastWeak' is held in some other way
169+-- the value might be collected immediately.
170+getFastWeakTicketWeak :: FastWeakTicket a -> IO (FastWeak a)
122-- Needs IO so that it can force the value - otherwise, could end up with a reference to the Ticket, which would retain the value
171-- Needs IO so that it can force the value - otherwise, could end up with a reference to the Ticket, which would retain the value
123#ifdef GHCJS_FAST_WEAK
172#ifdef GHCJS_FAST_WEAK
124−foreign import javascript unsafe "$r = $1.weak;" getFastWeakTicketWeak :: FastWeakTicket a -> IO (FastWeak a)
173+foreign import javascript unsafe "$r = $1.weak;" getFastWeakTicketWeak' :: FastWeakTicket a -> IO (FastWeak a)
174+{-# INLINE getFastWeakTicketWeak #-}
175+getFastWeakTicketWeak = getFastWeakTicketWeak'
126−getFastWeakTicketWeak :: FastWeakTicket a -> IO (FastWeak a)
127getFastWeakTicketWeak = return . _fastWeakTicket_weak
177getFastWeakTicketWeak = return . _fastWeakTicket_weak