-
Bug
-
Resolution: Fixed
-
P2
-
1.1.3, 1.1.4
-
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 - ###@###.###
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 - ###@###.###