[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