JDK-4623377 : REGRESSION: JOptionPane hangs resulting in 100% of cpu usage(when DND)
  • Type: Bug
  • Component: client-libs
  • Sub-Component: java.awt
  • Affected Version: 1.4.0
  • Priority: P4
  • Status: Closed
  • Resolution: Fixed
  • OS: windows_nt
  • CPU: x86
  • Submitted: 2002-01-14
  • Updated: 2002-05-20
  • Resolved: 2002-04-25
The Version table provides details related to the release that this issue/RFE will be addressed.

Unresolved : Release in which this issue/RFE will be addressed.
Resolved: Release in which this issue/RFE has been resolved.
Fixed : Release in which this issue/RFE has been fixed. The release containing this fix may be available for download as an Early Access Release or a General Availability Release.

To download the current JDK release, click here.
Other
1.4.1 hopperFixed
Related Reports
Relates :  
Relates :  
Description

Name: rmT116609			Date: 01/14/2002


FULL PRODUCT VERSION :
Java(TM) 2 Runtime Environment, Standard Edition (build 1.4.0-beta3-b84)
Java HotSpot(TM) Client VM (build 1.4.0-beta3-b84, mixed mode)


DESCRIPTION OF THE PROBLEM :

In Jdk1.3.1 I was able to show an option pane in the drop() method (of DropTarget Listener)
and ask for the confirmation from the user before moving the data. There seems to be a 
problem in jdk1.4 , the option pane popups up but then the program goes into an infinite 
loop (somewhere in the run method of 'sun.awt.windows.WiToolKit') and the user cannot i
interact with the option pane..(the java application uses 100% of the cpu till the application 
is terminated by doing an end task) The problem has been introduced in 1.4 and I think the
problem is in the Windows toolkit .I tested it under java 1.4 in Linux and linux works correctly

REGRESSION.  Last worked in version 1.3.1, 1.3.1_02.

The problem is reproducible on Windows 2000, Windows NT 4.0 using 1.4.0-beta3, 1.4.0-rc.

The probelm is not reproducible on Solaris 2.8, Linux Redhat 6.1 using 1.4.0-beta3.

STEPS TO FOLLOW TO REPRODUCE THE PROBLEM :
1) compile and execute the provided source code .
2) When the frame popups click 1 node child node and drag it another child node
3) then an option pane(Confirm Dialog)  pops up. Try to click on any of the buttons on the dialog.



EXPECTED VERSUS ACTUAL BEHAVIOR :
Expected -  The user should be able to click on any of the buttons of the Confirm Dialog .
Actual - The user cannot click on any of the buttons of the Option Pane, the program is going into an infinite loop (see the cpu usage)

This bug can be reproduced always.

---------- BEGIN SOURCE ----------
import javax.swing.tree.*;
import javax.swing.*;
import java.awt.dnd.*;
import java.awt.*;
import java.awt.datatransfer.*;

public class Test extends JFrame implements DragGestureListener,DropTargetListener, DragSourceListener {

    private DefaultTreeModel treeModel;

    private JTree tree;

    private DragSource ds = new DragSource();

    public Test() {
        super("Test");
        buildTree();
        getContentPane().add(new JScrollPane(tree));
        setSize(500,500);
        show();
        setDefaultCloseOperation(EXIT_ON_CLOSE);
    }

    public static void main(String[] args) {
        new Test();
    }

    private void buildTree() {
        TransferTreeNode rootNode = new TransferTreeNode("Root");

        rootNode.add(new TransferTreeNode("First"));
        rootNode.add(new TransferTreeNode("Second"));
        rootNode.add(new TransferTreeNode("Third"));

       	treeModel  = new DefaultTreeModel(rootNode);
                           tree       = new JTree(treeModel);

		tree.getSelectionModel().setSelectionMode(TreeSelectionModel.SINGLE_TREE_SELECTION);   						
   		ds.createDefaultDragGestureRecognizer(tree, DnDConstants.ACTION_MOVE,this);
    	new DropTarget(tree,this);
    }

/******************************************************************************/
/************************* Drag And Drop Support methods **********************/
/******************************************************************************/

    public void dragGestureRecognized(DragGestureEvent e) {

        if (e.getDragAction() != DnDConstants.ACTION_MOVE) {
            return;
        }

        Point p            = e.getDragOrigin();
		TreePath dragPath = tree.getPathForLocation(p.x,p.y);

        if (dragPath != null) {
            TransferTreeNode treeNode = (TransferTreeNode) dragPath.getLastPathComponent();
			e.startDrag(DragSource.DefaultMoveDrop,treeNode,this);
        }

    }

    public void dragDropEnd(DragSourceDropEvent e) {
    }

    public void dragEnter(DragSourceDragEvent e) {
    }

    public void dragExit(DragSourceEvent e) {
    }

    public void dragOver(DragSourceDragEvent e) {
    }

    public void dropActionChanged(DragSourceDragEvent e) {
    }

    public void dragEnter(DropTargetDragEvent e) {
    }

    public void dragExit(DropTargetEvent e) {
    }

    public void dragOver(DropTargetDragEvent e) {
    }

    public void dropActionChanged(DropTargetDragEvent e) {
    }

    public void drop(DropTargetDropEvent e) {

	       Transferable transferable = e.getTransferable();
        int option = JOptionPane.showConfirmDialog(this,"Confirm Move","Confirm Move", 	JOptionPane.YES_NO_OPTION);
        e.dropComplete(true);
   	}

}

	/** The Transferable Object */
	class TransferTreeNode extends DefaultMutableTreeNode implements Transferable {

	    public static final DataFlavor dataFlavor =
        		new DataFlavor(DataFlavor.javaJVMLocalObjectMimeType,"Test");
    	private static final DataFlavor[] dataFlavors = {dataFlavor};

        public TransferTreeNode(String string) {
            super(string);
        }

	    public Object getTransferData(DataFlavor df) {
    	    try {
	    	    if(df.equals(dataFlavor)) {
    	    	    return this;
	        	}
		        else {
    		        throw new UnsupportedFlavorException(df);
        		}
	        }
    	    catch (Exception ex) {
        	    KKClientUtilities.processException(ex);
	        }
    	    return null;
	    }

	    public DataFlavor[] getTransferDataFlavors() {
    	    return   dataFlavors;
	    }

    	public final boolean isDataFlavorSupported(DataFlavor df) {
        	return df.equals(dataFlavor);
	    }
    }


---------- END SOURCE ----------
(Review ID: 138250) 
======================================================================

Comments
CONVERTED DATA BugTraq+ Release Management Values COMMIT TO FIX: hopper FIXED IN: hopper INTEGRATED IN: hopper VERIFIED IN: hopper
14-06-2004

SUGGESTED FIX Name: dsR10078 Date: 04/08/2002 The solution is for the nested loop to process only WM_PAINT, WM_QUIT and WM_AWT_INVOKE_METHOD while a drag operation is in progress, and to process all events while the drop is processed (after the drag is completed). This way we won't reintroduce 4445747, since it manifests only during drag, and the deadlock described in the evaluation will be resolved. Investigation shows that another deadlock is still possible. The deadlock is as follows: a Java application processes drop and the toolkit thread executes the nested message loop. The nested message loop processes a message that starts a modal loop (e.g. a user drags a window title bar). In this case the current implementation is to send WM_CANCELMODE to exit the modal loop and then to post WM_QUIT to exit the nested message loop. However it appears that the modal loop may not exit immediatelly after it processes WM_CANCELMODE and in some situations it can get WM_QUIT from the queue. The modal loop discards WM_QUIT and so we fail to exit the nested message loop. At this point the drop target application seems to operate normally, but the drag source application is blocked as drag-and-drop never completes. To resolve this problem we use AwtToolkit member variables instead of WM_QUIT to exit the nested message loop. Additionally, the fix cleans up exception handling in WDragSourceContextPeer.startDrag(). The two problems here is that it prints a stack trace to the console and doesn't communicate the original exception message. ###@###.### 2002-04-08 ======================================================================
08-04-2002

EVALUATION Should fix in Hopper (infinite loop). ###@###.### 2002-01-14 Name: dsR10078 Date: 02/13/2002 When the drop happens, the drop target associated with the Java component receives drop notification from the native DnD system (OLE DnD on Win32). This notification is received and processed on the toolkit thread (AWT-Windows). To process the notification the drop target should notify the drop target listener registered with it and communicate the listener's responce (whether the drop is accepted or rejected and if accepted whether the drop transfer succeeded or failed) back to the native DnD system that will pass it to the drag source application. For security reasons the drop target listener should be notified on the thread that belongs to the AppContext of the component associated with the drop target. The current implementation is to post an ActiveEvent that notifies the drop target listener to the event queue of that AppContext. This way the listener is notified on the event dispatch thread of the AppContext (AWT-EventQueue-0). Since we need to get the listener's responce, the toolkit thread should wait until the ActiveEvent is processed. This desing introduced several deadlocks with similar scenario: while the toolkit thread is blocked waiting for the event dispatch thread, the event dispatch thread calls SendMessage on one of Java windows. These deadlocks were addressed with the fix for 4269666. The solution was to execute a nested message loop on the toolkit thread while it is waiting for the event dispatch thread to process some java event and terminate the nested loop after the java event is processed. However, even with this approach deadlocks during dnd were still possible. The problem is that the default processing for some window messages initiates a modal loop. For example, if the user click the window's title bar. If the nested message loop processes such message it runs into a modal loop. In this case an attempt to terminate the nested loop fails. To successfully terminate nested loops we should first cancel any possible modal loop. The implementation was to enumerate all windows created on the toolkit thread and send WM_CANCELMODE to each window. This way it worked fine, but for some reason EnumThreadWindows() is very slow during dnd on Win9X/ME. This caused severe performance degradation documented under bug id 4445747. Finally, the solution was to modify the nested message loop so that it peeks only selected messages. The current implementation is to process only WM_PAINT, WM_QUIT and one private message. All other messages are postponed until the toolkit thread quits from the nested loop to the standard message loop. The deadlock scenario is as follows: The test case calls JOptionPane.showConfirmDialog() in the drop() method of DropTargetListener. JOptionPane.showConfirmDialog() doesn't return until a modal dialog is closed. DropTargetListener.drop() doesn't complete until JOptionPane.showConfirmDialog() returns. The toolkit thread doesn't return from the nested loop until DropTargetListener.drop() completes. Mouse and keyboard messages are not processed until the toolkit thread returns from the nested loop. The modal dialog is not closed until mouse or keyboard messages are processed. As a result both the toolkit and event dispatch thread are blocked forever. ###@###.### 2002-02-13 ======================================================================
13-02-2002