On Fri, 2007-06-15 at 01:03 +0200, Udo Stenzel wrote:
> Greetings,
> 
> I was trying to understand the magic inside Data.Binary, and found two
> somewhat suspicious uses of inlinePerformIO, which imho has a far too
> innocuous name:

It's not really supposed to be a public api. We've decided to rename
Data.ByteString.Base to .Internal to make that fact clearer.

> | toLazyByteString :: Builder -> L.ByteString
> | toLazyByteString m = S.LPS $ inlinePerformIO $ do
> |     buf <- newBuffer defaultSize
> |     return (runBuilder (m `append` flush) (const []) buf)
> 
> Why is this safe?  Considering the GHC implementation of IO, isn't there
> a real danger that 'newBuffer defaultSize' is floated out and therefore
> every invocation of 'toLazyByteString' starts out with the same buffer?
> Isn't that exactly the reason why unsafePerformIO and runST are declared
> NOINLINE?

Yes, that seems very suspicious to me.

> The other occurence is:
> 
> | unsafeLiftIO :: (Buffer -> IO Buffer) -> Builder
> | unsafeLiftIO f =  Builder $ \ k buf -> inlinePerformIO $ do
> |     buf' <- f buf
> |     return (k buf')
> 
> which might be safe, since 'f buf' cannot float out of the lambda which
> binds 'buf', but still, all this stuff is inlined, something constant
> might get plugged in the place of buf and the result might be floated
> out to give us an even harder to find Heisenbug.

I think this is safe because of the continuation k. In the Builder
monoid, the continuation takes the place of the IO state token that gets
single threaded through everything.

Duncan

_______________________________________________
Haskell-Cafe mailing list
Haskell-Cafe@haskell.org
http://www.haskell.org/mailman/listinfo/haskell-cafe

Reply via email to