Tcl event loop

The Tcl event loop handles after events as well as non-blocking channel events.

See Also

notifier
The bridge between the world of events and the rest of the event loop.
thread
The next step after one reaches the limits of the the Tcl event loop.

Description

Tcl/Tk's event loop is essentially single threaded. There is a main event loop, Tcl_DoOneEvent(), that checks for work to do and handles that work by making callbacks. These callbacks are registered with the event loop by after, fileevent, and bind, by -command, -xscrollcommand, and similar options on widgets, and by various things within C code.

    Main
    Event ----+----> Callback from Tcl_DoWhenIdle (after idle)
    Loop      |
              +----> Callback from Tcl_CreateTimerHandler (after N)
              |
              +----> Callback from Tcl_CreateChannelHandler (fileevent)
              |
              +----> Callback from Tk_CreateEventHandler
              |      (bind, -command, etc)
              |
              +----> Other callbacks (Tk_CreateGenericHandler,
                     Tk_CreateErrorHandler, etc.)

The natural flow of the event loop is something like:

     Start
       |
       |<----------------------------------------------------------+
       v                                                           ^
   Do I have    No[*]  Calculate how            Sleep for at       |
   work to do?  -----> long I may sleep  -----> most that much --->|
       |                                        time               |
       | Yes                                                       |
       |                                                           |
       v                                                           |
   Do one callback                                                 |
       |                                                           |
       +-----------------------------------------------------------+

    [*] This may cause a return from the event loop if it was
        invoked recursively (see below)

Commands like update, tkwait, and vwait give the illusion of multithreading because they cause more events to be processed. It is just an illusion. What they really do is to invoke the event loop recursively:

   Main                                 Main       More
   event ---> Callback ---> update ---> event ---> callbacks
   loop                                 loop       as needed

with different termination conditions:

update
Enters the event loop and services both queues in the normal manner until all events remaining in the main queue are scheduled in the future and the idle queue is empty. When this command occurs in a script, it is almost always a hack to work around a flawed design. See Update considered harmful.
update idletasks
Enters the event loop and services the idle queue until its empty. Events are queued on the idle queue by after idle and Tcl_DoWhenIdle. When this command occurs in a script, it is almost always a hack to work around a flawed design. See Update considered harmful.
vwait
Enters the event loop and services both queues in the normal manner until the specified variable is modified.
tkwait
Enters the event loop and services both queues in the normal manner until various more complex conditions are met.

If any of these commands is called while a call to one of them is currently active (is the active command at another level), the event loop is entered again. This second call must return before the first call can return, and its easy, particularly with vwait, to write a circular script where the lower level call blocks the higher-level call, causing an unexpected synchronization in the program. One way to avoid nested entry into the event loop is to queue commands on the event loop as callbacks. coroutines facilitate this approach because a coroutine can schedule a call back to itself or to some other handler that will call it, and then yield. Coroutine-enabled event handling shows how to get the appearance of e.g. a nested vwait without actually doing one, and there are other examples of the general approaches at Update considered harmful.


DKF: Tcl doesn't run the event loop by default, so idiomatically people do this in their pure Tcl scripts to start the event loop:

vwait forever

Tk does run an event loop by default (and it handles the interactive console on Unix by fileevent — well, strictly the C API equivalent — so it looks the same as the blocking Tcl interactive console). The termination condition for the Tk event loop is having all main windows of the application closed; each interpreter that has loaded the Tk package has to destroy its root toplevel widget.


garynkill: Hi i very new to Tcl/Tk. I need to do a simulation and wondering how to do i create multi-threading environment - as i have a skim through the "man" pages and discover there weren't any threads commands. And found out about events. How to do i go about coding where i need a timer running and after every 10s call a "command" to be run? - RS: It is possible to build Tcl with multiple threads, and in the future this may even become standard. However, threads can make debugging much more difficult, and with the event model, it's easy to implement timers and repeated scheduled events:

proc every {ms body} {
    eval $body; after $ms [namespace code [info level 0]]
}

every 10000 {puts "hello world"}

See the every page for more.

garynkill: Thanks a lot for your help! Just an equiry it is possible to make use of only event and create a simulation program for example a simulation like running 10 nodes each operating as one thread and sending each messages to one another. I know how to do it in threads in Java but i am still a newbie to Tcl/tk so asking whether it is possible to use events to do this or if you have any links to these related tutorials? It would be most helpful! Thanks again

yes, it is possible, and probably significantly easier to do with events than with threads. I don't know of any tutorials but the problem sounds fairly trivial. Use 'event generate' to send messages (assuming a "node" is represented as a widget) and 'bind' to have each node respond to the message. Use tcl/tk 8.5 so you can make use of the -data option of 'event generate'.

garynkill: Thanks a lot :)

garynkill: Can somebody give me a rough example how event works within Tcl context or TK context where it can be implemented. Still had no heads over it. Thanks. Sorry a newbie

US: 'event generate' is a Tk command. For the purpose you mentioned above use after 0 handlerproc. This schedules your task for immediate execution (as soon as Tcl has nothing else to do).

garynkill: Then how about if i want a procedure to execute every 10 s like this can it be done by this command.

US: after 10000 handlerproc (Take a look at every).

garynkill: How do you stop cancel events in the events loop?

lexfiend: Using after cancel, of course.


GMGJunior 2016-01-03 09:53:33:

I have a question: how the event loop is synchronized with threads?

Example: 1. The Main Thread is in event loop by Tk - some GUI handling messages. Let's say this GUI works with some array "ThreadsInfo":

     set ThreadID [ thread::create ]

     set ThreadsInfo($ThreadID,status) "Running"

     set status $ThreadsInfo($ThreadID,status)

     thread::wait

   By using GUI the ThreadsInfo array is constantly changed.

2. Now, some thread with ThreadID sends a code to the main thread, which modifies the ThreadsInfo array.

     thread::send $MainThreadID \
     {
          set status $ThreadsInfo([ thread::id ],"Error")
     }

I understand, that this case requires tsv::set/get operations and I know how it works.

My question is: when the code is accepted by the Main Thread, is it performed atomically? Or could this Threads Info be changed by GUI during the execution of the code sent inside the thread::send?

Thanks.

chw: as far as I've understood Tcl you should stop worrying and learn to love the event loop and the apartment threading model. Things delivered by other threads are somewhat like an event and thus handled synchronously, i.e. there's never any unexpected interruption of the control flow. Java is more low level in this regard. There you have to deal with concurrency by using explicit synchronization points on objects and/or object methods. OTOH, Java gives you direct means to deal with waiting and wake up on entities with synchronization capabilities.