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:
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.
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:
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.
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.
Copyright © 1995-1996 Sun Microsystems, Inc. Copyright © 1995, 1996 Roger E. Critchlow Jr.