RE: [HOpenGL] HOpenGL and --enable-threaded-rts

This sounds like a lot of work and a porting nightmare (what do you mean Linux/Win32/HPUX/... doesn't have thread manipulation function X, it's available on FreeBSD/Win32/... What if there are other forms of thread-local state (e.g., errno)? What about setjmp/longjmp?).
Yes, these are all problems. However, there is a nice abstraction of the OS thread API in GHC's RTS, thanks to Sigbjorn. So I'm sure this API could be extended to include some thread-local state operations.
The only viable solution I can see is to provide a way to capture the calling thread (as a first class entity) when you call into Haskell and to explicitly specify whcih thread to use when you call out from Haskell. (Hmmm, sounds like callcc for C :-))
The trouble is, that is *way* too much overhead for a C call. HOpenGL does lots of these, and I strongly suspect that adding a full OS-thread context switch (well two, including the return) for each one would be a killer. Cheers, Simon

I'm slowly losing track of this discussion... My initial suggestion was that it is guaranteed that the same OS thread which created the f.i.w. thunk is used to call back to Haskell for *this* wrapped function. There is no overhead for calls Haskell->C, only for the comparatively rare case of C->Haskell. What's wrong with this? Cheers, S.

Yes, these are all problems. However, there is a nice abstraction of the OS thread API in GHC's RTS, thanks to Sigbjorn. So I'm sure this API could be extended to include some thread-local state operations.
A further piece of what one might call thread local state is 'recursive locks' like those found in Java. With normal locks, if a thread executes this: take(lock); take(lock); ... release(lock); release(lock); then the 2nd call to take will block because a thread already has the lock. With recursive locks, the implementation of take records who has the lock and just increments a counter if the same thread takes the lock again. Likewise, release decrements the counter and only releases the lock when the counter reaches 0. And arbitrary user code and libraries are free to implement all kinds of code that depends on the current thread id. I'll bet you're going to see a lot of cases like this.
The only viable solution I can see is to provide a way to capture the calling thread (as a first class entity) when you call into Haskell and to explicitly specify whcih thread to use when you call out from Haskell. (Hmmm, sounds like callcc for C :-))
The trouble is, that is *way* too much overhead for a C call. HOpenGL does lots of these, and I strongly suspect that adding a full OS-thread context switch (well two, including the return) for each one would be a killer.
So don't do it for every foreign function call - only do it for the ones that request it. Here's the implementation I imagine: For foreign imports and exports that have not requested any special thread behaviour, do exactly what GHC currently does. Overhead == 0. For foreign exports that have requested thread capture, the call goes like this: 1) Get a thread from GHC's thread pool 2) Allocate a first-class C-thread object on the Haskell heap. and fill in the details for this thread. 3) Add the normal foreign export function arguments plus a pointer to the C thread object to the GHC thread and make it runnable. 4) Block this thread so that it is ready to use later. 5) When C function returns, do so in the first-class C-thread object. For foreign imports that have requested explicit thread choice, the call goes like this: 1) Get the C-thread object (it's an argument to the Haskell function so this is easy), perform suitable sanity checks (i.e., not already in use). 2) Marshall argumens to C function into the C-thread. 3) Unblock the C thread, block the Haskell thread waiting for response. 4) When C function returns, context switch back to a GHC thread. Overhead for foreign export is higher. Overhead for foreign import is not much different from existing safe foreign imports. Overhead only occurs if you request this feature. -- Alastair Reid reid@cs.utah.edu http://www.cs.utah.edu/~reid/

G'day all. On Tue, Jun 18, 2002 at 05:58:15PM +0100, Alastair Reid wrote:
A further piece of what one might call thread local state is 'recursive locks' like those found in Java.
Recursive locks arguably should be part of the lock abstraction, not "thread local state ". Since all it costs is another counter, it's almost trivial for the lock implementation (usually the OS) to implement it, of only as an option. For comparison: all Win32 built-in locks (Win32 confusingly differentiates "mutexes" and "critical sections" even though they are essentially the same thing) are recursive. Under all pthreads implementations that I'm aware of, THREAD_MUTEX_RECURSIVE is supported. I don't know enough about any other platforms to comment, but my bet is you'll find it pretty much everywhere. I think you need to look no further than the thread id itself for "thread local state". As soon as you want to do anything with the thread id other than compare equality (even Ord-type comparison is not supported under pthreads), you have thread local state. I don't mean to detract from the fine work which the HOpenGL people have achieved, but I think that binding to the C implementation of GLUT was, in retrospect, a mistake. Binding to foreign language-specific frameworks in general is a mistake, IMO. Today it's only threads which you may be able to hack around, but tomorrow, your called-back function will want to throw an exception, or something even hairier will turn up and you'll be back where you started. Sorry for the pessimism, but this is bitter experience talking. Cheers, Andrew Bromage
participants (4)
-
Alastair Reid
-
Andrew J Bromage
-
Simon Marlow
-
Sven Panne