[Haskell-cafe] STM and unsafePerformIO/bracket

Ryan Yates fryguybob at gmail.com
Mon Feb 3 14:59:58 UTC 2014


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 at fpcomplete.com> wrote:

> >>>>> Rob Leslie <rob at 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 at haskell.org
> http://www.haskell.org/mailman/listinfo/haskell-cafe
>
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://www.haskell.org/pipermail/haskell-cafe/attachments/20140203/a49fbcf7/attachment.html>


More information about the Haskell-Cafe mailing list