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