Chapter 7. Using Unixqueue together with Tcl

The Tcl programming language has already an event queue implementation, and the Tk toolkit applies it to realize event queues for graphical user interfaces (GUIs). In the O'Caml world, Tcl/Tk is available through the packages camltk and labltk.

Unixqueue is designed to offer network components a common implementation of file descriptor events; Tcl/Tk can be used to program GUIs which do nothing but process user input events. Often, one wants to combine both queues, e.g. because a network process should be monitored by a GUI.

Fortunately, Tcl's API allows it to realize the same functionality as Unixqueue, and because of this it is possible to map the Unxiqueue functions to Tcl's event queue API. This has been done by the so-called "Tcl queue extension" to Unixqueue.

The Tcl queue extension offers one additional function:

val attach_to_tcl_queue : event_system -> (event_system -> unit) -> unit
By invoking
Unixqueue.attach_to_tcl_queue ues runner
the event system ues is "attached" to the global event queue of Tcl. This means that both queues now run in an integrated way; events of both queues are respected, and if one queue blocks the other queue may continue to process events.

This function returns immediately; it has only set up Tcl's queue to know that there is a second queue. You may now continue your program, and enter Tcl's main event loop. In camltk and labltk, this is

Tk.mainLoop()
You have time to add resources to ues until you enter Tcl's main loop. (If you do not do this, ues will immediately be detached from Tcl's queue, and nothing happens.) Once there are events in ues, the function runner is called, and the only argument is ues. The task of runner is to process the events in ues. The simplest way to do it is to pass Unixqueue.run as runner, but you may want to modify that. For example, you can update the display after the queue has been processed; or you can catch exceptions and do the right thing.

You can safely call attach_to_tcl_queue several times; nothing happens if the queues are already joined.

Unixqueue automatically detaches its event queues from Tcl's queue if there are not any resources left.

It is possible to attach several Unixqueues to the global Tcl queue.

Implementation. There are currently two implementations of the TCL extension. The advanced implementation (equeue_tcl_nat.cm[x]a defines several additional C primitives, and calls the event handling functions of TCL directly. Whenever possible, this implementation should be preferred. The other implementation (equeue_tcl.cm[x]a) uses the existing labltk bindings. The problem with labltk is that it is not possible to watch a file descriptor and check whether it is possible to read or to write, you can only check the possibility of one operation at once. Because of this, the file event handlers of labltk have been avoided, and the implementation bases only on the timers of labltk. Of course, throughput and latency of this solution are limited, but it may be still useful for network clients.

Sample code. The example discussed before, copying files in an event-driven way, has been extended to show how Unixqueue and Tcl can cooperate. While the file is being copied, a window informs about the progress and offers a "Stop" button which immediately aborts the copy procedure. See the directory "filecopy_labltk" in the distributed tarball.

Pitfalls. If you call Unixqueue functions from Unixqueue event handlers, the functions behave exactly as described in the previous chapters. However, it is also possible to call Unixqueue functions from TCL event handlers. In this case, not all change requests will be immediately honoured. Especially, add_event does not immediately invoke the appropriate event handler; the event is just recorded, and the handler will be called when the next system event happens. You can force to respect the new event as soon as possible by adding an empty handler using once with a timeout of 0 seconds. - The other Unixqueue functions should not behave differently (although the actually performed operations are very different). Especially you can call add_resource and remove_resource and the change will be respected immediately.