[Haskell] reader-like IO, parents of threads

Frederik Eaton frederik at a5.repetae.net
Sun Oct 16 15:53:23 EDT 2005


John Meacham suggested that I should be a little more clear about the
semantics I'm seeking. Also, apparently it isn't possible to implement
writeTLRef/modifyTLRef with the data structure I gave:

> data TLRef a = TLR a (MVar (Map ThreadId a))
(the first argument is a default value, the second is a map storing
the values in each thread. The MVar is for safe concurrent access)

Without those functions, it looks a little more like the Reader monad
I'm comparing it to.

- What happens on fork? The child thread effectively gets a "copy" of
each TLRef in its parent. They have the same values, but modifying
them using withTLRef has no effect on the values in other threads.

- Can you pass a TLRef to a different thread? Yes, but the value it
holds will not be the same when it is dereferenced in a different
thread.

The problem with writeTLRef is that if a child thread looks up the
default value for an unbound reference by looking up the value in its
parent, but after calling forkIO the parent changes the value with
writeTLRef, then the child thread will get the wrong value. It is
supposed to only see the value which was stored in the reference at
the point where forkIO was called.

Also, for this reason, I think withTLRef would have to be implemented
by creating a separate thread with forkIO and waiting for it to
finish. This would avoid overwriting a value which other child threads
might still need to access.

Note that an e.g. "myParentThreadId" function isn't enough - what is
needed is a

parentThreadId :: ThreadId -> IO (Maybe ThreadId)

which can look up the parent of an arbitrary thread.

Alternatively, if 'forkIO' supported hooks to run before and/or after
forking, then a 'parentThreadId' function could be implemented from
that.

Frederik

On Sun, Oct 16, 2005 at 04:40:40AM -0700, Frederik Eaton wrote:
> Hi,
> 
> I'm trying to get MonadReader-like functionality in the IO monad. It
> doesn't appear possible implement it with the interfaces that
> Haskell98 or GHC provide. I'm looking for something like "thread-local
> variables". The interface could be something like this:
> 
> newTLRef :: a -> IO (TLRef a)
> withTLRef :: TLRef a -> a -> IO b -> IO b
> readTLRef :: TLRef a -> IO a
> writeTLRef :: TLRef a -> a -> IO ()
> modifyTLRef :: TLRef a -> (a -> a) -> IO ()
> 
> This would have a lot of uses. I am aware of the "Implicit
> Configurations" paper by Kiselyov and Shan, but a solution such as
> theirs which requires modifying the type signatures of all
> intermediate function calls is not suitable. I want to be able to say
> "run algorithm A using database D" without requiring all of the
> functions in algorithm A to know that databases are somehow involved. 
> One way to look at it is that I am seeking something like the
> type-based approach, but easier and with less explicit syntax; another
> way to look at it is that I am seeking something like a global IORef
> based approach, but more safe.
> 
> An implementation based on ThreadId-keyed maps is almost workable:
> 
> data TLRef a = TLR a (MVar (Map ThreadId a))
> 
> The problem with this is that while it is possible to find out the
> ThreadId of the current thread, it doesn't appear to be possible to
> get the ThreadId of the parent thread, which would be needed for
> values to be properly inherited.
> 
> Is there a way around this? Will there ever be standard support for
> either finding the thread id of the parent of the current thread, or
> for something like the thread-local references I have proposed?
> 
> Thanks,
> 
> Frederik
> _______________________________________________
> Haskell mailing list
> Haskell at haskell.org
> http://www.haskell.org/mailman/listinfo/haskell
> 


More information about the Haskell mailing list