You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@wicket.apache.org by "Sven Meier (Jira)" <ji...@apache.org> on 2021/01/15 11:50:00 UTC

[jira] [Commented] (WICKET-6861) Possible race condition in clearing and triggering of Wicket timers

    [ https://issues.apache.org/jira/browse/WICKET-6861?page=com.atlassian.jira.plugin.system.issuetabpanels:comment-tabpanel&focusedCommentId=17265968#comment-17265968 ] 

Sven Meier commented on WICKET-6861:
------------------------------------

According to this StackOverflow answer your explanation isn't correct:

[https://stackoverflow.com/questions/46160745/javascript-any-way-that-settimeout-might-be-called-despite-being-cleartimeout#46160786]

Step 7.1 here [https://html.spec.whatwg.org/multipage/timers-and-user-prompts.html#timer-initialisation-steps]

Are you sure you're clearing the correct timer?

But if your fix works, I'm ok with adding that extra check to setTimeout().

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