Magic Mailing List |
|
From: R. Timothy Edwards (tim AT stravinsky DOT jhuapl.edu) Date: Tue Feb 26 2002 - 10:13:26 EST
Dear Michael, > Could I just remind you that X11 is not thread-safe. I learned > this the hard way -- it is not clearly documented. And, it > does make multi-threading Magic a bit more work: a thread > running DRC could not directly cause paint. I found out a way to get around the non-thread-safe issue using a tricky bit of interrupt signalling. It took a bit more work due to the lack of documentation, but I eventually determined that the lack of thread-safeness is due to the fact that X splits "events" up into two types: one type gets caught by the X event loop and processed by the user, the other gets processed from inside XNextEvent(). So while one would normally thing that the event loop thread should protect itself by: while(1) { XtNextEvent(&event); pthread_mutex_lock(&mutex); XtDispatchEvent(&event); pthread_mutex_unlock(&mutex); } it turns out that XtNextEvent() (or XNextEvent()) appends things to the X11 event queue itself, and this is what causes X11 to suddenly produce "unexpected async reply" messages and lock up the program. But the blocking select() function is inside XNextEvent, so you can't put the mutex lock around that. The way around it is to make sure that any other thread that wants to make X11 calls first interrupts XNextEvent, forcing it to stall until it can aquire the mutex lock again. So the X event loop becomes: . . . xloopid = getpid(); signal(SIGIO, xloop_lockout); while(1) { XtNextEvent(&event); pthread_mutex_lock(&mutex); XtDispatchEvent(&event); pthread_mutex_unlock(&mutex); } . . . and the routines needed to handle the interrupting are: /*----------------------------------------------*/ void xloop_lockout(int signum) { pthread_mutex_lock(&mutex); pthread_mutex_unlock(&mutex); } /*----------------------------------------------*/ void xloop_wait() { kill(xloopid, SIGIO); } /*----------------------------------------------*/ Any other thread making X11 calls needs to do the following sequence of calls: . . . pthread_mutex_lock(&mutex); xloop_wait(); /* Make X11 calls here */ pthread_mutex_unlock(&mutex); . . . The pthread_mutex_lock() call waits for any other thread to finish. Then xloop_wait() causes the X event loop to break out of XNextEvent() and run xloop_lockout(). xloop_lockout() attempts to grab the mutex lock, but can't do it until the other thread finishes making X11 calls. So the lock/unlock pair in xloop_lockout() stalls the X event loop, preventing it from making "hidden" X11 calls while the other thread is doing the same. I implemented this code in magic, changing the "X11Helper" from a forked process into a separate thread. Most of the X11 calls which originate from outside the X event loop thread are wrapped in a GrLock/GrUnlock pair, which makes a convenient place to put the mutex_lock/mutex_unlock calls. ---Tim
|
|