NAME

Tcl_CreateEventSource, Tcl_DeleteEventSource, Tcl_WatchFile, Tcl_FileReady, Tcl_SetMaxBlockTime, Tcl_QueueEvent, Tcl_WaitForEvent - Event sources, the event notifier, and the event queue

SYNOPSIS

#include <tcl.h>
Tcl_CreateEventSource(setupProc, checkProc, clientData)
Tcl_DeleteEventSource(setupProc, checkProc, clientData)
Tcl_WatchFile(file, mask)
Tcl_SetMaxBlockTime(timePtr)
int
Tcl_FileReady(file, mask)
Tcl_QueueEvent(evPtr, position)
int
Tcl_WaitForEvent(timePtr)

ARGUMENTS

Tcl_EventSetupProc *setupProc (in)
Procedure to invoke to prepare for event wait in Tcl_DoWhenIdle.
Tcl_EventCheckProc *checkProc (in)
Procedure for Tcl_DoWhenIdle to invoke after waiting for events. Checks to see if any events have occurred and, if so, queues them.
ClientData clientData (in)
Arbitrary one-word value to pass to setupProc and checkProc.
Tcl_File file (in)
Generic file handle as returned by Tcl_GetFile.
int mask (in)
Indicates the events of interest on file: an OR'ed combination of TCL_READABLE, TCL_WRITABLE, and TCL_EXCEPTION.
Tcl_Time *timePtr (in)
Indicates the maximum amount of time to wait for an event. This is specified as an interval (how long to wait), not an absolute time (when to wakeup). If the pointer passed to Tcl_WaitForEvent is NULL, it means there is no maximum wait time: wait forever if necessary.
Tcl_Event *evPtr (in)
An event to add to the event queue. The storage for the event must have been allocated by the caller using malloc or ckalloc.
Tcl_QueuePosition position (in)
Where to add the new event in the queue: TCL_QUEUE_TAIL, TCL_QUEUE_HEAD, or TCL_QUEUE_MARK.
int flags (in)
A copy of the flags argument passed to Tcl_DoOneEvent.

INTRODUCTION

The procedures described here are the building blocks out of which the Tcl event notifier is constructed. The event notifier is the lowest layer in the Tcl event mechanism. It consists of three things:
  1. Event sources: these represent the ways in which events can be generated. For example, there is a timer event source that implements the Tcl_CreateTimerHandler procedure and the after command, and there is a file event source that implements the Tcl_CreateFileHandler procedure. An event source must work with the notifier to detect events at the right times, record them on the event queue, and eventually notify higher-level software that they have occurred.
  2. The event queue: there is a single queue for the whole application, containing events that have been detected but not yet serviced. The event queue guarantees a fair discipline of event handling, so that no event source can starve the others. It also allows events to be saved for servicing at a future time.
  3. The procedure Tcl_DoOneEvent: this is procedure that is invoked by the application to service events. It works with the event sources and the event queue to detect and handle events, and calls Tcl_WaitForEvent to actually wait for an event to occur.

The easiest way to understand how the notifier works is to consider what happens when Tcl_DoOneEvent is called. Tcl_DoOneEvent is passed a flags argument that indicates what sort of events it is OK to process and also whether or not to block if no events are ready. Tcl_DoOneEvent does the following things:

  1. Check the event queue to see if it contains any events that can be serviced. If so, service the first possible event, remove it from the queue, and return.
  2. Prepare to block for an event. To do this, Tcl_DoOneEvent invokes a setup procedure in each event source. The event source will call procedures like Tcl_WatchFile and Tcl_SetMaxBlockTime to indicate what low-level events to look for in Tcl_WaitForEvent.
  3. Call Tcl_WaitForEvent. This procedure is implemented differently on different platforms; it waits for an event to occur, based on the information provided by the event sources. It may cause the application to block if timePtr specifies an interval other than 0. Tcl_WaitForEvent returns when something has happened, such as a file becoming readable or the interval given by timePtr expiring. If there are no events for Tcl_WaitForEvent to wait for, so that it would block forever, then it returns immediately and Tcl_DoOneEvent returns 0.
  4. Call a check procedure in each event source. The check procedure determines whether any events of interest to this source occurred (e.g. by calling Tcl_FileReady). If so, the events are added to the event queue.
  5. Check the event queue to see if it contains any events that can be serviced. If so, service the first possible event, remove it from the queue, and return.
  6. See if there are idle callbacks pending. If so, invoke all of them and return.
  7. Either return 0 to indicate that no events were ready, or go back to step [2] if blocking was requested by the caller.

The procedures in this file allow you to do two things. First, they allow you to create new event sources, such as one for UNIX signals or one to notify when subprocesses have exited. Second, the procedures can be used to build a new version of Tcl_DoOneEvent. This might be necessary to support a new operating system with different low-level event reporting mechanisms, or it might be necessary to merge Tcl's event loop with that of some other toolkit like Xt.

CREATING A NEW EVENT SOURCE

An event source consists of three procedures invoked by the notifier, plus additional C procedures that are invoked by higher-level code to arrange for event-driven callbacks. The three procedures called by the notifier consist of the setup and check procedures described above, plus an additional procedure that is invoked when an event is removed from the event queue for servicing.

The procedure Tcl_CreateEventSource creates a new event source. Its arguments specify the setup procedure and check procedure for the event source. SetupProc should match the following prototype:

typedef void Tcl_EventSetupProc(
	ClientData clientData,
	int flags);
The clientData argument will be the same as the clientData argument to Tcl_CreateEventSource; it is typically used to point to private information managed by the event source. The flags argument will be the same as the flags argument passed to Tcl_DoOneEvent except that it will never by 0 (Tcl_DoOneEvent replaces 0 with TCL_ALL_EVENTS). Flags indicates what kinds of events should be considered; if the bit corresponding to this event source isn't set, the event source should return immediately without doing anything. For example, the file event source checks for the TCL_FILE_EVENTS bit.

SetupProc's job is to provide information to Tcl_WaitForEvent about how to wait for events. It usually does this by calling Tcl_WatchFile or Tcl_SetMaxBlockTime. For example, setupProc can call Tcl_WatchFile to indicate that Tcl_WaitForEvent should return when the conditions given by the mask argument become true for the file given by file. The UNIX version of Tcl_WaitForEvent uses the information passed to Tcl_WatchFile to set the file masks for select, which it uses to wait for events. If Tcl_WatchFile isn't called by any event sources then Tcl_WaitForEvent will ignore files while waiting.

SetupProc can also invoke Tcl_SetMaxBlockTime to set an upper bound on how long Tcl_WaitForEvent will block. If no event source calls Tcl_SetMaxBlockTime then Tcl_WaitForEvent will wait as long as necessary for an event to occur; otherwise, it will only wait as long as the shortest interval passed to Tcl_SetMaxBlockTime by one of the event sources. For example, the timer event source uses this procedure to limit the wait time to the interval before the next timer event is ready. If an event source knows that it already has events ready to report, it can request a zero maximum block time. The timePtr argument to Tcl_WaitForEvent points to a structure that describes a time interval in seconds and microseconds:

typedef struct Tcl_Time {
	long sec;
	long usec;
} Tcl_Time;
The usec field should be less than 1000000.

Information provided to Tcl_WatchFile and Tcl_SetMaxBlockTime is only used for the next call to Tcl_WaitForEvent; it is discarded after Tcl_WaitForEvent returns. The next time an event wait is done each of the event sources' setup procedures will be called again, and they can specify new information for that event wait.

In addition to the generic procedures Tcl_WatchFile and Tcl_SetMaxBlockTime, other platform-specific procedures may also be available for setupProc, if there is additional information needed by Tcl_WaitForEvent on that platform.

The second procedure provided by each event source is its check procedure, indicated by the checkProc argument to Tcl_CreateEventSource. CheckProc must match the following prototype:

typedef void Tcl_EventCheckProc(
	ClientData clientData,
	int flags);
The arguments to this procedure are the same as those for setupProc. CheckProc is invoked by Tcl_DoOneEvent after it has waited for events. Presumably at least one event source is now prepared to queue an event. Tcl_DoOneEvent calls each of the event sources in turn, so they all have a chance to queue any events that are ready. The check procedure does two things. First, it must see if any events have triggered. Different event sources do this in different ways, but the procedure Tcl_FileReady may be useful for some event sources. It takes as arguments a file identifier file and a mask of interesting conditions; it returns another mask indicating which of those conditions were found to be present on the file during the most recent call to Tcl_WaitForEvent. Tcl_WaitForEvent only checks a file if Tcl_WatchFile was called by at least one event source, so it is possible for Tcl_FileReady to return 0 even if the file is ready.

If an event source's check procedure detects that an interesting event has occurred, then it must add the event to Tcl's event queue. To do this, the event source calls Tcl_QueueEvent. The evPtr argument is a pointer to a dynamically allocated structure containing the event (see below for more information on memory management issues). Each event source can define its own event structure with whatever information is relevant to that event source. However, the first element of the structure must be a structure of type Tcl_Event, and the address of this structure is used when communicating between the event source and the rest of the notifier. A Tcl_Event has the following definition:

typedef struct Tcl_Event {
    Tcl_EventProc *proc;
    struct Tcl_Event *nextPtr;
};
The event source must fill in the proc field of the event before calling Tcl_QueueEvent. The nextPtr is used to link together the events in the queue and should not be modified by the event source.

An event may be added to the queue at any of three positions, depending on the position argument to Tcl_QueueEvent:

TCL_QUEUE_TAIL
Add the event at the back of the queue, so that all other pending events will be serviced first. This is almost always the right place for new events.
TCL_QUEUE_HEAD
Add the event at the front of the queue, so that it will be serviced before all other queued events.
TCL_QUEUE_MARK
Add the event at the front of the queue, unless there are other events at the front whose position is TCL_QUEUE_MARK; if so, add the new event just after all other TCL_QUEUE_MARK events. This value of position is used to insert an ordered sequence of events at the front of the queue, such as a series of Enter and Leave events synthesized during a grab or ungrab operation in Tk.

When it is time to handle an event from the queue (steps 1 and 5 above) Tcl_DoOneEvent will invoke the proc specified in the first queued Tcl_Event structure. Proc must match the following prototype:

typedef int Tcl_EventProc(
	Tcl_Event *evPtr,
	int flags);
The first argument to proc is a pointer to the event, which will be the same as the first argument to the Tcl_QueueEvent call that added the event to the queue. The second argument to proc is the flags argument for the current call to Tcl_DoOneEvent; this is used by the event source to return immediately if its events are not relevant.

It is up to proc to handle the event, typically by invoking one or more Tcl commands or C-level callbacks. Once the event source has finished handling the event it returns 1 to indicate that the event can be removed from the queue. If for some reason the event source decides that the event cannot be handled at this time, it may return 0 to indicate that the event should be deferred for processing later; in this case Tcl_DoOneEvent will go on to the next event in the queue and attempt to service it. There are several reasons why an event source might defer an event. One possibility is that events of this type are excluded by the flags argument. For example, the file event source will always return 0 if the TCL_FILE_EVENTS bit isn't set in flags. Another example of deferring events happens in Tk if Tk_RestrictEvents has been invoked to defer certain kinds of window events.

When proc returns 1, Tcl_DoOneEvent will remove the event from the event queue and free its storage. Note that the storage for an event must be allocated by the event source (using malloc or the Tcl macro ckalloc) before calling Tcl_QueueEvent, but it will be freed by Tcl_DoOneEvent, not by the event source.

CREATING A NEW NOTIFIER

The notifier consists of all the procedures described in this manual entry, plus Tcl_DoOneEvent and Tcl_Sleep. Most of these procedures are generic, in that they are the same for all platforms. However, four of the procedures are platform-dependent: Tcl_WatchFile, Tcl_FileReady, Tcl_WaitForEvent, and Tcl_Sleep. To support a new platform, you must write new versions of these procedures. Tcl_WatchFile and Tcl_FileReady have already been described previously in this document, and Tcl_Sleep is described in its own manual entry.

Tcl_WaitForEvent is the lowest-level procedure in the notifier; it is responsible for waiting for an ``interesting'' event to occur or for a given time to elapse. Before Tcl_WaitForEvent is invoked, each of the event sources' setup procedure will have been invoked; the setup procedures will have provided information about what to wait for by invoking procedures like Tcl_WatchFile. The timePtr argument to Tcl_WaitForEvent gives the maximum time to block for an event, based on calls to Tcl_SetMaxBlockTime made by setup procedures and on other information (such as the TCL_DONT_WAIT bit in flags). Tcl_WaitForEvent uses information saved by Tcl_WatchFile, plus the timePtr argument to decide what to wait for and how long to block. It returns TCL_OK as soon as one of the specified events has occurred or the given amount of time has elapsed. However, if there are no event handlers (neither Tcl_WatchFile nor Tcl_SetMaxBlockTime has been called since the last call to Tcl_WaitForEvent), so that the procedure would block forever, then it returns immediately with a result of TCL_ERROR.

The easiest way to create a new notifier is to look at the code for an existing notifier, such as the files generic/tclNotify.c and unix/tclUnixNotfy.c.

KEYWORDS

block time, event notifier, event queue, event sources, file events
Copyright © 1995-1996 Sun Microsystems, Inc.
Copyright © 1995, 1996 Roger E. Critchlow Jr.