Question about touchForeignPtr

I have the following code: IOVector n e = IOVector !ConjEnum !Int (ForeignPtr e)! (Ptr e)! Int! newtype Vector n e = IOVector n e unsafeAtVector :: Vector n e -> Int -> e unsafeAtVector (Vector (IOVector c _ f p inc)) i = let g = if c == Conj then conjugate else id in inlinePerformIO $ do e <- peekElemOff p (i*inc) io <- touchForeignPtr f let e' = g e e' `seq` io `seq` return e' {-# INLINE unsafeAtVector #-} The Ptr, 'p' is derived from the ForeignPtr, 'f'. For some offset, 'o', it is defined as: p = unsafeForeignPtrToPtr f `advancePtr` o The "touchForeignPtr" is there to keep the garbage collector from deallocating the memory before we have a chance to read 'e'. My question is the following: Is the `seq` on `io` necessary (from a safety standpoint)? Or am I just being paranoid? Thanks in advance for any help, Patrick

On Sun, 2009-01-11 at 17:43 -0800, Patrick Perry wrote:
The "touchForeignPtr" is there to keep the garbage collector from deallocating the memory before we have a chance to read 'e'. My question is the following: Is the `seq` on `io` necessary (from a safety standpoint)? Or am I just being paranoid?
touchForeignPtr :: ForeignPtr a -> IO () There is no need to seq the returned () value. It's the side effect of touchForeignPtr that provides the guarantee that you're after. So I think you could use: ... in inlinePerformIO $ do e <- peekElemOff p (i*inc) touchForeignPtr f return $! g e By the way, in your: IOVector n e = IOVector !ConjEnum !Int (ForeignPtr e)! (Ptr e)! Int! Do the (ForeignPtr e) and the (Ptr e) point to the same thing? They appear to be related because you dereference p but touch f. It used to be the ForeignPtr was slower to dereference than a Ptr and so caching it like this could make it faster (but then requires lots of careful thought about touchForeignPtr). These days ForeignPtr is just as fast and so you can use withForeignPtr and never have to worry about touchForeignPtr. You'll notice ByteString works this way. Duncan

Thanks for your help, Duncan. On Jan 12, 2009, at 6:10 AM, Duncan Coutts wrote:
Do the (ForeignPtr e) and the (Ptr e) point to the same thing? They appear to be related because you dereference p but touch f.
It used to be the ForeignPtr was slower to dereference than a Ptr and so caching it like this could make it faster (but then requires lots of careful thought about touchForeignPtr). These days ForeignPtr is just as fast and so you can use withForeignPtr and never have to worry about touchForeignPtr. You'll notice ByteString works this way.
Often they point to the same thing, but not always. If they pointed to the same thing, I wouldn't store p separately, but often p has a nonzero offset from f. You either have to store the offset or cache the pointer. I found that it was both faster and simpler to cache p rather than the offset. Patrick

Patrick Perry wrote:
I have the following code:
IOVector n e = IOVector !ConjEnum !Int (ForeignPtr e)! (Ptr e)! Int! newtype Vector n e = IOVector n e
unsafeAtVector :: Vector n e -> Int -> e unsafeAtVector (Vector (IOVector c _ f p inc)) i = let g = if c == Conj then conjugate else id in inlinePerformIO $ do e <- peekElemOff p (i*inc) io <- touchForeignPtr f let e' = g e e' `seq` io `seq` return e' {-# INLINE unsafeAtVector #-}
The Ptr, 'p' is derived from the ForeignPtr, 'f'. For some offset, 'o', it is defined as:
p = unsafeForeignPtrToPtr f `advancePtr` o
The "touchForeignPtr" is there to keep the garbage collector from deallocating the memory before we have a chance to read 'e'. My question is the following: Is the `seq` on `io` necessary (from a safety standpoint)? Or am I just being paranoid?
You're just being paranoid - touchForeignPtr returns a (), so seqing it is a no-op. Cheers, Simon
participants (3)
-
Duncan Coutts
-
Patrick Perry
-
Simon Marlow