written by Gerd Stolpmann
This module generalizes the Unix.select function. The idea is to have an event queue (implemented by Equeue) that manages all events that can result from a Unix.select call. What can happen is that there is something to do for a file descriptor (reading, writing, accepting out-of-band data) or for a certain period of time ("timeout") nothing has happened. These events are then put into the queue where event handlers can react on them. You can describe what types of events should be generated by adding "resources". You can think a resource being a condition for which events are generated if the condition becomes true.
Since release 1.0 of Equeue, this module is thread-safe if no two thread share any entity provided by this module. The special archives ending with "_mt" must be used for multi-threaded programs.
exception Abort of (group * exn);;
First argument is the group where the abort happens. The second argument is an arbitrary exception that can be passed.
type operation = Wait_in of file_descr (* wait for input data *) | Wait_out of file_descr (* wait until output can be written *) | Wait_oob of file_descr (* wait for out-of-band data *) | Wait of wait_id (* wait only for timeout *)
type event = Input_arrived of (group * file_descr) | Output_readiness of (group * file_descr) | Out_of_band of (group * file_descr) | Timeout of (group * operation) | Term of group (* Group termination event *)
Note: Term g is used internally to perform the "clear" operation.
val create_unix_event_system : unit -> event_system
An event_system contains resources which can produce events. A resource is the pair (operation * timeout(float) ). The "operation" specifies what kind of events can happen with a file descriptor; it is possible to put the same descriptor several times into the event_system if the operations differ. The timeout is a float specifying the maximum time in seconds to wait for the event. If that period of time since the last event is exceeded a Timeout event is generated.
As a special case, the operation "Wait" does nothing except generating timeout events.
Event systems are implemented with the Equeue module. If you associate event handlers the same rules apply.
All resources and handlers belong to a "group". This is intended to simplify the clean-up; you can remove all resources and handlers of a group by calling only one function (clear).
There is a special handler which is useful to close file descriptors no longer needed. It is called if all resources are removed from the event system dealing with the file descriptor. This handler should close the descriptor. It is not necessary but allowed to call "clear".
Another special handler deals with aborts. Aborts can be forced by raising Abort (g,x) causing the execution of the abort handler for group g. The abort handler must remove all resources and do all clean-ups necessary to terminate I/O for d. In this case the close handler is not invoked. After the abort handler has done its work the "clear" function deletes the whole group from the event system.
val new_group : event_system -> group
begin a new group
val new_wait_id : event_system -> wait_id
get a new unique wait identifier
val exists_resource : event_system -> operation -> bool
Find out if a specific resource already exists
val add_resource : event_system -> group -> (operation * float) -> unit
Add another resource. You cannot add the same resource twice; if you try it the second resource is silently dropped.
Note that resources remain even if they have generated an event. This means that a simple "timeout" resource generates cyclicly events.
val add_close_action : event_system -> group -> (file_descr * (file_descr -> unit)) -> unit
The function is intended to be called if the file descriptor is closed. Note that this module cannot find out if a descriptor gets an EOF marker, so the following applies: If you find out that a descriptor should be closed, do this, and invoke "remove_resource". If you have previously added a "close action", this action is then done. Note that the group must only be passed for administrative purposes, it is not used as criterion while searching the appropriate action.
val add_abort_action : event_system -> group -> (group -> exn -> unit) -> unit
The function is called if an Abort exception has been caught. See below for details.
val remove_resource : event_system -> group -> operation -> unit
Removes all resources identified by the 'operation' and the 'group'. For every descriptor that has an associated "close action" this action is invoked.
val add_handler : event_system -> group -> (event_system -> event Equeue.t -> event -> unit) -> unit
Add an event handler that is associated to the given group.
val add_event : event_system -> event -> unit
add additional events
val clear : event_system -> group -> unit
remove all resources and handlers of the specified group
val run : event_system -> unit
invoke the event loop. Bases on Equeue.run. In contrast to the latter, this version of 'run' catches all exceptions Abort (d,x). In such a situation the abort handler for d is called and the event loop is resumed. Other exceptions than Abort are not caught.
val once : event_system -> group -> float -> (unit -> unit) -> unit
add a resource and a handler such that the function is invoked after the given period of time has elapsed. The resource and the handler are both removed then.
Cooperation with Tcl/Tk
val attach_to_tcl_queue : event_system -> (event_system -> unit) -> unit
attach_to_tcl_queue es runner: The event system 'es' is attached to the central Tcl-8.0 event queue. This function is only available if equeue is compiled for use with Tcl/Tk; if not you will get a Failure("Unixqueue.attach_to_tcl_queue").
This function is an extension to 'run' if unixqueues are used together with Tcl/Tk. Both Unixqueue and Tcl provide event queues for system events, and it is possible to merge both queues such that events may happen and be processed on one queue while the other queue blocks.
'attach_to_tcl_queue' returns immediately, but sets up a Tcl event source such that Tcl informs Unixqueue when events on interesting resources happen. After 'attach_to_tcl_queue' has been invoked, you may continue with the Tcl main event loop. Once events happen the event system 'es' is waiting for, the function 'runner' is called with 'es' as argument. 'runner' should invoke 'run', and catch any exceptions and handle them. Before 'runner' returns to the caller, all events on 'es' should have been processed; 'es' should then be empty. (If not, these events simply remain on the queue, and they will be processed again if next new event coming from a resource is added.)
If this function is called, and the event system is already attached, nothing happens.
Of course, this is all intended to help writing applications which have a graphical user interface (GUI) built with camltk, and some network functionality which is designed to work in the background. Simply create your GUI with camltk, and once the button is pressed which starts the network I/O, you call 'attach_to_tcl_queue', and the I/O will be processed concurrently with any user input coming from the GUI.
Go to the first, previous, next, last section, table of contents.