What's thread-safe ?

For something to be thread-safe means it can be called from any thread.

A thread is a smaller unit of parallellism than a process. Operating systems such as OS/2 and Windows NT have had threading for ages; UNIX has lagged a bit in this area, probably due to its multiprocessing capabilities being good enough.

Anyway - in the UNIX terminology - processes are independent things (one doesn't influence the other) which share no data. Threads are a level lower than processes : threads are independent but they do share data - all threads in the same process run with the same memory space.

Once you know that, it's easy to figure out that all accesses to data (anything in the thread's memory really) need to be watched carefully, because any of the other processes can be accessing the same data at the same time.

This is not only true for the application program, but for all the libraries it uses.

So if you want to write a multithreaded application that uses a library (such as LessTif), then that library must also be thread-safe.

How to achieve it ?

The mechanisms for dealing with multithreading have been built into X11 Release 6, so all we have to do is use their stuff in the right way.

The X sources can be compiled to be thread-safe themselves. Unfortunately not all implementations of X are compiled in that way - none of the XFree implementations that I've seen were compiled to be thread-safe.

The mechanisms however are available so we can call them, independent of how they've been compiled.

The calls provided by X are :

A few simple rules must be followed to work with these.

All API calls to a widget must be masked with XtAppLock() and XtAppUnlock(). This means that a function such as XmListAddItem() should start with a call to XtAppLock and should end with a call to XtAppUnlock. Doing this ensures that only one thread can run in that application context at a time.

Second, global data (if you really need it - otherwise get rid of it) can be secured by using XtProcessLock. This will ensure that nothing else uses the data. Note that calling XtProcessLock twice will cause deadlock.

To avoid deadlocks with interactions of the calls mentioned above, the rule to obey is acquire AppLock first, only then ProcessLock. This means for instance that between XtProcessLock and XtProcessUnlock one should not make calls to Xt or other widget API's.

Details on how to handle this in LessTif

LessTif has a couple of tweaked versions of the Xt calls :

The latter two can be used to replace _XmAppLock if you haven't looked up the Application Context yet. Also the functions mentioned will behave right even on older systems with only X11 Release 5.

Here's an example :

Widget XmCreateLabel(Widget parent, char *name, Arg *arglist, Cardinal argcount)

{

Widget w;

_XmObjectLock(parent);

w = XtCreateWidget(name, xmLabelWidgetClass, parent, arglist, argcount);

_XmObjectUnlock(parent);

return w;

}

Checklist

  1. Avoid global variables like the plague.
  2. If you still have to use them, protect global variables with _XmProcessLock.
  3. Treat write access in class records as global storage.
  4. Protect all public API's with _XmAppLock or _XmObjectLock.
  5. Don't forget to unlock after acquiring a lock.
  6. Check whether calls to the C library (and other libraries) are thread-safe. Unsafe functions by design include ctime, getpwuid, readdir. Use their threadsafe equivalents.

This has been written using Amaya 2.0 running with LessTif.