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