@@ -5,6 +5,15 @@
5{-# LANGUAGE ForeignFunctionInterface #-}
5{-# LANGUAGE ForeignFunctionInterface #-}
6{-# LANGUAGE JavaScriptFFI #-}
6{-# LANGUAGE JavaScriptFFI #-}
9+-- |Contains 'FastWeak', a weak pointer to some value, and 'FastWeakTicket' which ensures the value referred to by a 'FastWeak' stays live while the ticket is
12+-- On GHC or GHCJS when not built with the @fast-weak@ cabal flag, 'FastWeak' is a wrapper around the simple version of 'System.Mem.Weak.Weak' where the key
13+-- and value are the same.
15+-- On GHCJS when built with the @fast-weak@ cabal flag, 'FastWeak' is implemented directly in JS using @h$FastWeak@ and @h$FastWeakTicket@ which are a
16+-- nonstandard part of the GHCJS RTS. __FIXME__
8module Reflex.FastWeak
17module Reflex.FastWeak
9 ( FastWeakTicket
18 ( FastWeakTicket
10 , FastWeak
19 , FastWeak
@@ -35,29 +44,52 @@ 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' can be used to get the referred to value without fear of @Nothing,
48+-- and 'getFastWeakTicketWeak' can be used to get the weak version.
50+-- Implemented by way of special support in the GHCJS RTS, @h$FastWeakTicket@.
38newtype FastWeakTicket a = FastWeakTicket JSVal
51newtype FastWeakTicket a = FastWeakTicket JSVal
53+-- |A reference to some value which can be garbage collected if there are only weak references to the value left.
55+-- 'getFastWeakValue' can be used to try and obtain a strong reference to the value.
57+-- The value in a @FastWeak@ can also be kept alive by obtaining a 'FastWeakTicket' using 'getFastWeakTicket' if the value hasn't been collected yet.
59+-- Implemented by way of special support in the GHCJS RTS, @h$FastWeak@.
40newtype FastWeak a = FastWeak JSVal
60newtype FastWeak a = FastWeak JSVal
42-- Just designed to mirror JSVal, so that we can unsafeCoerce between the two
62-- Just designed to mirror JSVal, so that we can unsafeCoerce between the two
43data Val a = Val { unVal :: a }
63data 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'
65+-- | Coerce a JSVal that represents the heap object of a value of type @a@ into a value of type @a@
46unsafeFromRawJSVal :: JSVal -> a
66unsafeFromRawJSVal :: JSVal -> a
47unsafeFromRawJSVal v = unVal (unsafeCoerce v)
67unsafeFromRawJSVal v = unVal (unsafeCoerce v)
69+-- | Coerce a heap object of type @a@ into a 'JSVal' which represents that object.
49unsafeToRawJSVal :: a -> JSVal
70unsafeToRawJSVal :: a -> JSVal
50unsafeToRawJSVal v = unsafeCoerce (Val v)
71unsafeToRawJSVal v = unsafeCoerce (Val v)
73+-- |A 'FastWeak' which has been promoted to a strong reference. 'getFastWeakTicketValue' can be used to get the referred to value without fear of @Nothing,
74+-- and 'getFastWeakTicketWeak' can be used to get the weak version.
52data FastWeakTicket a = FastWeakTicket
75data FastWeakTicket a = FastWeakTicket
53 { _fastWeakTicket_val :: !a
76 { _fastWeakTicket_val :: !a
54 , _fastWeakTicket_weak :: {-# UNPACK #-} !(Weak a)
77 , _fastWeakTicket_weak :: {-# UNPACK #-} !(Weak a)
80+-- |A reference to some value which can be garbage collected if there are only weak references to the value left.
82+-- 'getFastWeakValue' can be used to try and obtain a strong reference to the value.
84+-- The value in a @FastWeak@ can also be kept alive by obtaining a 'FastWeakTicket' using 'getFastWeakTicket' if the value hasn't been collected yet.
86+-- Synonymous with 'Weak'.
57type FastWeak a = Weak a
87type FastWeak a = Weak a
90+-- |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
92+-- This needs to be in IO so we know that we've relinquished the ticket.
61getFastWeakTicketValue :: FastWeakTicket a -> IO a
93getFastWeakTicketValue :: FastWeakTicket a -> IO a
62#ifdef GHCJS_FAST_WEAK
94#ifdef GHCJS_FAST_WEAK
63getFastWeakTicketValue t = do
95getFastWeakTicketValue t = do
@@ -69,6 +101,7 @@ foreign import javascript unsafe "$r = $1.val;" js_ticketVal :: FastWeakTicket a
69getFastWeakTicketValue = return . _fastWeakTicket_val
101getFastWeakTicketValue = return . _fastWeakTicket_val
104+-- |Get the value referred to by a 'FastWeak' if it hasn't yet been collected, or @Nothing@ if it has been collected.
72getFastWeakValue :: FastWeak a -> IO (Maybe a)
105getFastWeakValue :: FastWeak a -> IO (Maybe a)
73#ifdef GHCJS_FAST_WEAK
106#ifdef GHCJS_FAST_WEAK
74getFastWeakValue w = do
107getFastWeakValue w = do
@@ -84,6 +117,8 @@ foreign import javascript unsafe "$r = ($1.ticket === null) ? null : $1.ticket.v
84getFastWeakValue = deRefWeak
117getFastWeakValue = deRefWeak
120+-- |Try to create a 'FastWeakTicket' for the given 'FastWeak' which will ensure the referred to value remains alive. Returns @Just@ if the value hasn't been
121+-- collected and a ticket can therefore be obtained, @Nothing@ if it's been collected.
87getFastWeakTicket :: forall a. FastWeak a -> IO (Maybe (FastWeakTicket a))
122getFastWeakTicket :: forall a. FastWeak a -> IO (Maybe (FastWeakTicket a))
88#ifdef GHCJS_FAST_WEAK
123#ifdef GHCJS_FAST_WEAK
89getFastWeakTicket w = do
124getFastWeakTicket w = do
@@ -103,8 +138,10 @@ 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
141+-- |Create a 'FastWeakTicket' directly from a value, creating a 'FastWeak' in the process which can be obtained with 'getFastWeakTicketValue'.
107mkFastWeakTicket :: a -> IO (FastWeakTicket a)
142mkFastWeakTicket :: a -> IO (FastWeakTicket a)
143+-- 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
144+-- associated FastWeak to actually be weak
108#ifdef GHCJS_FAST_WEAK
145#ifdef GHCJS_FAST_WEAK
109mkFastWeakTicket v = js_fastWeakTicket (unsafeToRawJSVal v)
146mkFastWeakTicket v = js_fastWeakTicket (unsafeToRawJSVal v)
@@ -119,11 +156,15 @@ mkFastWeakTicket v = do
159+-- |Demote a 'FastWeakTicket' which ensures the value is alive to a 'FastWeak' which doesn't. Note that unless the ticket or another for the same 'FastWeak' is
160+-- held some other way the value might be collected immediately.
161+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
162-- 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
163#ifdef GHCJS_FAST_WEAK
124−foreign import javascript unsafe "$r = $1.weak;" getFastWeakTicketWeak :: FastWeakTicket a -> IO (FastWeak a)
164+foreign import javascript unsafe "$r = $1.weak;" getFastWeakTicketWeak' :: FastWeakTicket a -> IO (FastWeak a)
165+{-# INLINE getFastWeakTicketWeak #-}
166+getFastWeakTicketWeak = getFastWeakTicketWeak'
126−getFastWeakTicketWeak :: FastWeakTicket a -> IO (FastWeak a)
127getFastWeakTicketWeak = return . _fastWeakTicket_weak
168getFastWeakTicketWeak = return . _fastWeakTicket_weak