Uploaded image for project: 'JDK'
  1. JDK
  2. JDK-4094565

Calling 'toFront()' on non-visible Window, Frame, and/or Dialogs is bad.

    XMLWordPrintable

    Details

    • Type: Bug
    • Status: Resolved
    • Priority: P4
    • Resolution: Fixed
    • Affects Version/s: solaris, 1.1, 1.1.4, 1.1.5, 1.1.8_003, 1.3.0, 1.3.1
    • Fix Version/s: 1.4.0
    • Component/s: client-libs
    • Subcomponent:
    • Resolved In Build:
      beta
    • CPU:
      generic, x86, sparc
    • OS:
      solaris_2.5.1, solaris_2.6, solaris_7, windows_95, windows_98, windows_2000

      Description



      Name: joT67522 Date: 11/21/97


      **** please CC: Saul Wold (sgw@eng) on any emails sent ****

      If an instance of Window, Frame, or Dialog is currently non-visible, and
      the 'toFront()' method is called, the window is typically shown, but
      the component doesn't think it's visible. This causes strange things
      to happen, like calling 'setVisible(false)' on the component will not
      cause the window to hide.

      The solaris source reveals that calling toFront() results in a call
      to 'pShow' in the Motif peer code, which always shows the X-Window.

      IT GETS WORSE!
      With respect to Dialogs, if the dialog is modal, calling "toFront()"
      will BLOCK inside the peer code! ie., there is still code in the
      Solaris peer code that does the blocking for modal dialogs, but there
      is also code in java.awt.Dialog to handle the block by creating another
      thread to process the event queue. So, if you call 'toFront()', the
      peer's blocking code goes off, but the new event queue thread never
      gets created. If the call was made from the AWT event queue thread,
      then no more java AWT events get dispatched, which is REALLY BAD.

      On windows NT, the problem of a hidden window showing itself does not
      exists, but calling "toFront()" on a hidden modal dialog does cause
      problems. The parent frame of the dialog will
      no longer accept mouse input after the 'toFront'
      method of the non-visible dialog has been called.
      (Review ID: 20570)
      ======================================================================

      phil.race@eng 1998-04-09

      There is a potential GUI lockup when using AWT modal dialogs on Solaris.
      If you call toFront() on a modal dialog which is already showing
      the GUI locks up.

      This bug is reproducible on Solaris/Motif with JDK 1.1.5 & JDK 1.2 Beta3

      Suppose an application has a button which shows a modal dialog
      In theory once the button is pressed, the modal dialog is immediately
      in effect and the user cannot press it again until the modal dialog
      is dismissed.

      However it is possible that the user can press this button twice before
      the modal dialog is in fact shown.

      This can happen if the application was "busy" enough that the user
      managed to press the button again before the modality is in effect.
      This can happen in real applications being run on busy or slow machines.

      The other way this bug can happen is if the application were to attempting
      to detect and prevent this from happening, and decide instead to just
      call toFront().

      In fact both of these reduce to the same internal problem since
      the Dialog's show method just calls toFront() if the Dialog is already
      visible.

      An argument could be made that this is "not a bug" and the application
      should always check whether the dialog is visible before showing it
      again. But our API docs certainly do not prohibit calling toFront() on a
      modal dialog.

      So we don't document not to call toFront, or show on an already visible
      modal dialog, and if it happens the application is completely locked.
      Also even if we did document it, this puts the onus on the application
      programmer to guard against this problem in all places s/he uses a modal
      dialog.

      Also this bug does not occur under NT, so an application programmer
      doing development and testing will see a platform specific bug.

      [[Actually there is ANOTHER Dialog bug I came across whilst looking
      at this one, also related to the toFront() method.
      The toFront() method is supposed to raise a window if already on
      screen, and to create and show it, if not already on screen.
      If you call toFront() on an unshown dialog, modal or otherwise it
      does not get shown.]]

      To reproduce the GUI lockup problem
      ------------------------------------

      Compile and run this program
      // ModalTestFrame.java
      import java.awt.*;
      import java.awt.event.*;

      public class ModalTestFrame extends Frame implements ActionListener {

        Button longButton;
        Button showButton;
        Dialog dialog;
        Button dismissButton;

        public static void main(String[] args) {
            ModalTestFrame f = new ModalTestFrame();
            f.setBounds(200,200,200,100);
            f.setVisible(true);
        }

        public ModalTestFrame() {
          super("Modal Test Frame");

          longButton = new Button("Long event");
          longButton.addActionListener(this);
          add("North", longButton);

          showButton = new Button("Show Dialog");
          showButton.addActionListener(this);
          add("South", showButton);

          dialog = new Dialog(this, "Modal Dialog 1", true);
          dismissButton = new Button("Dismiss 1");
          dismissButton.addActionListener(this);
          dialog.add("Center", dismissButton);
          dialog.setBounds(200,300,100,100);
       }

       public void actionPerformed(ActionEvent e) {
          if (e.getSource() == longButton) {
              System.out.println("Sleeping for 2 secs");
              try {
                    Thread.sleep(2000);
              } catch (InterruptedException ex) {
              }
          }
          if (e.getSource() == showButton) {
              System.out.println("Show");
              if (dialog.isVisible()) {
                 System.out.println("Already visible, bringing toFront");
                 dialog.toFront();
              }
              else
                dialog.setVisible(true);
              Toolkit.getDefaultToolkit().sync();
          }
          if (e.getSource() == dismissButton) {
              System.out.println("Dismiss");
              dialog.setVisible(false);
          }
       }

      }

      Using the test program press the button labelled "Long event". Next
      immediately press the "Show Dialog" button twice.

      You will see the following text printed out

      Sleeping for 2 secs
      Show
      Show
      Already visible, bringing toFront
      The 2nd "Show" message indicates that the application had queued
      a 2nd button action before the modal dialog from the 1st took effect.
      The test program could have called setVisible(true) again but in this
      case actually calls toFront().
      Both of these cause the lockup and neither should!

      The following extract from a thread dump of the test program
      (below) shows what happens.

      Full thread dump:
          "AWT-Modal" (TID:0xee312348, sys_thread_t:0x24e9c8, state:CW) prio=5
              at java.lang.Object.wait(Native Method)
              at sun.awt.motif.MDialogPeer.pShow(Native Method)
              at sun.awt.motif.ModalThread.run(MDialogPeer.java:212)
          "AWT-Dispatch-Proxy" (TID:0xee312318, sys_thread_t:0x24c418, state:CW) pri
      o=5
              at java.lang.Object.wait(Native Method)
              at sun.awt.motif.MDialogPeer.pShow(Native Method)
              at sun.awt.motif.MDialogPeer.toFront(MDialogPeer.java:181)
              at java.awt.Window.toFront(Window.java:231)
              at ModalTestFrame.actionPerformed(ModalTestFrame.java:47)
              at java.awt.Button.processActionEvent(Button.java:248)
              at java.awt.Button.processEvent(Button.java:221)
              at java.awt.Component.dispatchEventImpl(Component.java:2041)
              at java.awt.Component.dispatchEvent(Component.java:1951)
              at java.awt.EventQueue.dispatchEvent(EventQueue.java:167)
              at java.awt.EventDispatchThread.run(EventDispatchThread.java:45)

      Monitor Cache Dump:
          sun.awt.motif.ModalThread@EE312348/EE39EE80: owner "AWT-Modal" (0x24e9c8,
      1 entry)
              Waiting to be notified:
                  "AWT-EventQueue-0" (0x1bb150)

      Basically we have two threads, each executing the pShow() native method for
      the same dialog peer.

      The first thread is waiting on notification from the event dispatcher thread,
      via the awt_lock object, that an event is ready and has been dispatched.
      But the other thread waiting *is* the event dispatcher thread, and its
      waiting on a condition too: not dispatching events.
      It gets woken up whenever there's input, but immediately waits again
      because it believes there's some other modal dialog active that this
      one needs to wait for.

      Here's some more detail on how the modal show is supposed to work:

      The "AWT-Dispatch-Proxy" event dispatcher thread is created when attempting to
      show a modal dialog from an event processing thread (in this case
      the main AWT dispatcher thread, "AWT-EventQueue-0").
      This is because the thread doing the show will block but we must
      continue dispatching events. the proxy does this while the modal is showing.
      If we were to block the event dispatcher, without starting the proxy thread,
      then the GUI would freeze, because events would simply queue up and never
      be delivered, including the ones to the dialog which would dismiss it.

      Next the "AWT-Modal" thread is created by the dialog's peer object to
      do the actual show. The original thread ("AWT-EventQueue-0" in the case of
      the first show) waits on a notification from this thread.
      This indirection via the "AWT-Modal" thread is needed to have a
      minimal and trusted call stack in the showing thread.
      That the "AWT-EventQueue-0" (main AWT event queue) holds the AWT-Modal lock
      can be confirmed looking at the Monitor Dump Cache.

      The AWT-Modal thread calls the peer's pShow() native method.
      pShow() then calls a function which will wait for the next event
      to be delivered. To do this the called function obtains acquires the toolkit's
      awt_lock, but then releases it and waits for notification on that lock
      which is interpreted to mean that an event is ready for processing.

      That's an overview of what happens.

      However in the second show when we do a toFront() from the proxy dispatcher
      thread, there is no new dispatcher set up to take over.
      Possibly because it was supposed that toFront really did nothing more
      than a window raise and return ..
      But knowing that it calls the same show code we could expect this lock up.
      However there is one more wrinkle which contributes to this bug.
      We don't even get to the point where the thread blocks waiting for
      the modal to be dismissed.
      The code actually thinks another modal has been popped up above this
      one and is waiting for it.

      The modal dialog is assigned a number which is probably used to
      support nested modal dialogs.
      This number is intended to be per dialog, but ends up being per thread.
      The value gets set to 2 because there are 2 threads trying to show the dialog.

      One thread is now blocked waiting for the active modal to be number 1.
      The seconds thread numbered 2 is blocked waiting for events, but not getting
      them because the other thread is the dispatcher.

      Workaround:
      Never call toFront() on a Dialog, use show() or setVisible(true);
      Always check if a Dialog is visible before showing it, and don't
      make the call if its not needed.


        Attachments

          Issue Links

            Activity

              People

              Assignee:
              dmendenhsunw David Mendenhall (Inactive)
              Reporter:
              johsunw Joon Oh (Inactive)
              Votes:
              0 Vote for this issue
              Watchers:
              0 Start watching this issue

                Dates

                Created:
                Updated:
                Resolved:
                Imported:
                Indexed: