JDK-4777210 : setText causes deadlock in JFormattedTextField, JTextComponent
  • Type: Bug
  • Component: client-libs
  • Sub-Component: javax.swing
  • Affected Version: 1.4.1
  • Priority: P3
  • Status: Closed
  • Resolution: Duplicate
  • OS: windows_xp
  • CPU: x86
  • Submitted: 2002-11-12
  • Updated: 2002-11-13
  • Resolved: 2002-11-13
Related Reports
Duplicate :  
Description

Name: jl125535			Date: 11/11/2002


FULL PRODUCT VERSION :
java version "1.4.1"
Java(TM) 2 Runtime Environment, Standard Edition (build 1.4.1-b21)
Java HotSpot(TM) Client VM (build 1.4.1-b21, mixed mode)

FULL OPERATING SYSTEM VERSION :
Microsoft Windows XP [Version 5.1.2600]

ADDITIONAL OPERATING SYSTEMS : Windows 2000 sp2


A DESCRIPTION OF THE PROBLEM :
Under certain conditions, the setText(String) method of
JTextComponent/JFormattedTextField does not appear to be
thread safe in spite of JDK documentation indicating that
it is.  Deadlock can occur at seemingly random times.

Because this is a multithreaded, timing dependent, issue
different hardware platforms may experience different
results.


STEPS TO FOLLOW TO REPRODUCE THE PROBLEM :
1. Compile and run the demo application.
2. Click the test button.  The application may lock
immediately or it may lock on a subsequent click.
3. If it does not lock, you can also try changing the
timing of the Thread.sleep() methods and/or clicking
multiple times in succession.

EXPECTED VERSUS ACTUAL BEHAVIOR :
The application should not lock up.

ERROR MESSAGES/STACK TRACES THAT OCCUR :
There are no error messages.

REPRODUCIBILITY :
This bug can be reproduced often.

---------- BEGIN SOURCE ----------
/*	This class demonstrates that a JFormattedTextField's inherited setText method is not thread-safe.  It is also conceivable	that JTextComponent's setText method 	will not be thread-safe even when it is not being
used as the super class of a JFormattedTextField.
	
This class demonstrates thread deadlock occuring when a background thread is attempting to update the JFormattedTextField while the dispatch thread is
painting.  The deadlock evidently arises because the painting thread has acquired the AWTTreeLock before entering AbstractDocument.readLock().  The painting thread waits forever in readLock so that the background thread can finish editing.  However, the background thread may call TextComponent.fireCaretUpdate which causes it to hang in subsequent method
Component.AccessibleAWTComponent.getLocationOnScreen() because the latter needs to synchronize on the AWTTreeLock.
*/
	
import javax.swing.*;
import javax.swing.text.*;
import javax.accessibility.AccessibleContext;

import java.awt.event.*;
import java.awt.*;


public class FormattedTextTest extends JFrame {
	
	private AccessibleContext ac;
	private JFormattedTextField fld;
	private JPanel basePanel;
	
	private String[] values = new String[]{
		"one, one",
		"two, two",
		"three, three",
		"four, four"};

	
	
    public static final void main (String[] args){
        FormattedTextTest t = new FormattedTextTest();
        t.show();
    }
    
	/*  Construct a simple dialog to use for a test
		platform.	*/
	public FormattedTextTest(){
		super();
		setDefaultCloseOperation(DISPOSE_ON_CLOSE);
		
		JButton testButton = new JButton("Test");
		testButton.addActionListener( new ActionListener(){
			public void actionPerformed(ActionEvent e){
				test();
			}
		});

		fld = new JFormattedTextField();
		fld.setPreferredSize(new Dimension(40, 20));
		
		/*	Similar code is called from BasicComboBoxUI in
			the original application code.  It
			causes CaretListeners to be registered.	*/
		ac = fld.getAccessibleContext();

		/*	Installing our own formatter seems to be
			important in this scenario.
			JFormattedTextField may take a different path
			through the code when a formatter has been
			installed.
			*/
		DefaultFormatter formatter = new DefaultFormatter();
        DefaultFormatterFactory dff = new DefaultFormatterFactory(formatter);
        fld.setFormatterFactory(dff);

		basePanel = new JPanel();
		basePanel.add(testButton);
		basePanel.add(fld);

		getContentPane().add(basePanel);
		pack();
	}
	
	
	/*	Launch a background thread to repeatedly modify
		the field.  Then use the current (dispatch) thread
		to repeatedly paint the field.	*/
	private void test(){
		Thread thread = new Thread(new ValueSetter());
		thread.start();
		
		/*  Deadlock occurs when the edit thread and
			the paint thread are in just the right phase
			of execution.  A varying timing loop on both
			threads helps to increase the chances of this
			occurring.  If it doesn't occur on your
			machine, try adding more loops here
			and/or clicking the button multiple times so that
			multiple background threads will be updating
			the field simultaneously.	*/
        // Invoking revalidate() and paintImmediately() from the event
        // dispatching thread should be safe.
		for (int n = 0; n < 40; n ++){
			basePanel.revalidate();
			basePanel.paintImmediately(basePanel.getBounds());
			try{
				Thread.sleep(n);
			}
			catch (InterruptedException e){}
	
		}
	}
	
	private class ValueSetter implements Runnable{
		public void run(){
			for (int i = 0; i < 10; i++){
				for (int j = 0; j < values.length; j++){
					
					//this causes deadlock
					fld.setText(values[j]);
					
					//this alternative appears to be safe
					//fld.setValue(values[j]);
					
					try{
						Thread.sleep(i  + j);
					}
					catch (InterruptedException e){}
				}
			}
		}
	}
}
---------- END SOURCE ----------

CUSTOMER WORKAROUND :
These *appear* to work but haven't been thoroughly tested.
1.  Don't install Formatter and FormatterFactory.
2.  Use the setValue method instead of the setText method.
(Review ID: 166755) 
======================================================================