Interaction of the Tcl I/O system with the Tcl Notifier edit
The image below shows all the connections between I/O and Notifier components relevant to the generation and handling of file events. A textual explanation follows behind the image.
Note: The image shown here is down-scaled to fit most screens. Click on the image itself to get it in full size.
File events are setup by a tcl script using the command fileevent. This in turn internally sets up the necessary data structures for ChannelHandler
s and EventScript
s and also records the interest in events using UpdateInterest
. The main action of UpdateInterest
is to compute which events are actually requested and to deliver this information to the (platform-dependent) channel driver for the channel. The driver then uses Tcl_CreateFileHandler
to record this fact in the event source for file events inside of the notifier. It also passes a reference to Tcl_NotifyChannel
as the designed callback. Tcl_CreateFileHandler
just records all this information in a FileHandler
structure and causes a recomputation of the select masks used later. Beyond that it does nothing.
The generation and handling of events essentially begins with Tcl_DoOneEvent
which uses Tcl_ServiceEvent
to dispatch the first event found in the event queue. Only if the event queue is empty it will try and use the other parts of the notifier to wait for more events coming from the outside. This behaviour of Tcl_DoOneEvent
means that the creation and queueing of events inside of an event handler is a Bad Thing(TM) as this will drown out the generation of any other events like for channels and timers.
To generate events in case of an empty queue Tcl_DoOneEvent
will first ask all registered event sources to setup themselves, followed by a Tcl_WaitForEvent
. When that command returns all registered event sources are asked to queue any events they have detected during the Tcl_WaitForEvent
In the unix implementation of Tcl_WaitForEvent
the select system call is used to wait on file ids and/or a timeout. This means that there is no separate event source for file events, it is integrated with the notifier. The queued events contain a reference to a FileHandlerEventProc
as their callback and Tcl_ServiceEvent
will invoke this callback when the event reaches the front of the event queue. FileHandlerEventProc
will then look through the registered FileHandler
structures for the channel to be notified and invokes the callback recorded there, which is Tcl_NotifyChannel
The above means that the moment Tcl_NotifyChannel
is called the system can assume that it is inside of an executing event handler. Tcl_NotifyChannel
does not queue events, it is called when queued events are actually dispatched.
There is a second path generating file events. Actually they are timer events but as they result in the execution of Tcl_NotifyChannel
at the end of the path the generic part of the I/O system (and the tcl scripts called by it) see them as file events. This path begins in UpdateInterest which creates a timer via Tcl_CreateTimer
when the input buffer of the channel contains data and there is interest in readable events. The new timer is passed a reference to ChannelTimerProc
and records it in the TimerHandler
structure created by Tcl_CreateTimer
Note: While the timer is active the generic I/O system will tell the involved channel driver that there is no interest in the real thing, i.e. read events from the OS. This interest will be reinstated by the timer when he finds that he has drained the buffers.
The timer management is a separate event source and provides TimerSetupProc
as the interface to the generic notifier. When they are called was described earlier.TimerSetupProc
determines if there are pending timers, computes a timeout value and transfers it into the notifier via Tcl_SetMaxBlockTime
. This information is used by the select inside of Tcl_WaitForEvent
to return if there were no status changes on the watched ids during that time.TimerCheckProc
determines if one or more timers expired and (like Tcl_WaitForEvent
) uses Tcl_QueueEvent
to queue the appropriate events, passing them a reference to TimerEventProc
as their callback. When such an event arrives at the front of the queue Tcl_ServiceEvent
executes the callback, TimerEventProc
, which in turn executes the callback recorded in the associated TimerHandler
In our case this is ChannelTimerProc
. This procedure checks if the buffers are now drained and invokes Tcl_NotifyChannel
to initiate the usual processing of a file event if not. It will also recreate the timer in that case, before the invocation of Tcl_NotifyChannel
. In the case of finding empty buffers ChannelTimerProc
will call UpdateInterest
to reinstate the interest in the real read events from the OS.