John's approach is the right one, but note that the "STM setup work" will be visible to other transactions. Any work done there must be considered consistent as it will be committed. For instance if we have the following: atomically $ do m <- readTVar mutex check (not m) writeTVar m True writeTVar a True ffiCall atomically $ do writeTVar b True writeTVar m False If it should be the case that `a` is true only if `b` is true then we could run into problems with some other transaction that is not concerned with the `ffiCall` but is concerned about `a` and `b`. Another approach that might be viable is to just fix your foreign call's interface by putting a lock in the foreign code. If it really is a pure call, then there is no danger of deadlocking there. This will then be as safe as any foreign call inside STM. There are dangers here of course. One particular danger is if your call takes multiple arguments that need to be consistent, you can see inconsistency inside a failed transaction before the runtime system has determined that it is a failed transaction. For instance: atomically $ do x <- readTVar a y <- readTVar b return $ unsafePerformIO $ ffiCall x y Say `x` is an array and `y` is an index into that array. Even if your transactions keep these consistent, you could send inconsistent data to the foreign call. You can fix this by checking (at the value level) the integrity of the data or changing the granularity of the data and putting values `x` and `y` into the same `TVar`. Beyond this problem I don't know what other issues foreign calls inside STM face, but I would love to hear what others know. Ryan On Mon, Feb 3, 2014 at 6:17 AM, John Wiegley <johnw@fpcomplete.com> wrote:
Rob Leslie <rob@mars.org> writes:
Is this expected behavior? I realize performing any IO within an STM transaction is inherently unsafe, but I am a little surprised that 'bracket' fails here.
Is there a better way to do what I'm trying to accomplish? How can I provide a pure interface to my foreign function that will work within an STM transaction?
If your foreign function "depends on some global state in a way that compromises thread safety", I would hestitate to simply tell the FFI that it is pure.
Instead, you can break up your STM transaction into two pieces: A first part that sets up the transaction and sets a guard variable so other transactions cannot proceed until the second part is completed, then perform the FFI call, then the second part of the transaction. For example:
atomically $ do m <- readTVar mutex check (not m) writeTVar m True ... do whatever STM setup work is needed here ... ffiCall atomically $ do ... do whatever STM cleanup work is needed here ... writeTVar m False
This way your ffiCall is conceptually within a larger transactional block.
John _______________________________________________ Haskell-Cafe mailing list Haskell-Cafe@haskell.org http://www.haskell.org/mailman/listinfo/haskell-cafe