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

Deadlock in List.class

XMLWordPrintable

    • 1.1.4
    • x86
    • solaris_1, windows_nt
    • Verified

      The following bug fix is from Oracle, a test case will be added later.

      DESCRIPTION:
      Deadlock in List.class due to list monitor plus inter-thread send

      The Event Queue Thread calls into the peer while holding the List's monitor
      and the peer does an inter-thread send. Meanwhile the Window Thread calls a
      method in the List and waits for the monitor.
      The peer has to call SendMessage because it is fetching information from the
      List Box Control. Either the monitor must not be held while calling the peer
      or the Windows thread must not try to obtain the monitor.

      ORACLE'S FIX:
      The fix is isolated to List.select(). Here is the new code:
       
          public void select(int index) {
              // Bug #503978: select can't be synchronized while calling the peer,
              // because it is called from the Window Thread. It is sufficient to
              // synchronize the code that manipulates 'selected' except for the case
              // where the peer changes. To handle this case, we simply repeat the
              // selection process.
               
              ListPeer peer;
               
              do {
                  peer = (ListPeer)this.peer;
                  if (peer != null) {
                      peer.select(index);
                      return;
                  }
                   
                  synchronized(this)
                  {
                      boolean alreadySelected = false;
       
                      for (int i = 0 ; i < selected.length ; i++) {
                          if (selected[i] == index) {
                              alreadySelected = true;
                              break;
                          }
                      }
       
                      if (!alreadySelected) {
                          if (!multipleMode) {
                              selected = new int[1];
                              selected[0] = index;
                          } else {
                              int newsel[] = new int[selected.length + 1];
                              System.arraycopy(selected, 0, newsel, 0,
      selected.length);
                              newsel[selected.length] = index;
                              selected = newsel;
                          }
                      }
                  }
              } while (peer != this.peer);
          }

      _____________________________________________________
      Jeff Melnick 500 Oracle Parkway
      Fundamental Technologies Box 2op800
      Oracle Corp. Redwood Shores, CA 94065
      (415)506-9573
       
      "Strangers stopping strangers just to shake their hands"
       
      **************************** Test Case *********************

      The bug is easy to verify visually but here is a test case that demonstrates
      the deadlock. If you remove the _mThread.start(), the deadlock still happens
      but it takes much longer.
       
      To reproduce: Run the following applet and keep mouse clicking on the listbox
      to select various items. Eventually you will deadlock.
       
      import java.applet.*;
      import java.awt.*;
      import java.awt.event.*;
      public class DeadList
             extends Applet
             implements ItemListener, Runnable
      {
          private List _mList;
          private Thread _mThread;
       
          public DeadList()
          {
          }
       
          public void run()
          {
            System.out.println("Thread Started");
            while (true)
            {
                int indices[] = _mList.getSelectedIndexes();
                System.out.println("Items Selected");
                try { wait(1); } catch (Exception ex) { }
            }
          }
       
          public void start()
          {
              _mList = new List(10, true);
              _mList.addItem("Line 1");
              _mList.addItem("Line 2");
              _mList.addItem("Line 3");
              _mList.addItem("Line 4");
              _mList.addItem("Line 5");
              _mList.addItem("Line 6");
              _mList.addItem("Line 7");
              _mList.addItem("Line 8");
              _mList.addItem("Line 9");
              _mList.addItem("Line 10");
              _mList.addItem("Line 11");
              _mList.addItem("Line 12");
              _mList.addItem("Line 13");
              _mList.addItem("Line 14");
              _mList.addItem("Line 15");
              _mList.addItem("Line 16");
              _mList.addItem("Line 17");
              _mList.addItem("Line 18");
              _mList.addItem("Line 19");
              _mList.addItemListener(this);
              Panel panel = new Panel();
              panel.setLayout(new FlowLayout(FlowLayout.CENTER, 30, 30));
              panel.add(_mList);
              add(panel);
              _mThread = new Thread(this);
              _mThread.start();
              show();
              validate();
          }
       
          public void itemStateChanged(ItemEvent event)
          {
            int indices[] = _mList.getSelectedIndexes();
            System.out.println("Items Selected");
          }
      }
       ===============================================================================

      daniel.indrigo@Canada 1997-09-16 This bug has also been reported by another licensee and they have a concern about Oracle's suggested fix.

      This is occurring on 1.1.4 obtained from
      the licensee support ftp site.
      Below is a java program Deadlock which subclasses
      java.awt.List and overrides the select(int)
      method to send a propertyChangeEvent
      when the selection changes.
      javac Deadlock.java
      java Deadlock
      It takes a while for the window to appear.
      When it does, start double clicking in the listbox.
      Eventually you'll get a deadlock because
      the Awt-EventQueue-0 thread is running
      a synchronized method (getSelectedIndexes)
      and then the AWT-Window thread calls select(i) which, in this subclass, calls another synchronized method, getSelectedIndex

      This seems related to existing defect
      4059614 but that fix only "fixes" List,
      not subclasses which call synchronized
      methods. It seems the Window thread
      should *not* be calling methods on the List
      which can be overridden.

      I'm not exactly sure why there is a deadlock;
      I guess perhaps the peer or List code is
      also synchronizing on something else,
      not just the List object, otherwise one thread should finish. The WListPeer.java call is
      a call to isSelected(int) which is a native method.


      Here are the relevent threads from our debugger:

      AWT-EventQueue-0
      sun.awt.windows.WListPeer.getSelectedIndexes WListPeer.java(48)
      java.awt.List.getSelectedIndexes: List.java(374)
      Deadlock$1.actinPerformed Deadlock.java(87)
      java.awt.List.processActionEvent List.java(849)
      java.awt.List.processEvent List.java(795)
      java.awt.Component.dispatchEventImpl Component.java(1764)
      java.awt.Component.dispatchEvent Component.java(1704)
      java.awt.EventDispatchThread.run EventDispatchThread.java(63)

      AWT-Window

      java.awt.List.getSelectedIndex List.java(359)
      MyList.select Deadlock.java(10)
      sun.awt.windows.WListPeer.handleAction WListPeer.java(163)
      sun.awt.windows.WToolkit.run WToolkit.java(106)
      java.lang.Thread.run Thread.java(474)

      Here is the source:

      import java.awt.*;
      import java.awt.event.*;
      import java.beans.*;
      import java.util.*;

      class MyList extends List
      {
        public void select(int index )
          {
            if ( getSelectedIndex() != index ) {
              Integer oldSelected = new Integer(getSelectedIndex());
              String oldSelItem = getSelectedItem();
              String[] oldSelItems = null;
              if (getSelectedIndex()!= -1)
                oldSelItems = getSelectedItems();
                        
              int[] oldSelIndexes = getSelectedIndexes();
                                    
              super.select(index);
               
              firePropertyChange( "selectedItem",oldSelItem, getSelectedItem());
              
              if (index != -1)
                firePropertyChange( "selectedItems", oldSelItems, getSelectedItems());
              else
                firePropertyChange( "selectedItems", oldSelItems, null);
                                 
              firePropertyChange( "selectedIndex", oldSelected, new Integer(index) );
              firePropertyChange( "selectedIndexes", oldSelIndexes, getSelectedIndexes());
            } // if
          }

        Vector listeners;
        public synchronized void addPropertyChangeListener(PropertyChangeListener l)
          {
            if (listeners == null)
              listeners = new Vector();
            listeners.addElement(l);
          }

        void firePropertyChange(String name, Object oldVal, Object newVal)
          {
            if (listeners == null)
              return;
            PropertyChangeEvent e = new PropertyChangeEvent(this, name, oldVal, newVal);
            Vector v = (Vector) listeners.clone();
            for (int i = 0; i < v.size(); i++)
              {
                PropertyChangeListener l = (PropertyChangeListener) v.elementAt(i);
                l.propertyChange(e);
              }
          }

      }

      public class Deadlock extends Frame implements PropertyChangeListener
      {
        public static void main(String args[]) {
          new Deadlock();
        }
        
        Deadlock()
          {
            setSize(new Dimension(400,400));
            setLayout(new BorderLayout());
            MyList l1 = new MyList();
            l1.addPropertyChangeListener(this);
            l1.setMultipleMode(true);
            for (int i = 0; i < 256; i++)
              {
                l1.add("Item " + i);
                l1.select(i);
              }
            final Label label = new Label();
            label.setText(System.currentTimeMillis() + " min: xxx, max: xxx");
            add(label, BorderLayout.NORTH);
            l1.addActionListener(new ActionListener()
                                 {
                                   public void actionPerformed(ActionEvent e)
                                     {
                                       MyList l = (MyList) e.getSource();
                                       int min = 256;
                                       int max = 0;
                                       int selections[] = null;
                                       // call a synchronized method lots of times...
                                       for (int j = 0; j < 20; j++)
                                         selections = l.getSelectedIndexes();
                                       for (int k = 0; k < selections.length; k++)
                                         {
                                           if (selections[k] < min)
                                             min = selections[k];
                                           if (selections[k] > max)
                                             max = selections[k];
                                         }
                                       String text = System.currentTimeMillis() + " min: " + min + ", max: " + max;
                                       System.out.println(text);
                                       label.setText(text);
                                     }
                                 });
            add(l1, BorderLayout.CENTER);
            Button done = new Button("Done");
            done.addActionListener(new ActionListener()
                                   {
                                     public void actionPerformed(ActionEvent e)
                                       {
                                         System.exit(0);
                                       }
                                   });
            add(done, BorderLayout.SOUTH);
            setVisible(true);
          }
        public void propertyChange(PropertyChangeEvent e)
          {
          }

      }

      company - SAS Institute Inc. (LICENSEE) , email - ###@###.###

            busersunw Btplusnull User (Inactive)
            duke J. Duke
            Votes:
            0 Vote for this issue
            Watchers:
            0 Start watching this issue

              Created:
              Updated:
              Resolved:
              Imported:
              Indexed: