
google-web-toolkit - issue #8101
Timer.cancel() not always effective in old versions of IE
Found in GWT Release (e.g. 2.4.0, 2.5.0 RC):
2.5.1, should also be present in older versions
Encountered on OS / Browser (e.g. WinXP, IE8-9, FF7):
IE6 - IE8
Detailed description (please be as specific as possible):
clearTimeout has no effect in older versions of IE if the event has already been added to the event loop queue. For this to happen, there must be something else in the event loop that runs after the timeout has expired (so that the it has been added to the event loop queue) but before the timeout is run (so that clearing the timeout has no effect).
Shortest code snippet which demonstrates issue (please indicate where actual result differs from expected result):
This code snippet shows an alert in IE 8 and older because the timer is not properly canceled. In other browsers, the alert is not shown because the timer has been canceled.
final Timer canceledTimer = new Timer() {
@Override
public void run() {
Window.alert("Canceled timer executed");
}
};
canceledTimer.schedule(1000);
final Timer cancelingTimer = new Timer() {
@Override
public void run() {
canceledTimer.cancel();
}
};
cancelingTimer.schedule(500);
double start = Duration.currentTimeMillis();
while (Duration.currentTimeMillis() - start <= 2000) {
// Busy wait so that both timers will be added to the event loop queue the next time it is updated
}
Workaround if you have one:
Manually track of whether the timer has been canceled (e.g. by overriding Timer.cancel()) and check for that before running the actual action. This does however get complicated if you want to be able to reliably start the timer again after it has been canceled.
Comment #1
Posted on Apr 10, 2013 by Swift RhinoWow, that means that actually Timer can be completely broken: if you re-schedule a Timer instance, it cancelTimeout and then setTimeout again, so you cannot track whether it's been cancelled and when run() is called you cannot know whether it comes from the first schedule() or the second. You can track the time-delta but that's not reliable either: if something (long-running operation in another event) delays the first (unexpected) run() your time-tracking code can think it's the "expected call", but that one will be queued too so you'll have run() called twice and you cannot tell whether the first one is a spurious one (non-cancelled first scheduled timer) or not. Add to that that Timer's real add-value is being able to be cancelled and re-scheduled (compared to Scheduler) and that makes Timer totally broken; and the only reliable way or cancelling/rescheduling tasks is to use Scheduler without reusing your ScheduledCommand instances (so you can easily and reliably cancel your tasks by setting a 'cancel' state in your ScheduledCommand instances).
I'd be tempted to call this a KnownQuirk as I don't see a reliable workaround :-(
Comment #2
Posted on Apr 10, 2013 by Happy BearPotential generic workaround: Timer.cancel() could update a counter. When setTimeout is called, the current value of that counter is included in the closure. When the callback is eventually run, it can compare its own counter value with the current value. If the values match, Timer.run() can be invoked as we know that cancel() has not been called between the moment the timer was scheduled and the moment it was fired. If the values don't match, it means that cancel() has been called in between which means that Timer.run() should not be invoked.
Comment #3
Posted on Apr 10, 2013 by Swift RhinoAh yes, indeed, great simple lightweight solution!
Comment #4
Posted on Apr 10, 2013 by Happy BearI wouldn't have invented the workaround unless you had formulated the issue the way you did.
Will you start working on this right away or should I create a patch? We (Vaadin) would like to get a fix to this into the GWT repository as soon as possible.
Comment #5
Posted on Apr 10, 2013 by Swift RhinoYes please, provide the patch. I have way too many things to do already without picking that task, as simple as it could be :-)
Comment #6
Posted on Apr 12, 2013 by Happy BearSeems IE is also sensitive to the order in which different timers are scheduled. The sample code in the bug description is not effective if just run directly in onModuleLoad. To make it break there, canceledTimer.schedule(1000); should be moved to after cancelingTimer.schedule(500);.
It seems I originally tested the issue in a slightly more complex context where there were also other timeouts that had similar effects on my timers even though they were not scheduled in the "right" order.
Comment #7
Posted on Apr 15, 2013 by Happy BearComment #8
Posted on Apr 15, 2013 by Swift Rhino(No comment was entered for this change.)
Comment #9
Posted on May 8, 2013 by Massive Cat(No comment was entered for this change.)
Comment #10
Posted on May 8, 2013 by Swift Rhinor11611
Comment #11
Posted on May 13, 2014 by Swift Rhino(No comment was entered for this change.)
Comment #12
Posted on Jul 30, 2014 by Happy WombatI am currently using gwt 2.4.0. I want to include the above Timer patch for Issue 8101. How do I do it?
Status: Fixed
Labels:
Milestone-2_6