Bug 1323 : Concurrency problem when using noLoop()
Last modified: 2010-06-05 03:19



Assigned To:

Attachment Type Created Size Actions
Test case text/plain 2009-09-10 22:05 594 bytes

Description:   Opened: 2009-09-10 21:59
There's a concurrency problem between the draw() function and key/mouse
events when running in noLoop() mode. If you are in loop() mode then
input events are queued and only delivered after draw() has finished
executing. But when you are in noLoop() mode this doesn't happen. Input
events can actually be delivered and processed in the middle of draw(). If
the event handler modifies some variables that draw() is using then they'll
change unexpectedly.

Processing seems designed to hide this kind of complexity from users by,
for example, not exposing any threading primitives. So I think this should
be fixed on the Processing side.

At first it might seem simple to fix by changing the "if (looping)" check
in checkKeyEvent and checkMouseEvent to "if (looping || redraw)". This
doesn't quite solve the problem since draw() can call noLoop() and from
that point on it would be vulnerable again. I think the events should be
queued whenever draw() is actually executing.

Processing v1.0.7 on Windows XP
Additional Comment #1 From jnz 2009-09-10 22:05
Test case

Here's a test case to demonstrate the problem. Run it and press any key. Both
numbers will tick up. Press a key multiple times in rapid succession (or hold
a key down) and you'll see the value change unexpectedly in the middle of the
draw() function. If you comment out the noLoop() and redraw() calls you'll see
that this problem doesn't happen in looping mode.
Additional Comment #2 From fry 2009-09-14 09:28
Right.. unfortunately we probably need to simply say that you can't draw in
mouse/key events when using noLoop(). If you're posting that many events
that fire redraws, people should be using loop() anyway.
Additional Comment #3 From jnz 2009-09-14 15:15
I don't understand. The test case I posted doesn't make any drawing calls
from within the key handler.

I'm also not sure I follow your logic about always using loop(). I'm in a
situation where the sketch normally runs at 10fps when in "play mode". But
when it is in "stop mode" there will be no action and so no need to update
the screen. I can call noLoop() to stop the applet from sucking up CPU
time. But then if the user clicks or presses a key I'll need one (and only
one) screen update to show the changes they've made. Unless, of course,
they click on "play" in which case I'd call loop() again.

Right now this is dangerous because the draw() routine can be interrupted
without my knowledge to go handle a key/mouse event. And when it comes
back the invariants that one would just assume to hold have been violated.

I don't really know enough about how Java handles these events to offer a
complete technical solution. Are they being delivered on the same thread
as the one running draw() or a separate one? If the former, how does the
drawing code decide when to go off and handle an event? If the latter,
couldn't one simply add a big synchronize lock around all the user-code to
avoid any reentrancy problems?
This bug is now being tracked here.