With the upcome of JDK1.3, Sun introduced a new API, an extension of the Java Native Interface JNI, the JAWT, whose purpose is to allow drawing to AWT canvases from native code routines, without need of 'dirty hacks' to protect critical sections against concurrent access by other, Java internal drawing code. The necessity to do so is especially given on Unix platforms, where the X11/Motif environment, a by default thread-unsafe GUI system is the most popular graphical user interface solution.
As opposed to 'dirty hack' solutions, which usually took advantage of the accessability of internal lock symbols (awt_lock) outside the libawt, the JAWT enforces the obedience of some design rules - if not recognized, the protection facilities will simply fail.
Most restrictions arise from the fact, that there is no global lock symbol available (of course), because the idea is not and was never, to lock an UI globally, but just, to protect the accessed canvas against concurrency. Ideally, it doesn't matter from a API user's point of view, how those locks are actually implemented (on a X11 platform, they will probably be AWT-global, on Win32 systems on the other hand, they may be undefined at all). The immediate consequence is, that locking is available only, if the API is available, which is only the case at points where handles to canvases are. At the first glance, this is trivial, but the consequences are not, as we will see in the next chapter. There are several situations imaginable in a native code library, where UI-associated operations must be performed in a context where no canvas is at-hand.
Another constraint is imposed by the single-threaded concept of event-dispatch in the AWT: paint() methods are invoked by the EventQueue thread, as other event-handling methods are. The JAWT enforces drawing to take place inside the EventQueue thread - all attempts to acquire a canvas lock from outside this thread will result in a lock error. It is not always trivial to fulfill that requirement.
A more sophisticated native drawing library will probably try to use a canvas of non-default color depth. It is possible to do so, using AWT-built-in mechanisms: the GraphicsConfigTemplate class, which allows to specify selection criteria for GraphicsConfiguration instances from sets of configurations provided by devices. No native code is required, if no other requirements than color depth are to meet. If the library, for instance, needs a GL capable canvas, as the graph3d package does, the situation is more complicated: On X11-bases systems, criteria can reliably be specified by using the glXChooseVisual() C routine only - this has to be done in a native method implementation of a special GraphicsConfigTemplate. On the other hand, glXChooseVisual() is a function built on top of the non-threadsafe Xlib - so the call should be protected by a lock. As we pointed out in the introduction, this is only possible since JDK1.3, if a canvas handle is available - bad luck here. One could expect, that all queries of GraphicsConfigTemplate instances should be AWT-internally protected to avoid trouble. This is probably the case on some platforms (Sun's JDK1.3 works without trouble here), but not on others (SGI's or HP's JDK1.3, for instance: JVM crashes). Consequence: Don't rely on proper locking by AWT itself!.
Possible workarounds for this issue are:
We have learned until now, that locking facilities are available, where canvas handles are available only, and that locking has to take place in the AWT event queue thread.
...
synchronized ( mylib.getLock() ) {
mycanvas.beginSomething(); // JAWT lock, execution delayed
...
mycanvas.doSomethingElse(); // JAWT lock, execution delayed
...
mycanvas.endSomething(); // JAWT lock, execution delayed
}
...
...
mycanvas.paint(g); // locks library, JAWT lock
...
In such cases, there is great danger of causing deadlocks. If, in thread 1,
execution had already entered the critical section, and one of the delayed
methods was invoked, the whole sequence would block until this action had
beed executed. If there was a paint() pending and executed by the event
thread, just before the delayed action was processed, the native code would
try to lock the library, had to wait for the lock, which is currently owned
by thread 1, and block the event thread. Consequence: Don't export
a library lock - redesign your library interface instead.