Skip to content

Commit

Permalink
Expand documentation for unsafeThaw (#386)
Browse files Browse the repository at this point in the history
It now explains dangers of unsafeThaw. Fixes #139
  • Loading branch information
Shimuuar authored May 23, 2021
1 parent e7d09b8 commit 8a375d7
Show file tree
Hide file tree
Showing 5 changed files with 130 additions and 10 deletions.
28 changes: 26 additions & 2 deletions vector/src/Data/Vector.hs
Original file line number Diff line number Diff line change
Expand Up @@ -2074,8 +2074,32 @@ unsafeFreeze :: PrimMonad m => MVector (PrimState m) a -> m (Vector a)
{-# INLINE unsafeFreeze #-}
unsafeFreeze = G.unsafeFreeze

-- | /O(1)/ Unsafely convert an immutable vector to a mutable one without
-- copying. The immutable vector may not be used after this operation.
-- | /O(1)/ Unsafely convert an immutable vector to a mutable one
-- without copying. Note that this is very dangerous function and
-- generally it's only safe to read from resulting vector. In which
-- case immutable vector could be used safely as well.
--
-- Problem with mutation happens because GHC has a lot of freedom to
-- introduce sharing. As a result mutable vectors produced by
-- @unsafeThaw@ may or may not share same underlying buffer. For
-- example:
--
-- > foo = do
-- > let vec = V.generate 10 id
-- > mvec <- V.unsafeThaw vec
-- > do_something mvec
--
-- Here GHC could lift @vec@ outside of foo which means all calls to
-- @do_something@ will use same buffer with possibly disastrous
-- results. Whether such aliasing happens or not depends on program in
-- question, optimization levels, and GHC flags.
--
-- All in all attempts to modify vector after unsafeThaw falls out of
-- domain of software engineering and into realm of black magic, dark
-- rituals, and unspeakable horrors. Only advice that could be given
-- is: "don't attempt to mutate vector after unsafeThaw unless you
-- know how to prevent GHC from aliasing buffers accidentally. We
-- don't"
unsafeThaw :: PrimMonad m => Vector a -> m (MVector (PrimState m) a)
{-# INLINE unsafeThaw #-}
unsafeThaw = G.unsafeThaw
Expand Down
28 changes: 26 additions & 2 deletions vector/src/Data/Vector/Generic.hs
Original file line number Diff line number Diff line change
Expand Up @@ -2245,8 +2245,32 @@ freeze :: (PrimMonad m, Vector v a) => Mutable v (PrimState m) a -> m (v a)
{-# INLINE freeze #-}
freeze mv = unsafeFreeze =<< M.clone mv

-- | /O(1)/ Unsafely convert an immutable vector to a mutable one without
-- copying. The immutable vector may not be used after this operation.
-- | /O(1)/ Unsafely convert an immutable vector to a mutable one
-- without copying. Note that this is very dangerous function and
-- generally it's only safe to read from resulting vector. In which
-- case immutable vector could be used safely as well.
--
-- Problem with mutation happens because GHC has a lot of freedom to
-- introduce sharing. As a result mutable vectors produced by
-- @unsafeThaw@ may or may not share same underlying buffer. For
-- example:
--
-- > foo = do
-- > let vec = V.generate 10 id
-- > mvec <- V.unsafeThaw vec
-- > do_something mvec
--
-- Here GHC could lift @vec@ outside of foo which means all calls to
-- @do_something@ will use same buffer with possibly disastrous
-- results. Whether such aliasing happens or not depends on program in
-- question, optimization levels, and GHC flags.
--
-- All in all attempts to modify vector after unsafeThaw falls out of
-- domain of software engineering and into realm of black magic, dark
-- rituals, and unspeakable horrors. Only advice that could be given
-- is: "don't attempt to mutate vector after unsafeThaw unless you
-- know how to prevent GHC from aliasing buffers accidentally. We
-- don't"
unsafeThaw :: (PrimMonad m, Vector v a) => v a -> m (Mutable v (PrimState m) a)
{-# INLINE_FUSED unsafeThaw #-}
unsafeThaw = stToPrim . basicUnsafeThaw
Expand Down
28 changes: 26 additions & 2 deletions vector/src/Data/Vector/Primitive.hs
Original file line number Diff line number Diff line change
Expand Up @@ -1716,8 +1716,32 @@ unsafeFreeze :: (Prim a, PrimMonad m) => MVector (PrimState m) a -> m (Vector a)
{-# INLINE unsafeFreeze #-}
unsafeFreeze = G.unsafeFreeze

-- | /O(1)/ Unsafely convert an immutable vector to a mutable one without
-- copying. The immutable vector may not be used after this operation.
-- | /O(1)/ Unsafely convert an immutable vector to a mutable one
-- without copying. Note that this is very dangerous function and
-- generally it's only safe to read from resulting vector. In which
-- case immutable vector could be used safely as well.
--
-- Problem with mutation happens because GHC has a lot of freedom to
-- introduce sharing. As a result mutable vectors produced by
-- @unsafeThaw@ may or may not share same underlying buffer. For
-- example:
--
-- > foo = do
-- > let vec = V.generate 10 id
-- > mvec <- V.unsafeThaw vec
-- > do_something mvec
--
-- Here GHC could lift @vec@ outside of foo which means all calls to
-- @do_something@ will use same buffer with possibly disastrous
-- results. Whether such aliasing happens or not depends on program in
-- question, optimization levels, and GHC flags.
--
-- All in all attempts to modify vector after unsafeThaw falls out of
-- domain of software engineering and into realm of black magic, dark
-- rituals, and unspeakable horrors. Only advice that could be given
-- is: "don't attempt to mutate vector after unsafeThaw unless you
-- know how to prevent GHC from aliasing buffers accidentally. We
-- don't"
unsafeThaw :: (Prim a, PrimMonad m) => Vector a -> m (MVector (PrimState m) a)
{-# INLINE unsafeThaw #-}
unsafeThaw = G.unsafeThaw
Expand Down
28 changes: 26 additions & 2 deletions vector/src/Data/Vector/Storable.hs
Original file line number Diff line number Diff line change
Expand Up @@ -1779,8 +1779,32 @@ unsafeFreeze
{-# INLINE unsafeFreeze #-}
unsafeFreeze = G.unsafeFreeze

-- | /O(1)/ Unsafely convert an immutable vector to a mutable one without
-- copying. The immutable vector may not be used after this operation.
-- | /O(1)/ Unsafely convert an immutable vector to a mutable one
-- without copying. Note that this is very dangerous function and
-- generally it's only safe to read from resulting vector. In which
-- case immutable vector could be used safely as well.
--
-- Problem with mutation happens because GHC has a lot of freedom to
-- introduce sharing. As a result mutable vectors produced by
-- @unsafeThaw@ may or may not share same underlying buffer. For
-- example:
--
-- > foo = do
-- > let vec = V.generate 10 id
-- > mvec <- V.unsafeThaw vec
-- > do_something mvec
--
-- Here GHC could lift @vec@ outside of foo which means all calls to
-- @do_something@ will use same buffer with possibly disastrous
-- results. Whether such aliasing happens or not depends on program in
-- question, optimization levels, and GHC flags.
--
-- All in all attempts to modify vector after unsafeThaw falls out of
-- domain of software engineering and into realm of black magic, dark
-- rituals, and unspeakable horrors. Only advice that could be given
-- is: "don't attempt to mutate vector after unsafeThaw unless you
-- know how to prevent GHC from aliasing buffers accidentally. We
-- don't"
unsafeThaw
:: (Storable a, PrimMonad m) => Vector a -> m (MVector (PrimState m) a)
{-# INLINE unsafeThaw #-}
Expand Down
28 changes: 26 additions & 2 deletions vector/src/Data/Vector/Unboxed.hs
Original file line number Diff line number Diff line change
Expand Up @@ -1812,8 +1812,32 @@ unsafeFreeze :: (Unbox a, PrimMonad m) => MVector (PrimState m) a -> m (Vector a
{-# INLINE unsafeFreeze #-}
unsafeFreeze = G.unsafeFreeze

-- | /O(1)/ Unsafely convert an immutable vector to a mutable one without
-- copying. The immutable vector may not be used after this operation.
-- | /O(1)/ Unsafely convert an immutable vector to a mutable one
-- without copying. Note that this is very dangerous function and
-- generally it's only safe to read from resulting vector. In which
-- case immutable vector could be used safely as well.
--
-- Problem with mutation happens because GHC has a lot of freedom to
-- introduce sharing. As a result mutable vectors produced by
-- @unsafeThaw@ may or may not share same underlying buffer. For
-- example:
--
-- > foo = do
-- > let vec = V.generate 10 id
-- > mvec <- V.unsafeThaw vec
-- > do_something mvec
--
-- Here GHC could lift @vec@ outside of foo which means all calls to
-- @do_something@ will use same buffer with possibly disastrous
-- results. Whether such aliasing happens or not depends on program in
-- question, optimization levels, and GHC flags.
--
-- All in all attempts to modify vector after unsafeThaw falls out of
-- domain of software engineering and into realm of black magic, dark
-- rituals, and unspeakable horrors. Only advice that could be given
-- is: "don't attempt to mutate vector after unsafeThaw unless you
-- know how to prevent GHC from aliasing buffers accidentally. We
-- don't"
unsafeThaw :: (Unbox a, PrimMonad m) => Vector a -> m (MVector (PrimState m) a)
{-# INLINE unsafeThaw #-}
unsafeThaw = G.unsafeThaw
Expand Down

0 comments on commit 8a375d7

Please sign in to comment.