MAGIC Magic Mailing List
 
 

From: R. Timothy Edwards (tim AT stravinsky DOT jhuapl.edu)
Date: Tue Feb 26 2002 - 10:13:26 EST

  • Next message: erik peterson: "Re: Jeff Solomon's Future of Magic"

    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
    


  •  
     
    Questions? Contact Rajit Manohar
    cornell logo