I had a consistent problem while handling touch-events in an application that has noLoop() in setup() and uses redraw() whenever the screen needs to be redrawn after a relevant touch-event. The exception "Nothing left on the event queue" was thrown from InternalEventQueue.remove() at arbitrary moments.
The problem was that two threads tried to dequeue the eventsQueue concurrently. The GLThread called PApplet.dequeueEvents() from PApplet.handleDraw() concurrently with the main thread which called PApplet.dequeueEvents() from PApplet.postEvent(), which is called from PApplet.nativeMotionEvent(). Below you can find a trace of a concrete situation.
I'm not deeply familiar with the implementation, but it seems that PApplet.postEvent() should not call PApplet.dequeueEvents() when either looping or redraw is true. This would be fixed as follows:
public void postEvent(processing.event.Event pe) {
eventQueue.add(pe);
- if (!looping) {
+ if (!looping && !redraw) {
dequeueEvents();
}
}
It might also be necessary to synchronize dequeueEvents() to avoid a potential race condition when postEvent() in the main thread checks the (!looping && !redraw) condition right before redraw is set to true in redraw(). The main thread would then continue with dequeueEvents() while the GLThread would at some point call handleDraw() which would call dequeueEvents() concurrently.
02 x 05-26 10:51:49.267: I/System.out(8800): >> handleDraw() - count: 0, offset: 0, thread: Thread[GLThread 10,5,main]
03 05-26 10:51:49.277: I/System.out(8800): >> nativeMotionEvent() - count: 0, offset: 0, thread: Thread[main,5,main]
04 05-26 10:51:49.277: I/System.out(8800): >> postEvent() - count: 0, offset: 0, thread: Thread[main,5,main]
05 05-26 10:51:49.277: I/System.out(8800): >> InternalEventQueue.add() - count: 0, offset: 0, thread: Thread[main,5,main]
06 05-26 10:51:49.277: I/System.out(8800): << InternalEventQueue.add() - count: 1, offset: 0, thread: Thread[main,5,main]
07 x 05-26 10:51:49.287: I/System.out(8800): >> dequeueEvents() - count: 1, offset: 0, thread: Thread[GLThread 10,5,main]
08 05-26 10:51:49.287: I/System.out(8800): - looping: false, thread: Thread[main,5,main]
09 x 05-26 10:51:49.287: I/System.out(8800): >> InternalEventQueue.available() - count: 1, offset: 0, thread: Thread[GLThread 10,5,main]
10 05-26 10:51:49.287: I/System.out(8800): >> dequeueEvents() - count: 1, offset: 0, thread: Thread[main,5,main]
11 x 05-26 10:51:49.287: I/System.out(8800): - while-step in dequeueEvents() - count: 1, offset: 0, thread: Thread[GLThread10,5,main]
12 05-26 10:51:49.287: I/System.out(8800): >> InternalEventQueue.available() - count: 1, offset: 0, thread: Thread[main,5,main]
13 05-26 10:51:49.287: I/System.out(8800): - while-step in dequeueEvents() - count: 1, offset: 0, thread: Thread[main,5,main]
14 x 05-26 10:51:49.297: I/System.out(8800): >> InternalEventQueue.remove() - count: 1, offset: 0, thread: Thread[GLThread 10,5,main]
15 x 05-26 10:51:49.297: I/System.out(8800): << InternalEventQueue.remove() - count: 0, offset: 0, thread: Thread[GLThread 10,5,main]
16 x 05-26 10:51:49.297: I/System.out(8800): >> InternalEventQueue.available() - count: 0, offset: 0, thread: Thread[GLThread 10,5,main]
17 05-26 10:51:49.297: I/System.out(8800): >> InternalEventQueue.remove() - count: 0, offset: 0, thread: Thread[main,5,main]
public void handleDraw() {
println(">> handleDraw() - count: " + eventQueue.count + ", offset: "
+ eventQueue.offset + ", thread: " + Thread.currentThread());
...
}
protected void nativeMotionEvent(android.view.MotionEvent motionEvent) {
println(">> nativeMotionEvent() - count: " + eventQueue.count + ",
offset: " + eventQueue.offset + ", thread: " + Thread.currentThread());
...
}
class InternalEventQueue {
...
synchronized void add(Event e) {
println(">> InternalEventQueue.add() - count: " + count + ",
offset: " + offset + ", thread: " + Thread.currentThread());
if (count == queue.length) {
queue = (Event[]) expand(queue);
}
queue[count++] = e;
println("<< InternalEventQueue.add() - count: " + count + ",
offset: " + offset + ", thread: " + Thread.currentThread());
}
synchronized Event remove() {
println(">> InternalEventQueue.remove() - count: " + count + ",
offset: " + offset + ", thread: " + Thread.currentThread());
if (offset == count) {
throw new RuntimeException("Nothing left on the event queue.");
}
Event outgoing = queue[offset++];
if (offset == count) {
// All done, time to reset
offset = 0;
count = 0;
}
println("<< InternalEventQueue.remove() - count: " + count + ",
offset: " + offset + ", thread: " + Thread.currentThread());
return outgoing;
}
synchronized boolean available() {
println(">> InternalEventQueue.available() - count: " + count + ",
offset: " + offset + ", thread: " + Thread.currentThread());
return count != 0;
}
}
public void postEvent(processing.event.Event pe) {
println(">> postEvent() - count: " + eventQueue.count + ", offset: "
+ eventQueue.offset + ", thread: " + Thread.currentThread());
eventQueue.add(pe);
println(" - after add in postEvent() - count: " + eventQueue.count +
", offset: " + eventQueue.offset + ", thread: " +
Thread.currentThread());
println(" - looping: " + looping + ", thread: " +
Thread.currentThread());
if (!looping) {
dequeueEvents();
}
}
protected void dequeueEvents() {
println(">> dequeueEvents() - count: " + eventQueue.count + ",
offset: " + eventQueue.offset + ", thread: " + Thread.currentThread());
//boolean available = eventQueue.available();
while (eventQueue.available()) {
println(" - while-step in dequeueEvents() - count: " +
eventQueue.count + ", offset: " + eventQueue.offset + ", thread: " +
Thread.currentThread());
Event e = eventQueue.remove();
...
}
}