Updated 2013-03-25 05:34:43 by pooryorick

Summary  edit

A description of the Tcl event loop.

Description  edit

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 the after, fileevent and bind commands, 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
simply does the calls until there are no more to do.
update idletasks
does only after idle and Tcl_DoWhenIdle callbacks.
vwait
runs the event loop, including going to sleep, until some code breaks it by assigning to the specified variable.
tkwait
has various more complex termination conditions.

You need to be careful with these calls, or you can get bizarre results because your recursive call into the event loop has resulted in having the event loop make a recursive call to you. If you get into this kind of trouble, consider avoiding the four calls above and structuring your code to be callback-driven. This restructuring can be a tremendous cleaning of the stables; it's much better to structure callback-driven code from the very beginning. See Update considered harmful for some pointers on ways to do this.

Also, as of Tcl 8.6 the coroutine mechanism can be a great help in this, as it can reduce your need to restructure your code. Coroutine-enabled event handling shows how to get the appearance of e.g. a nested vwait without actually doing one.

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 [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).

garynkillOne question: How do you stop cancel events in the events loop?

lexfiend Using after cancel, of course.