[ https://issues.apache.org/jira/browse/WICKET-6861?page=com.atlassian.jira.plugin.system.issuetabpanels:comment-tabpanel&focusedCommentId=17270011#comment-17270011 ]
Emond Papegaaij commented on WICKET-6861: ----------------------------------------- No, that won't help. The trigger function clears the timer before it does the Ajax call. Adding that precondition will prevent all ajax calls for that timer: {code:javascript} Wicket.TimerHandles[timerId] = setTimeout(function() { Wicket.Timer.clear(timerId); f(); // this does the ajax call and here the timer is is never in Wicket.TimerHandles }, delay); {code} Also, if we change the trigger function to not clear {{timerId}}, it will still not work, because the Ajax response of the first timer trigger in my event trace will set it again. I think the only way to really fix this, is to mark a timer as stopped in {{Wicket.TimerHandles}}. For example, we could put {{false}} in {{Wicket.TimerHandles[timerId]}} and have {{Wicket.Timer.set}} check that. However, that will break restarting a timer. The cause of the problem lies in the fact that the timer does not really live on the client side while it is executing its Ajax request. > Possible race condition in clearing and triggering of Wicket timers > ------------------------------------------------------------------- > > Key: WICKET-6861 > URL: https://issues.apache.org/jira/browse/WICKET-6861 > Project: Wicket > Issue Type: Bug > Components: wicket-core > Affects Versions: 9.2.0 > Reporter: Emond Papegaaij > Priority: Minor > > In our test suite we see some intermittent failures related to > {{AbstractAjaxTimerBehavior}}. At a few places in our application, we use a > background poller at a 1 sec interval that checks for an out-of-band > submission of the form data. So the user either has to fill the form via the > web interface or via some other route. Either route can complete the form, > but as soon as one of the two is triggered, the other must stop. The race > condition lies in the {{AbstractAjaxTimerBehavior}} triggering while the user > has already submitted the form manually. > The naive implementation would stop {{AbstractAjaxTimerBehavior}} via > {{Wicket.Timer.clear('timer0')}} in the Ajax response of the form submit. > However, this results in a very large gap between the moment of submission > and the actual moment the timer is stopped. To fix this, we've added the > following code to the {{AjaxSubmitLink}}: > {code:java} > @Override > public void renderHead(IHeaderResponse response) { > super.renderHead(response); > response.render(OnDomReadyHeaderItem.forScript( > "Wicket.Event.add('" + getMarkupId() + "', 'click', " + > "function() {Wicket.Timer.clear('" + getTimerId() + "');})")); > } > {code} > This puts the call to {{Wicket.Timer.clear}} before the actual Ajax submit > and should therefore prevent the timer from triggering after the Ajax submit > is done. However, in very rare occurrences we still see the timer triggering. > When it happens, the timer is always directly after the Ajax submit (often > within 10ms). This makes us believe the current implementation has a race > condition in the following way: > * The user clicks the Ajax submit link > * The execution of the event handlers is started in the browser > * At this moment the {{setTimeout}} triggers, but it is suspended because JS > is single threaded and the browser is already execution JS in response to UI > interaction > * The first event handler now clears the timer > * The second event handler performs the Ajax call > * Now the JS executor is freed and the timer function is executed > Although I'm not entirely at home in the execution model of JS in a browser, > this is the only explanation I could come up with. There is no way to > reproduce it, other than run a complex test suite 1000ths of times. My > proposed fix is to replace the timeout function in {{wicket-ajax-jquery.js}} > in {{Wicket.Timer.set}} with this: > {code:javascript} > Wicket.TimerHandles[timerId] = setTimeout(function() { > if (timerId in Wicket.TimerHandles) { > Wicket.Timer.clear(timerId); > f(); > } > }, delay); > {code} > This should prevent the function {{f()}} to be executed after the timer has > been cleared (Wicket.Timer.clear deletes the {{timerId}} from > {{Wicket.TimerHandles}}. -- This message was sent by Atlassian Jira (v8.3.4#803005)