Login  Register

Re: KeyListener problem

Posted by Michael Schmid on Mar 18, 2020; 2:14pm
URL: http://imagej.273.s1.nabble.com/KeyListener-problem-tp5023044p5023053.html

Hi Kenneth,

a few thoughts:

If you suspect a race condition between ImageListener and KeyListener,
can you try with the ImageJ daily build?
Since a few days, the ImageListener callbacks are in the EventQueue.
KeyListener callbacks are in the EventQueue anyhow.  So a race condition
between these callbacks cannot happen with the daily build.

By the way, after you are done, don't forget to register ImageJ as a
KeyListener again.

Instead of an endless loop for waiting, it would be nicer to use the
Java 'wait' and 'notify' mechanism (both must be in 'synchronized'
blocks).  There is an example in the PlotWindow class:

https://github.com/imagej/imagej1/blob/master/ij/gui/PlotWindow.java#L836
https://github.com/imagej/imagej1/blob/master/ij/gui/PlotWindow.java#L865


In your code with the endless loop, it would be better to declare
'waitingForKey' volatile or have an AtomicBoolean for it.  Otherwise, if
the threads running the keyListener callback (the EventQueue) and the
one running the endless loop are on different CPU cores, the one running
the event queue may have the "waitingForKey" variable in its own CPU
cache and it may go undetected that another core modifies it.  So the
statement
 > waitingForKey = true;
 > while(waitingForKey) Thread.sleep(100);
might actually behave like this:
 > while(true) Thread.sleep(100);

If you use the synchronized notify/wait mechanism instead, there is no
need for declaring variables as volatile because synchronized blocks
also care about the cache problem.

Michael
______________________________________________________________________

On 18/03/2020 2:17 pm, Kenneth Sloan wrote:

> This is a JAVA plugin.  It's too big to post all of it - but here are what I think are the relevant parts.  Again - it works perfectly on one machine - and fails intermittently on another.
>
> Perhaps someone who has done this before can spot my error.  I was following the template for keyListener on the imageJ site.
>
>
> public class AVL_Area_Fraction implements PlugIn, KeyListener, ImageListener
> {
> ...
>      private boolean waitingForKey = false;
>      private int keyCode;
>      private char keyChar;
>      private int modifiers;
>      @Override
> public void keyTyped(KeyEvent e) {}
>      @Override
> public void keyReleased(KeyEvent e) {}
>      @Override
> public void keyPressed(KeyEvent e)
>      {
> IJ.log("keyPressed");
> if(!waitingForKey) return; // ignore keyboard when we don't want a key
> keyCode = e.getKeyCode();
> keyChar = e.getKeyChar();
> modifiers = e.getModifiers();
> IJ.log("   "+keyCode+"   "+keyChar+"   "+modifiers);
> waitingForKey = false;
>      }
>        
>      @Override
>      public void imageClosed(ImagePlus imp)
>      {
> IJ.log("imageClosed");
> if(null!=win) win.removeKeyListener(this);
> if(null!=canvas) canvas.removeKeyListener(this);
> ImagePlus.removeImageListener(this);
> IJ.log("removed Listeners");
>      }
>      @Override
>      public void imageOpened(ImagePlus imp){}
>      @Override
>      public void imageUpdated(ImagePlus imp){}
>      @Override
> public void run(String arg0)
>      {
> ... other setup
> // listen to keyboard - stop IJ from listening!
> win.removeKeyListener(IJ.getInstance());
> canvas.removeKeyListener(IJ.getInstance());
> IJ.log("removed IJ Listeners");
> win.addKeyListener(this);
> canvas.addKeyListener(this);
> ImagePlus.addImageListener(this);
> IJ.log("added Listeners");
> ...
>
> int category;
> try
>    {
> win.requestFocus();
> IJ.log("sampling...");
> category = sampleAt(...);
> IJ.log("sampled");
>    }
> catch (Exception e)
>    {
> throw new RuntimeException(programName+" interrupted");
>    }
>   ...
> // done listening to the keyboard
> win.removeKeyListener(this);
> canvas.removeKeyListener(this);
> ImagePlus.removeImageListener(this);
> IJ.log("removed Listeners");
> ...
>
>     private int sampleAt(...)
> throws InterruptedException
>      {
>        ...
> while(true)
>    {
> waitingForKey = true;
> while(waitingForKey) Thread.sleep(100);
>
> // key pressed!
> // keyCode, keyChar, and modifiers are current
> if(keyCode == KeyEvent.VK_SPACE) return -1; // DELETE does not work
> if(keyCode == KeyEvent.VK_ENTER) return -2; // no mas!
> int category = keyCode - KeyEvent.VK_1; // 1-based keys; 0-based category
> if(category < 0) continue; // try again!
> if(category >= choices.length) continue; // try again!
> return category; // good category!
>    }
>      }
> }
>
>
>
> Looking at this, I'm wondering if there might be a race condition between adding the Listeners and going into the busy wait for "waitingForKey" to become false.  But...that doesn't look like an issue - so I'm just at a loss.
>
> When this fails, the log window shows
>
> ============================
> removed IJ Listeners
> added Listeners
> sampling...
> ============================
>
> So, it's clearly (?) stuck in the wait loop, and keyPressed is never called.
>
> If anyone is up for it, I'd be happy to send the entire plugin for testing.
>
> Again - works perfectly on one machine and fails intermittently on another.  Both mac laptops - the failing one is older than the one that works.  I think the "client" is currently testing on a 3rd laptop to see if the problem is specific to that one machine.
>
> --
> Kenneth Sloan
> [hidden email]
> Vision is the art of seeing what is invisible to others.
>
> --
> ImageJ mailing list: http://imagej.nih.gov/ij/list.html
>

--
ImageJ mailing list: http://imagej.nih.gov/ij/list.html