|
|
 |
 |
 |
 |
TCL(Tool Command Language) Scripting
|
 |
 |
 |
 |
 |
 |
 |
 |
Quick question about the event loop
Hi, I'm working on a script which will have to rely heavily on the event loop using vwait. Not having much experience with the event loop, I have one very simple question (actually, I guess I have two questions). 1) If two events occur "simultaneously," do the event handlers get called simultaneously, or linearly? I realize that events most likely never occur at the same time, but what about "virtually" the same time. 2) When an event occurs and the handler is called, is the event loop exited to process the event? I guess I'm wondering if an event occurs while one is already being processed, will the second event block until the first is completed processing, or will the second event be handled when it occurs? Thanks, Andy
On May 21, 5:22 pm, Andrew Falanga <af300@gmail.com> wrote: > 1) If two events occur "simultaneously," do the event handlers get > called simultaneously, or linearly?
Linearly, that is, sequentially. All vwait does is (approximately): while (1) fetch_one_event_and_call_its_handler(); > 2) When an event occurs and the handler is called, is the event loop > exited to process the event?
Not "exited", but "up the stack", hence waiting until we return. > I guess I'm wondering if an event occurs > while one is already being processed, will the second event block > until the first is completed processing, or will the second event be > handled when it occurs?
The second event will be dequeued and handled only after the first handler has returned. Bottom line: don't worry, Tcl is as single-threaded as can be, as long as you don't use the Thread package. Vwait is design exactly for this: do many brilliant asynchronous things, without any reentrance/deadlock nightmare. You're on the good track by the way. Many a newcomer starts talking about threads before even learning about [vwait]. Congrats ! -Alex
Alexandre Ferrieux wrote: > On May 21, 5:22 pm, Andrew Falanga <af300 @gmail.com> wrote: >> 1) If two events occur "simultaneously," do the event handlers get >> called simultaneously, or linearly? > Linearly, that is, sequentially. > All vwait does is (approximately): > while (1) fetch_one_event_and_call_its_handler(); >> 2) When an event occurs and the handler is called, is the event loop >> exited to process the event? > Not "exited", but "up the stack", hence waiting until we return. >> I guess I'm wondering if an event occurs >> while one is already being processed, will the second event block >> until the first is completed processing, or will the second event be >> handled when it occurs? > The second event will be dequeued and handled only after the first > handler has returned. > Bottom line: don't worry, Tcl is as single-threaded as can be, as long > as you don't use the Thread package. Vwait is design exactly for this: > do many brilliant asynchronous things, without any reentrance/deadlock > nightmare. > You're on the good track by the way. Many a newcomer starts talking > about threads before even learning about [vwait]. Congrats !
All of that is correct, but I'd say that unless you *really* understand the event loop, never call vwait more than zero or one times. Zero if you're running wish, once if you're running tclsh. Like Alexandre explained, the event loops is just that -- a loop. It's not some mysterious multi-threaded beast. It's really no different conceptually than: while {1} { get the next event process the next event } Realize that if "process the next event" includes a vwait, your code now looks like this: while {1} { get the next event while {1} { get the next event process the next event } } In other words, each call to "vwait" in effect creates a new (potentially) infinite loop. Each of these loops can only be terminated in reverse order (last one first). These loops do *not* run in parallel. Only after the inner-most vwait terminates can the next higher loop continue. -- Bryan Oakley http://www.tclscripting.com
Alexandre Ferrieux wrote:
... >> I guess I'm wondering if an event occurs >> while one is already being processed, will the second event block >> until the first is completed processing, or will the second event be >> handled when it occurs? > The second event will be dequeued and handled only after the first > handler has returned. > Bottom line: don't worry, Tcl is as single-threaded as can be, as long > as you don't use the Thread package. Vwait is design exactly for this: > do many brilliant asynchronous things, without any reentrance/deadlock > nightmare.
... The main gotcha to look out for is re-entering the event loop. Basically, your event handlers should never call [vwait] or [tkwait] or [update] or any command which calls one of these (such as most of the tk_* commands that pop up dialogs). There is advice on the wiki about how to restructure an application to avoid this: http://wiki.tcl.tk/1255 Otherwise, you can assume that each event handler runs to completion as an atomic action. -- Neil
In article <f2site$9n@oyez.ccc.nottingham.ac.uk>, Neil Madden <n@cs.nott.ac.uk> wrote: >... >The main gotcha to look out for is re-entering the event loop. >Basically, your event handlers should never call [vwait] or [tkwait] or >[update] or any command which calls one of these (such as most of the >tk_* commands that pop up dialogs). There is advice on the wiki about >how to restructure an application to avoid this: http://wiki.tcl.tk/1255
. . . ... such a "gotcha" that we sometimes simplify this to, "you don't need [update], and you need at most one [vwait]." While that's not entirely true, it helps focus attention in productive directions.
Neil Madden wrote: > The main gotcha to look out for is re-entering the event loop. > Basically, your event handlers should never call [vwait] or [tkwait] or > [update] or any command which calls one of these
Unfortunately, a number of the useful packages in Tcllib call these for you. (http, smtp, etc) If there were a way to time-out a socket call without using the event loop, it would help in these cases, IME. -- Darren New / San Diego, CA, USA (PST) His kernel fu is strong. He studied at the Shao Linux Temple.
On May 22, 2:29 am, Darren New <d@san.rr.com> wrote: > [...] If there were a way to time-out a socket call > without using the event loop, it would help in these cases, IME.
You're right that something's missing there. But I don't think it is "without using the event loop". Indeed, it would be a shame to force the http package to implement a timeout *without* after+fileevent (because it would be forced to redo it in C -- yuck !). Instead, I think the real need is to temporarily disable all the previous handlers: vwaitadmin push fileevent ... after ... vwait ::mypackage::mybreak ;# the nested one vwaitadmin pop Rationale for introducing a new primitive: currently, only fileevents can be introspected in pure Tcl by iterating over [info channels], and they are easy to enable/disable. Tk handlers are much harder to enumerate (to say the least). And [after] handlers are just not reachable, and even if we could enumerate them, we couldn't re-enable them with the same ID after disabling them (which is mandatory because the calling script may have stored the ID). Not even speaking about other, exotic event sources like those in Snack... But of course I realize this is not trivial to implement ! -Alex
Alexandre Ferrieux wrote: > Tk handlers are much harder to enumerate (to say the least).
But there's only one that matters: the (internal, hidden) fileevent on the (internal, hidden) low-level X file descriptor. Disabling it is easiest from C though for sure. > And [after] handlers are just not reachable,
Actually, there's [after info]... > and even if we could enumerate them, we couldn't re-enable > them with the same ID after disabling them (which is mandatory because > the calling script may have stored the ID).
Which is a proper objection. > Not even speaking about > other, exotic event sources like those in Snack...
Actually, those are file(descriptor)events, timer events, or direct injections into the event queue in (indirect) response to one of the other kind of events. If you want real exotic stuff, try messages from other threads or signals... Donal.
Alexandre Ferrieux wrote: > Instead, I think the real need is to temporarily disable all the > previous handlers:
While that's a fairly simple solution compared to mine, I think an even better solution would be to have continuations. A command that would do just what vwait does now *without* nesting would be another useful addition. (Altho, honestly, in the particular case biting me right now, I'm not sure it would be all that helpful.) In any case, some way of returning to the top-level event loop in the middle of something without having to turn all your code inside out manually would be a great addition too. I don't expect this will ever happen, given that I can't imagine how to do it without breaking the integration with C. -- Darren New / San Diego, CA, USA (PST) His kernel fu is strong. He studied at the Shao Linux Temple.
On May 22, 5:30 pm, "Donal K. Fellows" <donal.k.fell @manchester.ac.uk> wrote: > Alexandre Ferrieux wrote: > > Tk handlers are much harder to enumerate (to say the least). > But there's only one that matters: the (internal, hidden) fileevent on > the (internal, hidden) low-level X file descriptor. Disabling it is > easiest from C though for sure.
Except on Windows it's not a file descriptor ;-) > > And [after] handlers are just not reachable, > Actually, there's [after info]...
Oops -- long time since I last checked those new goodies ! > > and even if we could enumerate them, we couldn't re-enable > > them with the same ID after disabling them (which is mandatory because > > the calling script may have stored the ID). > Which is a proper objection.
Yessss > > Not even speaking about > > other, exotic event sources like those in Snack... > Actually, those are file(descriptor)events, timer events, or direct > injections into the event queue in (indirect) response to one of the > other kind of events. If you want real exotic stuff, try messages from > other threads or signals...
Well, exotic or not, we still don't have a way of frrezing them today ... I'd happily TIP for [vwaitadmin] if there were a tad more demand. Darren ? -Alex
Alexandre Ferrieux wrote: > I'd happily TIP for [vwaitadmin] if there were a tad more demand. > Darren ?
I have the work-arounds for what I'm doing, and as I can't really even use 8.5 conviently yet, any actual "demand" I have for this would be problematic. I think if you work it out, you have to make it work with existing libraries. I.e., you should be able to wrap up the http and/or smtp calls with the new command, so you don't have to change http or smtp to make it work the way you want. (From what I can tell, your proposal does this already, but it's hard to be sure without thinking about it a whole bunch.) But yes, thinking on it, a convenient way to suspend all current events would be useful. Questions are likely to come up with multiple vwaitadmin push calls, what happens if you do vwaitadmin push fileevent ... vwaitadmin pop (is the fileevent still around?) vwaitadmin push (is the fileevent disabled now?) I think the simple answers would work, but again I'd have to think about it a whole bunch. Possibly adding sufficient introspection to stuff that this can be done at the command level as a library might be better... Difficult with Tk, I'd think. I mostly write servers, so all the Tk-based stuff isn't something I've had problems with. -- Darren New / San Diego, CA, USA (PST) His kernel fu is strong. He studied at the Shao Linux Temple.
On May 23, 12:47 am, Darren New <d@san.rr.com> wrote: > [...] so you don't have to change http or smtp to > make it work the way you want. (From what I can tell, your proposal does > this already, but it's hard to be sure without thinking about it a whole > bunch.)
Yes, this transparency is the exact purpose of my proposal. The idea is that between a push/pop pair, you have a clear slate, exactly like on interp init. All previously registered handlers are "frozen", but reactivated when you cross the "pop". > Questions are likely to come up with multiple > vwaitadmin push calls, what happens if you do > vwaitadmin push > fileevent ... > vwaitadmin pop > (is the fileevent still around?)
No. The handler state is restored as it was before the push. (with possible delayed-removal of fileevents whose channels have been closed between push and pop) > vwaitadmin push > (is the fileevent disabled now?)
Which fileevent ? ;-) The slate is empty here. > I think the simple answers would work, but again I'd have to think about > it a whole bunch. Possibly adding sufficient introspection to stuff that > this can be done at the command level as a library might be better...
Yes, I had thought about this too. I would also have preferred the introspection method, because it allows to write much less in C and much more in Tcl, however it adds a big load of complexity because it implies to invent a script-level representation for handlers which are internal (like Windows Event objects or hidden /dev/dsp file descriptor in Snack, or the equally hidden X11 socket fd); and as mentioned above, to make it worse, even already scriptable handlers like [after] are currently not "freezable"... While a more traditional, monolithic C implementation would simply juggle the pointers to (a stack of) saved and current list-of-event- sources (off the top of my hat -- currently I'm ignorant of all the details)... -Alex
On May 23, 12:47 am, Darren New <d@san.rr.com> wrote: > [...] so you don't have to change http or smtp to > make it work the way you want. (From what I can tell, your proposal does > this already, but it's hard to be sure without thinking about it a whole > bunch.)
Yes, this transparency is the exact purpose of my proposal. The idea is that between a push/pop pair, you have a clear slate, exactly like on interp init. All previously registered handlers are "frozen", but reactivated when you cross the "pop". > Questions are likely to come up with multiple > vwaitadmin push calls, what happens if you do > vwaitadmin push > fileevent ... > vwaitadmin pop > (is the fileevent still around?)
No. The handler state is restored as it was before the push. (with possible delayed-removal of fileevents whose channels have been closed between push and pop) > vwaitadmin push > (is the fileevent disabled now?)
Which fileevent ? ;-) The slate is empty here. > I think the simple answers would work, but again I'd have to think about > it a whole bunch. Possibly adding sufficient introspection to stuff that > this can be done at the command level as a library might be better...
Yes, I had thought about this too. I would also have preferred the introspection method, because it allows to write much less in C and much more in Tcl, however it adds a big load of complexity because it implies to invent a script-level representation for handlers which are internal (like Windows Event objects or hidden /dev/dsp file descriptor in Snack, or the equally hidden X11 socket fd); and as mentioned above, to make it worse, even already scriptable handlers like [after] are currently not "freezable"... While a more traditional, monolithic C implementation would simply juggle the pointers to (a stack of) saved and current list-of-event- sources (off the top of my hat -- currently I'm ignorant of all the details)... -Alex
On May 23, 12:47 am, Darren New <d@san.rr.com> wrote: > [...] so you don't have to change http or smtp to > make it work the way you want. (From what I can tell, your proposal does > this already, but it's hard to be sure without thinking about it a whole > bunch.)
Yes, this transparency is the exact purpose of my proposal. The idea is that between a push/pop pair, you have a clear slate, exactly like on interp init. All previously registered handlers are "frozen", but reactivated when you cross the "pop". > Questions are likely to come up with multiple > vwaitadmin push calls, what happens if you do > vwaitadmin push > fileevent ... > vwaitadmin pop > (is the fileevent still around?)
No. The handler state is restored as it was before the push. (with possible delayed-removal of fileevents whose channels have been closed between push and pop) > vwaitadmin push > (is the fileevent disabled now?)
Which fileevent ? ;-) The slate is empty here. > I think the simple answers would work, but again I'd have to think about > it a whole bunch. Possibly adding sufficient introspection to stuff that > this can be done at the command level as a library might be better...
Yes, I had thought about this too. I would also have preferred the introspection method, because it allows to write much less in C and much more in Tcl, however it adds a big load of complexity because it implies to invent a script-level representation for handlers which are internal (like Windows Event objects or hidden /dev/dsp file descriptor in Snack, or the equally hidden X11 socket fd); and as mentioned above, to make it worse, even already scriptable handlers like [after] are currently not "freezable"... While a more traditional, monolithic C implementation would simply juggle the pointers to (a stack of) saved and current list-of-event- sources (off the top of my hat -- currently I'm ignorant of all the details)... -Alex
On May 23, 12:47 am, Darren New <d@san.rr.com> wrote: > [...] so you don't have to change http or smtp to > make it work the way you want. (From what I can tell, your proposal does > this already, but it's hard to be sure without thinking about it a whole > bunch.)
Yes, this transparency is the exact purpose of my proposal. The idea is that between a push/pop pair, you have a clear slate, exactly like on interp init. All previously registered handlers are "frozen", but reactivated when you cross the "pop". > Questions are likely to come up with multiple > vwaitadmin push calls, what happens if you do > vwaitadmin push > fileevent ... > vwaitadmin pop > (is the fileevent still around?)
No. The handler state is restored as it was before the push. (with possible delayed-removal of fileevents whose channels have been closed between push and pop) > vwaitadmin push > (is the fileevent disabled now?)
Which fileevent ? ;-) The slate is empty here. > I think the simple answers would work, but again I'd have to think about > it a whole bunch. Possibly adding sufficient introspection to stuff that > this can be done at the command level as a library might be better...
Yes, I had thought about this too. I would also have preferred the introspection method, because it allows to write much less in C and much more in Tcl, however it adds a big load of complexity because it implies to invent a script-level representation for handlers which are internal (like Windows Event objects or hidden /dev/dsp file descriptor in Snack, or the equally hidden X11 socket fd); and as mentioned above, to make it worse, even already scriptable handlers like [after] are currently not "freezable"... While a more traditional, monolithic C implementation would simply juggle the pointers to (a stack of) saved and current list-of-event- sources (off the top of my hat -- currently I'm ignorant of all the details)... -Alex
Alexandre Ferrieux wrote: >> vwaitadmin push >> fileevent ... >> vwaitadmin pop >> (is the fileevent still around?) > No. The handler state is restored as it was before the push.
See, I'm not sure that's reasonable. If I put in a fileevent that you don't even realize is in there, I wouldn't want it turned off again. I don't want "pop" to restore the previous state. I want it to reenable what "push" turned off, without disabling anything new, for example. If I call [pack] between push and pop, and then call vwaitadmin pop before I return, I don't want to update idletasks there lost, do I? I don't want [after] handlers started between push and pop to disappear when I do a pop. That wouldn't make sense to me. > (with possible delayed-removal of fileevents whose channels have been > closed between push and pop)
I suspect there are a bunch more things like that. Widgets deleted? >> vwaitadmin push >> (is the fileevent disabled now?) > Which fileevent ? ;-) > The slate is empty here.
Right. I meant, a second call to "push" doesn't reenable what was enabled in the previous call to push. Logical, but something you'd have to think about, or at least document. (Not everyone thinks the same way about these things.) -- Darren New / San Diego, CA, USA (PST) His kernel fu is strong. He studied at the Shao Linux Temple.
Alexandre Ferrieux wrote: > Yes, this transparency is the exact purpose of my proposal. > The idea is that between a push/pop pair, you have a clear slate, > exactly like on interp init. > All previously registered handlers are "frozen", but reactivated when > you cross the "pop".
That's error-prone. Better to have it so that the magical command takes a script argument and runs that script with all handlers defined before it starts disabled, re-enabling them when the script terminates. For why, see the analogy from SQLite with transactions: $db eval {BEGIN TRANSACTION} set code [catch { ... $db eval ... ... } error] if {$code == 1} { $db eval {ROLLBACK TRANSACTION} error $error } else { $db eval {COMMIT TRANSACTION} # Note that this is flaky; see sqlite docs for why } vs: $db transaction { ... $db eval ... ... } (For those of you with C++ experience, this is exactly like RAII. Except with syntax that doesn't suck.) Donal.
Donal K. Fellows wrote: > Alexandre Ferrieux wrote: >> Yes, this transparency is the exact purpose of my proposal. >> The idea is that between a push/pop pair, you have a clear slate, >> exactly like on interp init. >> All previously registered handlers are "frozen", but reactivated when >> you cross the "pop". > That's error-prone. Better to have it so that the magical command takes > a script argument and runs that script with all handlers defined before > it starts disabled, re-enabling them when the script terminates.
... Agreed. This is a much nicer interface. The existing [vwait] command could be extended to support this: vwait name ?script? With the script argument it suspends the current event loop and starts a new one (a new notifier/event queue). All event sources created within the script are created on the new event queue. The event loop then runs on the new queue until the variable is set, at which point the new notifier is destroyed (along with all new event sources) and the old one reinstated. An alternative would be to make an event queue/notifier a first-class object at the Tcl level so that you can do something like: set q [event queue] $q chan event $sock readable ... $q vwait myvar ;# loop on this event queue only Do slave interps have their own event queues? Or is it just threads? -- Neil
Neil Madden wrote: > Do slave interps have their own event queues? Or is it just threads?
Event queues are per-thread. Donal.
On May 23, 1:37 am, Darren New <d@san.rr.com> wrote: > > No. The handler state is restored as it was before the push. > See, I'm not sure that's reasonable. If I put in a fileevent that you > don't even realize is in there, I wouldn't want it turned off again. I > don't want "pop" to restore the previous state. I want it to reenable > what "push" turned off, without disabling anything new, for example.
That's your way of seeing it; it is different, and more complicated to implement since you have to merge lists of handlers, while mine just has to juggle the [event queue] objects suggested by Neil. > If I call [pack] between push and pop, and then call vwaitadmin pop > before I return, I don't want to update idletasks there lost, do I? I
The idea behind this was more to put small atomic things between push and pop, like the async socket with timeout of your http example; not intended for heavyweight Tk fiddling... > don't want [after] handlers started between push and pop to disappear > when I do a pop. That wouldn't make sense to me.
Same remark. Your aim seems to be different after all. Not surprising our specs diverge. -Alex
On May 23, 10:17 am, "Donal K. Fellows" <donal.k.fell @manchester.ac.uk> wrote: > Alexandre Ferrieux wrote: > > All previously registered handlers are "frozen", but reactivated when > > you cross the "pop". > That's error-prone. Better to have it so that the magical command takes > a script argument ...
Sure. I only wanted to focus on the core primitive, without immediately coupling it with an eval/uplevel... But granted, this is superior from the user's standpoint. -Alex
On May 23, 10:17 am, "Donal K. Fellows" <donal.k.fell @manchester.ac.uk> wrote: > Alexandre Ferrieux wrote: > > All previously registered handlers are "frozen", but reactivated when > > you cross the "pop". > That's error-prone. Better to have it so that the magical command takes > a script argument ...
Sure. I only wanted to focus on the core primitive, without immediately coupling it with an eval/uplevel... But granted, this is superior from the user's standpoint. -Alex
On May 23, 10:17 am, "Donal K. Fellows" <donal.k.fell @manchester.ac.uk> wrote: > Alexandre Ferrieux wrote: > > All previously registered handlers are "frozen", but reactivated when > > you cross the "pop". > That's error-prone. Better to have it so that the magical command takes > a script argument ...
Sure. I only wanted to focus on the core primitive, without immediately coupling it with an eval/uplevel... But granted, this is superior from the user's standpoint. -Alex
On May 23, 10:17 am, "Donal K. Fellows" <donal.k.fell @manchester.ac.uk> wrote: > Alexandre Ferrieux wrote: > > All previously registered handlers are "frozen", but reactivated when > > you cross the "pop". > That's error-prone. Better to have it so that the magical command takes > a script argument ...
Sure. I only wanted to focus on the core primitive, without immediately coupling it with an eval/uplevel... But granted, this is superior from the user's standpoint. -Alex
Alexandre Ferrieux wrote: > That's your way of seeing it; it is different, and more complicated to > implement since you have to merge lists of handlers, while mine just > has to juggle the [event queue] objects suggested by Neil.
Oh, I was thinking perhaps you'd have a flag on each event source that says whether you actually invoke what it's supposed to fire or not. I.e., disable or enable the event source, skipping over disabled ones, like [update idle] skips over non-idle events. Then push would disable all enabled events and remember which those were, and pop would just reenable those it disabled. >> If I call [pack] between push and pop, and then call vwaitadmin pop >> before I return, I don't want to update idletasks there lost, do I? I > The idea behind this was more to put small atomic things between push > and pop, like the async socket with timeout of your http example; not > intended for heavyweight Tk fiddling...
Right. >> don't want [after] handlers started between push and pop to disappear >> when I do a pop. That wouldn't make sense to me. > Same remark. Your aim seems to be different after all. Not surprising > our specs diverge.
That's my aim too. I just think it's more robust if my library doesn't mysteriously stop working simply because someone else made an "atomic" event. Consider this example: I send a mail message. The library I use to do this leaves the socket to the mail server open, with a file-event that watches for the remote side to close the socket (due to a timeout) and cleans up its local internal state if that happens. Like a PHP "persistant" connection. Now, the caller wraps things up, sends a message, and then unwraps. The mail library stops cleaning up internal state when the remote server closes the socket. I can't even imagine how you'd begin to track down such a memory leak, let alone cure it easily. I think a better design would be "pause all event sources now set up, don't trigger anything I'm not expecting, and run that stuff. Then, when I say so, unpause all the stuff I put on hold." Rather than "reset the state of the world". -- Darren New / San Diego, CA, USA (PST) His kernel fu is strong. He studied at the Shao Linux Temple.
|
 |
 |
 |
 |
|