Bug 4434 - GPF when running SWT Tree code (1FNI25V)
Summary: GPF when running SWT Tree code (1FNI25V)
Status: RESOLVED FIXED
Alias: None
Product: Platform
Classification: Eclipse Project
Component: SWT (show other bugs)
Version: 2.0   Edit
Hardware: All Windows All
: P4 normal (vote)
Target Milestone: 2.1 RC3   Edit
Assignee: Steve Northover CLA
QA Contact:
URL:
Whiteboard:
Keywords:
Depends on:
Blocks:
 
Reported: 2001-10-11 14:16 EDT by Mike Wilson CLA
Modified: 2003-03-12 16:12 EST (History)
1 user (show)

See Also:


Attachments
test case which was causing the stack trace (3.11 KB, text/plain)
2003-03-07 16:33 EST, Christophe Cornu CLA
no flags Details
test case (1.05 KB, text/plain)
2003-03-07 16:34 EST, Christophe Cornu CLA
no flags Details

Note You need to log in before you can comment on or make changes to this bug.
Description Mike Wilson CLA 2001-10-11 14:16:44 EDT
What I did to produce this GPF was set the tree selection (by mistake, bug in my code) to 
	a tree item that the wasn't visible at the time. The hidden tree item existed, but it's parent 
	wasn't expanded. 

	Let me know if you'd like a test case - I'll write one and send it up.

NOTES:

Unhandled exception
Type=GPF vmState=0x00000003
ExceptionCode=0xc0000005 ExceptionAddress=0xbfba9aec ContextFlags=0x0001001f
Handler1=0x10e01566 Handler2=0x100385f0
Module=C:\WINDOWS\SYSTEM\COMCTL32.DLL
Module_base_address=0xbfb90000
Offset_in_DLL=0x00019aec
EDI=0x02ccb3ec ESI=0x73f9810c EAX=0x017f07ac
EBX=0x017f07ac ECX=0x00000000 EDX=0x00020020
EBP=0x0063eed8 ESP=0x0063eebc EIP=0xbfba9aec

Thread: main (priority 5)
0000 com/ibm/swt/win32/OS.CallWindowProc(IIIII)I
0017 com/ibm/swt/widgets/Tree.callWindowProc(III)I
043f com/ibm/swt/widgets/Window.windowProc(III)I
0015 com/ibm/swt/widgets/Display.windowProc(IIII)I
0000 com/ibm/swt/win32/OS.SendMessage(IIII)I
0092 com/ibm/swt/widgets/Tree.setSelection([Lcom/ibm/swt/widgets/TreeItem;)V
00be com/ibm/ive/tools/profiler/ui/methodcalltree/MethodCallTreeViewer.setSelection(Lcom/ibm/ive/tools/profiler/ui/methodcalltree/MethodCallTreeElement;)V
0022 com/ibm/ive/tools/profiler/ui/methodcalltree/MethodCallTreeViewer.domainChanged(Lcom/ibm/jface/elements/DomainEvent;)V
0034 com/ibm/jface/elements/DomainEvent.dispatch(Ljava/lang/Object;)V
000c com/ibm/jface/elements/DefaultDomainModel$1.run()V
000f com/ibm/swt/widgets/RunnableLock.run()V
0022 com/ibm/swt/widgets/Display.runAsyncMessages()Z
0044 com/ibm/swt/widgets/Display.readAndDispatch()Z
0005 com/ibm/jface/util/Utilities.dispatchEventAndSleep(Lcom/ibm/swt/widgets/Display;)V
000a com/ibm/jface/util/Utilities.runEventLoop()V
001c com/ibm/jface/Application.run()V
0063 com/ibm/ive/tools/profiler/ui/presentation/Profiler.main([Ljava/lang/String;)V

SN (12/22/99 11:25:02 AM)
	Please always submit test code.

JH (12/22/99 1:46:08 PM)
	Here's a test case - sorry if it's a little long. I have two classes: TreeGpfTest and TreeNode. 
	TreeGpfTest is mostly scavenged from the SWT examples so it should look somewhat
	familiar. Running TreeGpfTest will bring up a window with a tree and a button at the
	bottom. Clicking the button will attempt to ensure that a tree path is expanded and then 
	set the selection to the last item in the path. If the tree path isn't already expanded, then 
	this should cause the GPF. That is, if you bring up the window and hit the button then you'll
	get the GPF.

	The code actually GPFs in Tree.setSelection() but changing the code in TreeGpfTest.getTestItem() to
	look like:
	
				if (!testItem.getExpanded()) {				
					if (testItem.getItems().length == 1 && testItem.getItems()[0].getText().equals(falseNodeText)) {
						// dispose of dummy items
						TreeItem [] dummyNodes = testItem.getItems ();
						for (int n = 0; n < dummyNodes.length; n++) dummyNodes[n].dispose ();
						expandTree (lastItem);
					} else {
						testItem.setExpanded(true);
					}			
				}

	instead of:

			if (!testItem.getExpanded()) {
				expandTree(testItem);
			}			

	will prevent the GPF.


import com.ibm.swt.SWT;
import com.ibm.swt.widgets.*;
import com.ibm.swt.layout.*;
import com.ibm.swt.graphics.*;
import com.ibm.swt.events.*;

import java.util.*;

public class TreeGpfTest implements TreeListener {

	private Shell shell;
	private Display display;
	protected Button runTestButton;
	private Tree tree;
	private TreeNode root;
	static private final String falseNodeText = "falseNode";
	static private final int treeDepth = 5;
	static private final int childrenPerNode = 5;
	
public static void main(String[] args) {
	new TreeGpfTest().open().run().close();
}


private void runSelectionTest() {
	TreeItem testItem = getTestItem();
	TreeItem[] items = new TreeItem[1];
	items[0] = testItem;
	tree.setSelection(items);
	tree.showItem(testItem);	
}

private TreeItem getTestItem() {
	TreeItem testItem = null;
	TreeItem[] items = tree.getItems();
	for (int i = 0; i < treeDepth; i++) {
		testItem = items[childrenPerNode - 1];
		
		if (!testItem.getExpanded()) {
			expandTree(testItem);
		}
		
		items = testItem.getItems();		
	}
	return testItem;
}

public void expandTree (TreeItem item) {
	TreeNode node = (TreeNode) item.getData();
	Vector children = node.getChildren();
	
	for (int i = 0; i < children.size(); i++) {
		TreeNode next = (TreeNode) children.elementAt(i);
		TreeItem nextItem = new TreeItem(item, SWT.NULL);
		nextItem.setData(next);
		nextItem.setText(next.getName());
		
		if (next.getChildren().size() > 0) {
			TreeItem dummyItem = new TreeItem(nextItem, SWT.NULL);
			dummyItem.setText(falseNodeText);
		}
	}
}

private Shell createShell() {
	Shell shell = new Shell();
	shell.setText("GPF Example");
	shell.setBounds(0, 0, 500, 400);
	shell.setBackground(new Color(255, 255, 255));

	shell.setLayout(new GridLayout());
	addComponents(shell);
	
	return shell;
}


private TreeGpfTest close () {
	if ((shell != null) && (!shell.isDisposed ())) shell.dispose ();
	shell = null;
	return this;
}

private TreeGpfTest open () {
	shell = createShell ();
	display = shell.getDisplay ();
	shell.open ();
	return this;
}

private TreeGpfTest run () {
	while (!shell.isDisposed ()) {
		if (!display.readAndDispatch ()) display.sleep ();
	}
	return this;
}

private void addComponents(Shell shell) {
	tree = new Tree(shell, SWT.NONE);
	tree.addTreeListener(this);
	initializeTreeItems();
	GridData data = new GridData();
	data.horizontalAlignment = GridData.FILL;
	data.verticalAlignment = GridData.FILL;
	data.grabExcessHorizontalSpace = true;
	data.grabExcessVerticalSpace = true;
	tree.setLayoutData(data);

	runTestButton = new Button(shell, SWT.PUSH);
	runTestButton.setText("Run test.");
	runTestButton.addSelectionListener(new SelectionAdapter() {
		public void widgetSelected(SelectionEvent e) {
			runSelectionTest();
			runTestButton.setText("Test Successful.");		
		}
	});	
	
}


private void initializeTreeItems() {
	root = TreeNode.createTree(treeDepth, childrenPerNode);
	Vector children = root.getChildren();
	for (int i = 0; i < children.size(); i ++) {
		TreeNode next = (TreeNode) children.elementAt(i);
		TreeItem item = new TreeItem(tree, 0);
		item.setData(next);
		item.setText(next.getName());
		
		if (next.getChildren().size() > 0) {
			TreeItem fakeItem = new TreeItem (item, SWT.NULL);
			fakeItem.setText(falseNodeText);
		}
	}
}

public void treeCollapsed(TreeEvent event) {
}

/**
 * 
 */
public void treeExpanded(TreeEvent event) {
	TreeItem item = (TreeItem) event.item;
	// If the node hasn't been viewed yet it has a dummy node
	// to allows expansion. Remove the dummy treeItem then 
	// populate the tree
	if (item.getItems ()[0].getText().equals(falseNodeText)) {
		// dispose of dummy items
		TreeItem [] items = item.getItems ();
		for (int i=0; i<items.length; i++) items [i].dispose ();
		
		// expand the tree item
		expandTree (item);
	} 
}

}



/*************************************************************************************************************************
 *
 * TreeNode class 
 *
*************************************************************************************************************************/

import java.util.*;

public class TreeNode {
	Vector children = new Vector();
	String nodeName;

static public TreeNode createTree(int depth, int numChildren) {
	TreeNode rootNode = new TreeNode("Root");
	if (depth > 0) {
		int childDepth = depth - 1;
		for (int i = 0; i < numChildren; i++) {
			TreeNode nextChild = TreeNode.createTree(childDepth, numChildren);
			nextChild.setName("Level " + depth + " child " + i);
			rootNode.addChild(nextChild);
		}
	}
	return rootNode;
}

public TreeNode(String name) {
	super();
	setName(name);
}

public Vector getChildren() {
	return children;
}

public void addChild(TreeNode child) {
	children.addElement(child);
}

public String getName() {
	return nodeName;
}

public void setName(String name) {
	nodeName = name;
}

}

SN (12/23/99 11:27:44 AM)
	Long is not a problem.  Also JH says:

>Yeah, as long as the path I want to expand/select isn't already
>expanded. If you just bring up the window and hit the test
>button it should GP every time.
>

SN (12/23/99 12:11:53 PM)
	Does not GP in VA/Java but throws a null pointer exception.

SN (12/23/99 1:03:42 PM)
	Ok, I know what's happening but I don't understand the failure mode.
	The example code is selecting a node that's not visible.  This causes
	the tree to expand to show the node.  The application's expand callbacks
	delete the children and this deletes the node that the application is trying
	to select.  I don't understand the GP or null pointer exception yet.

SN (1/4/00 5:28:00 PM)
	I hacked a quick fix that avoided the null pointer exception and
	this GP'd after sending the expand callbacks in TVM_SELECTITEM.

	It looks like deleting the item that is about to be selected inside
	of an expand callback (that is sent because the tree must expand
	to show the item about to be selected) causes Windows it GP
	when it finally get's around to selecting the item.

	I have no idea how to work around this.

	McQ (27/11/2000 3:24:15 PM) -
		If this is not going to be fixed, this needs to be documented as a known
		limitation. SN to update javadoc
Comment 1 DJ Houghton CLA 2001-10-24 06:28:31 EDT
PRODUCT VERSION: 991208 Windows VM
Comment 2 Steve Northover CLA 2002-06-07 16:45:44 EDT
This very complicated GP exists in Windows.  I don't know how to work around it 
at this time.  Marking WONTFIX.
Comment 3 Mike Wilson CLA 2002-06-10 10:09:39 EDT
SN to write the documentation that describes the case and include in the 
readme.
Comment 4 Christophe Cornu CLA 2003-03-07 11:14:35 EST
I believe bug 33834 could be a variant of this test case.
We are testing a workaround for this bug 4434 (and we are trying to understand 
the scope under which this problem occurs). This should also provide a fix for bug 
33834.

With a recent version of SWT (>= Eclipse 2.1 RC1), the test case in this PR causes 
the following events.
- within the expand callback, a tree item is disposed
- SWT removes the tree item from the tree table. 
- TVM_DELETEITEM is called to destroy the OS tree item.
The problem is that, for some reason, this TVM_DELETEITEM causes a 
TVN_ITEMEXPANDING resulting in another SWT.Expand event. At this stage,
the OS considers the tree item to still be there (TVM_GETITEM etc return it), but it 
has already been removed from the SWT tree table.
- this causes TreeItem.getItems() to possibly return an array with a null item, 
causing the NPE. (remember: we are all here still before returning from the call to
TVM_DELETEITEM).

One workaround is to detect that case by checking for a null item in 
Tree.wmNotify.TVN_ITEMEXPANDING and in TreeItem.getItems


java.lang.NullPointerException
	at PR4434BIG.treeExpanded(PR4434BIG.java:103)
	at org.eclipse.swt.widgets.TypedListener.handleEvent
(TypedListener.java:173)
	at org.eclipse.swt.widgets.EventTable.sendEvent(EventTable.java:77)
	at org.eclipse.swt.widgets.Widget.sendEvent(Widget.java:836)
	at org.eclipse.swt.widgets.Widget.sendEvent(Widget.java:861)
	at org.eclipse.swt.widgets.Widget.sendEvent(Widget.java:845)
	at org.eclipse.swt.widgets.Tree.wmNotifyChild(Tree.java:1883)
	at org.eclipse.swt.widgets.Control.WM_NOTIFY(Control.java:3810)
	at org.eclipse.swt.widgets.Composite.WM_NOTIFY(Composite.java:638)
	at org.eclipse.swt.widgets.Control.windowProc(Control.java:2810)
	at org.eclipse.swt.widgets.Decorations.windowProc
(Decorations.java:1225)
	at org.eclipse.swt.widgets.Display.windowProc(Display.java:2285)
	at org.eclipse.swt.internal.win32.OS.CallWindowProcW(Native Method)
	at org.eclipse.swt.internal.win32.OS.CallWindowProc(OS.java:1232)
	at org.eclipse.swt.widgets.Tree.callWindowProc(Tree.java:152)
	at org.eclipse.swt.widgets.Control.windowProc(Control.java:2839)
	at org.eclipse.swt.widgets.Display.windowProc(Display.java:2285)
	at org.eclipse.swt.internal.win32.OS.SendMessageW(Native Method)
	at org.eclipse.swt.internal.win32.OS.SendMessage(OS.java:1781)
	at org.eclipse.swt.widgets.Tree.destroyItem(Tree.java:337)
	at org.eclipse.swt.widgets.TreeItem.releaseChild(TreeItem.java:482)
	at org.eclipse.swt.widgets.Widget.dispose(Widget.java:367)
	at PR4434BIG.treeExpanded(PR4434BIG.java:107)
	at org.eclipse.swt.widgets.TypedListener.handleEvent
(TypedListener.java:173)
	at org.eclipse.swt.widgets.EventTable.sendEvent(EventTable.java:77)
	at org.eclipse.swt.widgets.Widget.sendEvent(Widget.java:836)
	at org.eclipse.swt.widgets.Widget.sendEvent(Widget.java:861)
	at org.eclipse.swt.widgets.Widget.sendEvent(Widget.java:845)
	at org.eclipse.swt.widgets.Tree.wmNotifyChild(Tree.java:1883)
	at org.eclipse.swt.widgets.Control.WM_NOTIFY(Control.java:3810)
	at org.eclipse.swt.widgets.Composite.WM_NOTIFY(Composite.java:638)
	at org.eclipse.swt.widgets.Control.windowProc(Control.java:2810)
	at org.eclipse.swt.widgets.Decorations.windowProc
(Decorations.java:1225)
	at org.eclipse.swt.widgets.Display.windowProc(Display.java:2285)
	at org.eclipse.swt.internal.win32.OS.CallWindowProcW(Native Method)
	at org.eclipse.swt.internal.win32.OS.CallWindowProc(OS.java:1232)
	at org.eclipse.swt.widgets.Tree.callWindowProc(Tree.java:152)
	at org.eclipse.swt.widgets.Control.windowProc(Control.java:2839)
	at org.eclipse.swt.widgets.Display.windowProc(Display.java:2285)
	at org.eclipse.swt.internal.win32.OS.SendMessageW(Native Method)
	at org.eclipse.swt.internal.win32.OS.SendMessage(OS.java:1781)
	at org.eclipse.swt.widgets.Tree.setSelection(Tree.java:1013)
	at PR4434BIG$1.widgetSelected(PR4434BIG.java:60)
	at org.eclipse.swt.widgets.TypedListener.handleEvent
(TypedListener.java:87)
	at org.eclipse.swt.widgets.EventTable.sendEvent(EventTable.java:77)
	at org.eclipse.swt.widgets.Widget.sendEvent(Widget.java:836)
	at org.eclipse.swt.widgets.Display.runDeferredEvents(Display.java:1779)
	at org.eclipse.swt.widgets.Display.readAndDispatch(Display.java:1487)
	at PR4434BIG.main(PR4434BIG.java:68)
Exception in thread "main" 

Comment 5 Christophe Cornu CLA 2003-03-07 16:33:17 EST
Created attachment 3920 [details]
test case which was causing the stack trace
Comment 6 Christophe Cornu CLA 2003-03-07 16:34:09 EST
Created attachment 3921 [details]
test case
Comment 7 Christophe Cornu CLA 2003-03-07 16:48:16 EST
Fixed released version v>20030307
Comment 8 Veronika Irvine CLA 2003-03-12 16:12:15 EST
Approved by Mike and Veronika for RC3.