Some Eclipse Foundation services are deprecated, or will be soon. Please ensure you've read this important communication.
Bug 119207 - TableItem errors after/during dispose
Summary: TableItem errors after/during dispose
Status: RESOLVED FIXED
Alias: None
Product: Platform
Classification: Eclipse Project
Component: SWT (show other bugs)
Version: 3.2   Edit
Hardware: PC Windows 2000
: P3 normal (vote)
Target Milestone: ---   Edit
Assignee: Steve Northover CLA
QA Contact:
URL:
Whiteboard:
Keywords:
Depends on:
Blocks:
 
Reported: 2005-12-05 01:01 EST by ArronM CLA
Modified: 2006-01-06 12:22 EST (History)
0 users

See Also:


Attachments

Note You need to log in before you can comment on or make changes to this bug.
Description ArronM CLA 2005-12-05 01:01:59 EST
On SWT 3212, the dispose state flag is not getting set soon enough for TableItem while the Table is virtual.  TableItem.destroyWidget() calls Table.destroyItem() before TableItem.releaseHandle(), which sends an OS message to delete the item.  In some cases, the OS will call Table back immediately, requesting view data for a new table row.  SetData will be triggered, and that almost-removed TableItem will still return that it's not disposed.  Calling a method on it will result in an error, since parts of the object have indeed been destoyed.

Here's a snippet which shows the problem:

public static void main(String[] args) {
  System.out.println("SWT Version: " + SWT.getVersion());
  Display display = new Display();

  final Shell shell = new Shell(display);
  shell.setLayout(new FillLayout());

  final Table table = new Table(shell, SWT.BORDER | SWT.VIRTUAL);
  table.addListener(SWT.SetData, new Listener() {
    public void handleEvent(Event event) {
      try {
        final TableItem item = (TableItem) event.item;
        System.err.println("SetData for row " + table.indexOf(item));
        item.setText("Row");

        TableItem firstItem = table.getItem(0);
        if (!firstItem.isDisposed()) {
          System.err.println("Row #0 says it's not disposed");
          firstItem.setData("Key4", "4");
        }
      } catch (Exception e) {
        e.printStackTrace();
      }
    }
  });

  for (int i = 0; i < 2; i++) {
    TableItem tableItem = new TableItem(table, SWT.NONE);
    tableItem.setData("Key1", "1");
  }

  
  shell.setSize(400, shell.getMinimumSize().y + table.getItemHeight());
  shell.open();
  
  TableItem item = table.getItem(0);
  System.err.println("About to Dispose Row 0..");
  item.dispose();

  while (!shell.isDisposed()) {
    if (!display.readAndDispatch())
      display.sleep();
  }
  display.dispose();
}

The output of this snippet is:
SWT Version: 3212
SetData for row 0
Row #0 says it's not disposed
About to Dispose Row 0..
SetData for row 1
Row #0 says it's not disposed
java.lang.NullPointerException
  at org.eclipse.swt.widgets.Widget.setData(Widget.java:1023)
  at Test$1.handleEvent(Test.java:43)
  at org.eclipse.swt.widgets.EventTable.sendEvent(EventTable.java:66)
  at org.eclipse.swt.widgets.Widget.sendEvent(Widget.java:896)
  at org.eclipse.swt.widgets.Widget.sendEvent(Widget.java:920)
  at org.eclipse.swt.widgets.Widget.sendEvent(Widget.java:905)
  at org.eclipse.swt.widgets.Table.checkData(Table.java:341)
  at org.eclipse.swt.widgets.Table.wmNotifyChild(Table.java:4165)
  at org.eclipse.swt.widgets.Control.WM_NOTIFY(Control.java:3689)
  at org.eclipse.swt.widgets.Composite.WM_NOTIFY(Composite.java:1090)
  at org.eclipse.swt.widgets.Control.windowProc(Control.java:3194)
  at org.eclipse.swt.widgets.Decorations.windowProc(Decorations.java:1538)
  at org.eclipse.swt.widgets.Display.windowProc(Display.java:3908)
  at org.eclipse.swt.internal.win32.OS.CallWindowProcW(Native Method)
  at org.eclipse.swt.internal.win32.OS.CallWindowProc(OS.java:1627)
  at org.eclipse.swt.widgets.Table.callWindowProc(Table.java:256)
  at org.eclipse.swt.widgets.Table.callWindowProc(Table.java:180)
  at org.eclipse.swt.widgets.Control.windowProc(Control.java:3229)
  at org.eclipse.swt.widgets.Table.windowProc(Table.java:3633)
  at org.eclipse.swt.widgets.Display.windowProc(Display.java:3908)
  at org.eclipse.swt.internal.win32.OS.SendMessageW(Native Method)
  at org.eclipse.swt.internal.win32.OS.SendMessage(OS.java:2285)
  at org.eclipse.swt.widgets.Table.destroyItem(Table.java:1314)
  at org.eclipse.swt.widgets.TableItem.destroyWidget(TableItem.java:137)
  at org.eclipse.swt.widgets.Widget.release(Widget.java:730)
  at org.eclipse.swt.widgets.Widget.dispose(Widget.java:400)
  at Test.main(Test.java:62)
Comment 1 Steve Northover CLA 2005-12-06 15:40:11 EST
The problem is that the item cannot be marked as disposed until "item.dispose()" returns.  However, during the dispose, the table is asked to redraw, the virtual callback is triggered and the application code asks for the item ("table.getItem(0)" inside SWT.SetData and gets the item that is in the process of being disposed.

Reordering the code in TableItem.destroyWidget() to save away the fields that are needed to dispose the widget in the operating system (in this case the parent), fixes the problem:

void destroyWidget () {
	Table parent = this.parent;
	releaseHandle ();
	parent.destroyItem (this);
}
Comment 2 Steve Northover CLA 2005-12-06 15:42:03 EST
Need to investigate this pattern for every Item subclass on every platform.  For example, saving away the parent won't work on GTK because "item.handle" is needed in Table.destroyItem().
Comment 3 Steve Northover CLA 2006-01-06 12:22:21 EST
Fixed > 20060106