unsafePerformIO: is it correctly used?

When I was browsing the GHC documentation, I came across the following: If the I/O computation wrapped in unsafePerformIOfile:///C:/app/ghc-6.10.1/doc/libraries/base/System-IO-Unsafe.html#v%3Aunsaf... performs side effects, then the relative order in which those side effects take place (relative to the main I/O trunk, or other calls to unsafePerformIOfile:///C:/app/ghc-6.10.1/doc/libraries/base/System-IO-Unsafe.html#v%3Aunsaf...) is indeterminate. You have to be careful when writing and compiling modules that use unsafePerformIOfile:///C:/app/ghc-6.10.1/doc/libraries/base/System-IO-Unsafe.html#v%3Aunsaf... : - Use {-# NOINLINE foo #-} as a pragma on any function foo that calls unsafePerformIOfile:///C:/app/ghc-6.10.1/doc/libraries/base/System-IO-Unsafe.html#v%3Aunsaf.... If the call is inlined, the I/O may be performed more than once. - Use the compiler flag -fno-cse to prevent common sub-expression elimination being performed on the module, which might combine two side effects that were meant to be separate. A good example is using multiple global variables (like test in the example below). - Make sure that the either you switch off let-floating, or that the call to unsafePerformIOfile:///C:/app/ghc-6.10.1/doc/libraries/base/System-IO-Unsafe.html#v%3Aunsaf... cannot float outside a lambda. For example, if you say: f x = unsafePerformIO (newIORef []) you may get only one reference cell shared between all calls to f. Better would be f x = unsafePerformIO (newIORef [x]) because now it can't float outside the lambda. Now in Reactive, in e.g. the internal IVar.hs module, I see readIVar :: IVar a -> a readIVar (IVar v) = unsafePerformIO $ readMVar v At first sight this function is not following all the rules described above. Could this cause problems? Cheers, Peter

2008/11/20 Peter Verswyvelen
Now in Reactive, in e.g. the internal IVar.hs module, I see readIVar :: IVar a -> a readIVar (IVar v) = unsafePerformIO $ readMVar v At first sight this function is not following all the rules described above. Could this cause problems?
I cannot prove it formally, but I think it does not. readMVar does not have observable side-effects, assuming the MVar is never taken (which is what IVar's abstraction ensures). Luke

Hi Peter,
I'm glad to see you're getting into the Reactive code!
As a complement to Luke's answer:
I think readIVar is an exception to the usual guidelines below. The point
of the IVar type is that it can get written *at most once*, so any read will
yield the same value. It doesn't matter in this case whether the readMVar
computation is duplicated. It always has to yield the same result, namely
the only value that has been or will be written to the MVar, or bottom if no
value ever gets written. As Luke mentioned, the IVar interface does not
allow takeMVar to be called, except via readMVar, which replaces the value
after reading.
- Conal
2008/11/20 Peter Verswyvelen
When I was browsing the GHC documentation, I came across the following: If the I/O computation wrapped in unsafePerformIO performs side effects, then the relative order in which those side effects take place (relative to the main I/O trunk, or other calls to unsafePerformIO) is indeterminate. You have to be careful when writing and compiling modules that use unsafePerformIO:
- Use {-# NOINLINE foo #-} as a pragma on any function foo that calls unsafePerformIO. If the call is inlined, the I/O may be performed more than once. - Use the compiler flag -fno-cse to prevent common sub-expression elimination being performed on the module, which might combine two side effects that were meant to be separate. A good example is using multiple global variables (like test in the example below). - Make sure that the either you switch off let-floating, or that the call to unsafePerformIO cannot float outside a lambda. For example, if you say: f x = unsafePerformIO (newIORef []) you may get only one reference cell shared between all calls to f. Better would be f x = unsafePerformIO (newIORef [x]) because now it can't float outside the lambda.
Now in Reactive, in e.g. the internal IVar.hs module, I see
readIVar :: IVar a -> a readIVar (IVar v) = unsafePerformIO $ readMVar v
At first sight this function is not following all the rules described above. Could this cause problems?
Cheers, Peter
_______________________________________________ Reactive mailing list Reactive@haskell.org http://www.haskell.org/mailman/listinfo/reactive

Okay. But there are a couple of other functions that use unsafePerformIO.
Are all of these behaving nicely then? Just double checking :)
On Fri, Nov 21, 2008 at 8:22 AM, Conal Elliott
Hi Peter,
I'm glad to see you're getting into the Reactive code!
As a complement to Luke's answer:
I think readIVar is an exception to the usual guidelines below. The point of the IVar type is that it can get written *at most once*, so any read will yield the same value. It doesn't matter in this case whether the readMVar computation is duplicated. It always has to yield the same result, namely the only value that has been or will be written to the MVar, or bottom if no value ever gets written. As Luke mentioned, the IVar interface does not allow takeMVar to be called, except via readMVar, which replaces the value after reading.
- Conal
2008/11/20 Peter Verswyvelen
When I was browsing the GHC documentation, I came across the following:
If the I/O computation wrapped in unsafePerformIO performs side effects, then the relative order in which those side effects take place (relative to the main I/O trunk, or other calls to unsafePerformIO) is indeterminate. You have to be careful when writing and compiling modules that use unsafePerformIO:
- Use {-# NOINLINE foo #-} as a pragma on any function foo that calls unsafePerformIO. If the call is inlined, the I/O may be performed more than once. - Use the compiler flag -fno-cse to prevent common sub-expression elimination being performed on the module, which might combine two side effects that were meant to be separate. A good example is using multiple global variables (like test in the example below). - Make sure that the either you switch off let-floating, or that the call to unsafePerformIO cannot float outside a lambda. For example, if you say: f x = unsafePerformIO (newIORef []) you may get only one reference cell shared between all calls to f. Better would be f x = unsafePerformIO (newIORef [x]) because now it can't float outside the lambda.
Now in Reactive, in e.g. the internal IVar.hs module, I see
readIVar :: IVar a -> a readIVar (IVar v) = unsafePerformIO $ readMVar v
At first sight this function is not following all the rules described above. Could this cause problems?
Cheers, Peter
_______________________________________________ Reactive mailing list Reactive@haskell.org http://www.haskell.org/mailman/listinfo/reactive
participants (3)
-
Conal Elliott
-
Luke Palmer
-
Peter Verswyvelen