[Haskell] reader-like IO, parents of threads

Simon Marlow simonmar at microsoft.com
Tue Oct 18 06:47:29 EDT 2005


It seems that you can do this as long as you provide your own version of
forkIO, but not if you want to use the built-in forkIO.

One could argue that getting the parent ThreadId is something that
should be supported natively by forkIO, and I might be inlined to agree.
Unfortunately there are some subtleties: currently a ThreadId is
represented by a pointer to the thread itself, which causes the thread
to be kept alive.  This has implications not only for space leaks, but
also for reporting deadlock: if you have a ThreadId for a thread, you
can send it an exception with throwTo at any time, and hence the runtime
can never determine that the thread is deadlocked so it will never get
the NonTermination exception.  Perhaps we need two kinds of ThreadId: a
weak one for use in Maps, and a strong one that you can use with
throwTo.  But then building a Map in which some elements can be garbage
collected is a bit tricky (it can be done though; see our old Memo table
implementation in fptools/hslibs/util/Memo.hs).

Cheers,
	Simon

On 16 October 2005 20:53, Frederik Eaton wrote:

> 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
>> 
> _______________________________________________
> Haskell mailing list
> Haskell at haskell.org
> http://www.haskell.org/mailman/listinfo/haskell



More information about the Haskell mailing list