JDK-4063380 : AWT Problem with dispose on windows
  • Type: Bug
  • Component: client-libs
  • Sub-Component: java.awt
  • Affected Version: 1.1,1.1.5
  • Priority: P4
  • Status: Closed
  • Resolution: Fixed
  • OS: windows_95
  • CPU: generic
  • Submitted: 1997-07-08
  • Updated: 1999-01-15
  • Resolved: 1999-01-15
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 Other
1.1.7 b01Fixed 1.2.0Fixed
Related Reports
Relates :  
Relates :  
Description
	The following program produces a crash when the detach
	and reattach button are pressed repeatedly.
	
	Running on a Pentium 166MHz PC with 32 Mb or RAM.
	
	
	
	import java.awt.*;
	import java.awt.event.*;
	
	/**
	 * @author	Michael Averbukh
	 */
	public
	class CBWindow 
		extends Frame 
	{
		public
		CBWindow()
		{	super.setTitle("main window");
			setLayout( new BorderLayout() );
			addWindowListener( new WindowAdapt() );
			setBounds( 100 , 100, 400, 300 );
			createPanel();
		}
	
		class WindowAdapt
			extends WindowAdapter
		{
			public void
			windowClosing( WindowEvent event )
			{	System.exit(0);
			}
		}
	
		public void
		createPanel()
		{	cbpanel = new CBPanel( this );
			add( "Center", cbpanel );
			setVisible( true );
		}
	
		public synchronized void
		createFrame()
		{	remove( cbpanel );
			cbframe = new CBFrame( this );
			cbframe.setVisible( true );
		}
	
		private CBPanel		cbpanel;
		private CBFrame		cbframe;
	
		public static void
		main( String[] args )
		{	new CBWindow();
		}
	}
	
	class CBPanel
		extends Panel
		implements ActionListener
	{
		CBPanel( CBWindow parent )
		{	this.parent = parent;
			setLayout( new BorderLayout(10,10) );
	
			detachButton = new Button( "Detach Window" );
			detachButton.addActionListener( this );
			Label label = new Label("Panel");
			label.setAlignment( Label.CENTER );
	
			Panel headerPanel = new Panel();
			headerPanel.setLayout( new BorderLayout() );
			headerPanel.add( "West", detachButton );
			headerPanel.add( "Center", label );
			add( "North", headerPanel );
		}
	
		public void
		actionPerformed( ActionEvent event )
		{	Object source = event.getSource();
			if ( source == detachButton )
			{	parent.createFrame();
			}
		}
	
		private Button			detachButton;
		private CBWindow		parent;
	}
	
	class CBFrame
		extends Frame
		implements ActionListener
	{
		CBFrame( CBWindow parent )
		{	super.setTitle("Frame");
			this.parent = parent;
			setLayout( new BorderLayout(10,10) );
	
			reattachButton = new Button( "Reattach Window" );
			reattachButton.addActionListener( this );
			Label label = new Label("Frame");
			label.setAlignment( Label.CENTER );
	
			Panel headerPanel = new Panel();
			headerPanel.setLayout( new BorderLayout() );
			headerPanel.add( "West", reattachButton );
			headerPanel.add( "Center", label );
			add( "North", headerPanel );
	
			addWindowListener( new WindowAdapt() );
			setBounds( 100 , 100, 300, 200 );
		}
	
		class WindowAdapt
			extends WindowAdapter
		{
			public void
			windowClosing( WindowEvent event )
			{	setVisible( false );
	                        hide();
				System.out.println( "WindowAdapt(): Exiting Frame" );
			}
		}
	
		public void
		actionPerformed( ActionEvent event )
		{	Object source = event.getSource();
			if ( source == reattachButton )
			{	parent.createPanel();
				setVisible( false );
	                        hide();
				System.out.println( "actionPerformed(): Exiting Frame" );
			}
		}
	
	        public synchronized void
	        finalize()
	        {
	                System.out.println("CAlling dispose from finalizer");
	                dispose();
	        }
	
		private Button			reattachButton;
		private CBWindow		parent;
	}
>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>


After further investigating into this issue, I believe that this problem is not with dispose... Actually dispose() in the above code never gets executed. It is also not with the hide() that gets called/executed in the actionPerformed event loop, as shown above in the code snippet. 

There is the problem with calling the method remove() within AWT enviornment... the method remove(cbpanel) that gets called in createFrame() of the above code snippet, causes lock/crash. 

The symptom of the problem is that AWT causes GPF (General Protection Fault) as page fault, with the alert dialog displayed "The program has performed an illegal operation and will be shut down" (that sometimes requires rebooting of the system, mostly ctrl C will let you exit out of the app).
 
The above code was tested by replacing hide() with dispose() (in the action event loop) and the code still fails at remove(cbpanel). You may put the debug strings before and after calling remove(cbpanel) and witness the problem behaviour.

sandhya.vora@Eng 1997-07-10
sandhya.vora@Eng 1997-07-10

Comments
CONVERTED DATA BugTraq+ Release Management Values COMMIT TO FIX: 1.1.7 1.2beta4 FIXED IN: 1.1.7 1.2beta4 INTEGRATED IN: 1.1.7 1.2beta4
14-06-2004

EVALUATION ralph.kar@Eng 1998-02-25 Here is what we know so far: - In the original testcase it seemed to be somewhat unclear of where the problem is located. The new testcase does not use panels, remove(), etc. anymore. Instead I simply use a button that I put into each window. I have the ability to use different layout and I can also have this testcase run automatically by posting action events in the event queue. With that I can still reproduce the original problem. I tried different layout managers, different modes (manual and automatic), different click speeds and elimination of an overlap area. - It is not a timing issue. I went through the testcase really slow (waiting 2 seconds between each click) and the problem still shows up. - There is never a constant click count for the problem to appear. Sometimes it takes me 200 clicks and sometimes it appears within the first 10 clicks. - After the problem showed up for the first time, the Windows95 subsystem seems to become unstable. This requires frequent reboots. - It might be an overlap problem. I placed the 2 frames next to each other (vertically) and the problem still shows up. However I noticed that whenever the child frame is created the contour of the original position of the parent appears for a flicker. After I moved the parent completely out of its original coordinate area, I have no longer been able to reproduce the problem. - It is somehow related to native events. When I try to emulate the mouse clicks by posting action events into the event queue the problem does initially not show up. When I have it run for a longer time I eventually get a NullPointerException. Now comes the interesting part: When I run the automatic testcase and move the mouse pointer into the overlap area, the problem shows up within the next few seconds or so. This makes me believe that we are dealing with a redraw problem. However in automatic mode it takes way more events in order to fail, whereas in manual mode I can reproduce the problem sometimes very fast. - The problem with the vanishing memory resource is related to not calling dispose() on the frame. Calling dispose() after setVisible(false) will keep the memory available (BTW: this is a common mistake in AWT coding). However, the dispose() issue is unrelated to the bug. Here is my new testcase: import java.awt.*; import java.awt.event.*; public class TestApp extends Frame implements WindowListener, ActionListener { private static long clicks = 0; private static boolean border = false; private static boolean runnit = false; private TestApp parent = null; private TestApp child = null; private Label label = null; private Button button = null; public TestApp(String text, TestApp par, boolean border) { this(text, border); parent = par; } public TestApp(String text, boolean border) { super("TestApp 4063380"); if (border == true) { this.setLayout(new BorderLayout()); } else { this.setLayout(new FlowLayout(FlowLayout.LEFT)); } button = new Button("Action"); button.addActionListener(this); if (border == true) { this.add(button, "Center"); } else { this.add(button); } label = new Label(text, Label.CENTER); if (border == true) { this.add(label, "South"); } else { this.add(label); } this.addWindowListener(this); this.setBounds(50, 50, 300, 200); this.setVisible(true); } private void runIt() { Toolkit tk = Toolkit.getDefaultToolkit(); EventQueue eq = tk.getSystemEventQueue(); while (true) { if (child == null) { // Send event to the parent eq.postEvent(new ActionEvent(button, ActionEvent.ACTION_PERFORMED, "")); } else { // Send event to the child eq.postEvent( new ActionEvent(child.button, ActionEvent.ACTION_PERFORMED, "")); } try { Thread.sleep(20); } catch (InterruptedException ex) { ex.printStackTrace(); } } } public void actionPerformed(ActionEvent e) { if ((e.getSource() == button) && (parent == null) && (child == null)) { // Handle the button from the parent clicks++; System.out.println("" + clicks + " clicks"); child = new TestApp("Child", this, border); child.setBounds(50, 50, 200, 100); } else if ((e.getSource() == button) && (parent != null) && (child == null)) { // Handle the button from the child clicks++; System.out.println("" + clicks + " clicks"); this.setVisible(false); parent.child = null; this.dispose(); } } public void windowOpened(WindowEvent e) {} public void windowClosed(WindowEvent e) {} public void windowIconified(WindowEvent e) {} public void windowDeiconified(WindowEvent e) {} public void windowActivated(WindowEvent e) {} public void windowDeactivated(WindowEvent e) {} public void windowClosing(WindowEvent e) { System.exit(0); } static public void main(String args[]) { TestApp test = null; for (int i=0; i<args.length; i++) { if (args[i].equalsIgnoreCase("-auto")) { runnit = true; } else if (args[i].equalsIgnoreCase("-border")) { border = true; } } test = new TestApp("Parent", border); if (runnit == true) { test.runIt(); } } } --------------------------------------------------- mick.fleming@Ireland 1998-02-26 I ran the above and got the the same stack trace as the previous testcase : KERNEL32! bff9a07c() KERNEL32! bff7989d() AwtComponent::SendMouseEvent(long 505, __int64 888490971050, long 30, long 16, long 0, long 0, long 0, tagMSG * 0x00000000 {msg=0x00700465 wp=0x04f70016 lp=0x00700465}) line 1697 AwtComponent::WmMouseExit(unsigned int 0, int 30, int 16) line 1045 AwtComponent::WindowProc(unsigned int 1129, unsigned int 0, long 1048606) line 615 + 38 bytes AwtComponent::WndProc(void * 0x00000ba0, unsigned int 1129, unsigned int 0, long 1048606) line 161 + 23 bytes KERNEL32! bff93d4b() AwtToolkit::PreProcessMouseMsg(AwtComponent * 0x007692c0, tagMSG & {msg=0x00000200 wp=0x00000000 lp=0x00100020}) line 558 AwtToolkit::PreProcessMsg(tagMSG & {msg=0x00000200 wp=0x00000000 lp=0x00100020}) line 509 + 16 bytes AwtToolkit::GetMessageFilter(int 0, unsigned int 1, long 34337842) line 387 + 16 bytes KERNEL32! bff7241d() ---------------------------------------------------- ralph.kar@Eng 1998-02-26 I tried the fix that was suggested in 4051487 (Sleep(10);) and it makes the problem disappear. However, this is considered extremely bad programming practice and I am sure that the AWT team will not approve such a change, but it might be useful for IBM as a temporary workaround. Meanwhile it is certain that the problem exists because a mouse exit event is being send to a window that has been already disposed. We will have to determine at what point and how to establish a lock or a mechanism that flushes the event queue. ------------------------------------------------- After talking with Robert Bruce and digging into the code myself, I changed the native ...__dispose() method in awt_Component.cpp to post a message to the Windows event queue, and the object deletion is handled there. This avoids the race condition entirely. On another note, the test case only calls dispose() during the finalize() method called by the GC. On Win95 anyway, the GC didn't run often enough for this to work effectively. The result was that the system ran out of window handles. When I changed the test case to call dispose() on the Frames it wanted gone, I was able to go about 1500 mouse clicks with the test case before my mouse finger got tired. :-) fred.ecks@Eng 1998-03-01
01-03-1998

PUBLIC COMMENTS ralph.kar@Eng 1998-02-26 The problem of the bug seems to be related to 4051487.
26-02-1998