[Haskell-cafe] Locking, unsafePerformIO and bolt-on thread safety.

Erik Hesselink hesselink at gmail.com
Tue May 10 08:26:56 CEST 2011


My first thought was to create all of the lookup table lazily, and
create a pure top level binding for it. Something like:

lookupTable :: [Int]
lookupTable = map (\x -> unsafePerformIO (create_lookup_table x) `seq` x) [1..]

Then on a calculation you would index into this list and pass the
result as the last argument to perform_math. However, I'm not sure how
evaluation of thunks works with multiple threads; it might be possible
that this will occasionally execute create_lookup_table twice. I found
this paper [1] which suggests (in 3.5) that it does indeed, and they
suggest a primitive (justOnce :: a -> a) to prevent it. So I guess
this won't work, unless things have changed since 2005.

Erik

[1] http://research.microsoft.com/en-us/um/people/simonpj/papers/parallel/multiproc.pdf

On Tue, May 10, 2011 at 02:45, Jason Dusek <jason.dusek at gmail.com> wrote:
>  A friend is making bindings to a C library that offers some
>  fast math operations. The normal way to use the library is
>  like this:
>
>    int a = ...; int b = ...; int c = ...; int d = ...;
>    int x                    =  ...;
>    int m, n;
>    create_lookup_table(x);
>    m                        =  perform_math(a, b, x);
>    n                        =  perform_math(c, d, x);
>
>  We see that the lookup table for x must be created before we
>  can perform math in the field/ring/what-have-you defined by x.
>  Once we have created the table, though, we're done.
>
>  My friend would like to create a pure interface to this
>  library. One thought was to write an interface to perform_math
>  that checked if the table was created, created it if not, and
>  did all this while locking an MVar so that no other instance
>  could be called at the same time, trashing the table. Doing
>  this behind unsafePerformIO would seem to be the ticket.
>
>  We end up with an implementation like this:
>
>    module FastMath where
>
>    import Control.Concurrent
>    import Foreign
>    import Foreign.C
>
>
>    foreign import ccall create_lookup_table :: CInt -> IO ()
>    foreign import ccall perform_math :: CInt -> CInt -> CInt -> IO CInt
>
>    masterLock               =  unsafePeformIO (newMVar [CInt])
>
>    safe_perform_math a b x  =  do
>      list                  <-  takeMVar masterLock
>      toPut                 <-  if not (x `elem` list)
>                                  then do create_lookup_table x
>                                          return (x:list)
>                                  else    return list
>      result                <-  perform_math a b x
>      putMVar masterLock toPut
>      return result
>
>    performMath a b x = unsafePerformIO (safe_perform_math a b x)
>
>  This does not compile but I think it gets the point across. Is
>  this approach safe? The unsafePerformIO in conjunction with
>  locking has me worried.
>
> --
> Jason Dusek
> ()  ascii ribbon campaign - against html e-mail
> /\  www.asciiribbon.org   - against proprietary attachments
>
> _______________________________________________
> Haskell-Cafe mailing list
> Haskell-Cafe at haskell.org
> http://www.haskell.org/mailman/listinfo/haskell-cafe
>



More information about the Haskell-Cafe mailing list