**Interaction of the Tcl I/O system with the Tcl [Notifier]** 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. [Diagram of I/O Internals] 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` and `TimerCheckProc` 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` structure. 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.