Skip to content

NullPointerException thrown in PApplet.dequeueWindowEvents() #918

Closed
@hx2A

Description

@hx2A

I had several running Sketches and was moving the windows around when this exception was thrown:

java.lang.NullPointerException: Cannot invoke "java.lang.Integer.intValue()" because the return value of "java.util.Map.remove(Object)" is null
        at processing.core.PApplet.dequeueWindowEvents(PApplet.java:9740)                                        
        at processing.core.PApplet.handleDraw(PApplet.java:2085)
        at processing.opengl.PSurfaceJOGL$DrawListener.display(PSurfaceJOGL.java:840)                             
        at jogamp.opengl.GLDrawableHelper.displayImpl(GLDrawableHelper.java:692)
        at jogamp.opengl.GLDrawableHelper.display(GLDrawableHelper.java:674)
        at jogamp.opengl.GLAutoDrawableBase$2.run(GLAutoDrawableBase.java:443)                                    
        at jogamp.opengl.GLDrawableHelper.invokeGLImpl(GLDrawableHelper.java:1293)
        at jogamp.opengl.GLDrawableHelper.invokeGL(GLDrawableHelper.java:1147)
        at com.jogamp.newt.opengl.GLWindow.display(GLWindow.java:782)
        at com.jogamp.opengl.util.AWTAnimatorImpl.display(AWTAnimatorImpl.java:81)
        at com.jogamp.opengl.util.AnimatorBase.display(AnimatorBase.java:453)
        at com.jogamp.opengl.util.FPSAnimator$MainTask.run(FPSAnimator.java:178)
        at java.base/java.util.TimerThread.mainLoop(Timer.java:566) 
        at java.base/java.util.TimerThread.run(Timer.java:516)

I see what the problem is. In the code there is this:

  /**
   * Internal use only: called by Surface objects to queue a position
   * event to call windowPositioned() when it's safe, which is after
   * the beginDraw() call and before the draw(). Note that this is
   * only the notification that the window is in a new position.
   */
  public void postWindowMoved(int newX, int newY) {
    if (external && !fullScreen) {
      frameMoved(newX, newY);
    }

    windowEventQueue.put("x", newX);
    windowEventQueue.put("y", newY);
  }


  /** Called when the window is moved */
  public void windowMoved() {  }


  private void dequeueWindowEvents() {
    if (windowEventQueue.containsKey("x")) {
      windowX = windowEventQueue.remove("x");
      windowY = windowEventQueue.remove("y");    // line 9740, where exception was thrown
      windowMoved();
    }
    if (windowEventQueue.containsKey("w")) {
      // these should already match width/height
      //windowResized(windowEventQueue.remove("w"),
      //              windowEventQueue.remove("h"));
      windowEventQueue.remove("w");
      windowEventQueue.remove("h");
      windowResized();
    }
  }

According to the comment, that postWindowMoved() method gets called by Surface objects "after the beginDraw() call and before the draw()".

That dequeueWindowEvents() method gets called in handleDraw() at a point that is also after the beginDraw() call and before the draw().

Putting both "x" and "y" in windowEventQueue is not an atomic operation, even though the queue is a ConcurrentHashMap, which is thread safe. This must have been a fluke exception where postWindowMoved() and dequeueWindowEvents() were executing at the same time, and "x" was added but "y" had not.

This is a rare exception and is probably next to impossible to reproduce. Nevertheless, it should be an easy thing to fix, so let's fix it, and I'd like to work on it.

My proposed solution is for windowEventQueue to store pairs of numbers, for both x & y, and w & h. We can create a simple dummy class Pair that we put and remove from the queue.

Metadata

Metadata

Assignees

Labels

No labels
No labels

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions