United StatesChange Country, Oracle Worldwide Web Sites Communities I am a... I want to...
Bug ID: JDK-4713003 1.4.1 REGRESSION: Windows XP freezes on Java application exit/draw
JDK-4713003 : 1.4.1 REGRESSION: Windows XP freezes on Java application exit/draw

Details
Type:
Bug
Submit Date:
2002-07-10
Status:
Resolved
Updated Date:
2002-12-06
Project Name:
JDK
Resolved Date:
2002-11-12
Component:
client-libs
OS:
windows_98,windows_xp,windows_2000
Sub-Component:
2d
CPU:
x86
Priority:
P3
Resolution:
Fixed
Affected Versions:
1.2.0,1.4.1,1.4.1_01
Fixed Versions:
1.4.1_02 (02)

Related Reports
Backport:
Duplicate:
Duplicate:
Duplicate:
Duplicate:
Duplicate:
Duplicate:
Relates:

Sub Tasks

Description

Name: jk109818			Date: 07/10/2002


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

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

EXTRA RELEVANT SYSTEM CONFIGURATION :
ATI Radeon AGP graphics card 7200
Driver: ati2dvag.dll ver: 6.13.10.6071 (English)
DirectX 8.1 (4.08.01.0810)

A DESCRIPTION OF THE PROBLEM :
Windows XP freezes (hangs) on Java application exit. I
experience this consistantly on all applications that take
advantage of image drawing functions. I have not been able
to reproduce on other Java applications. Application runs
fine but Windows XP freezes indefinitely on exit. Once
while running an application using drawImage() to a panel
I entered a Windows blue screen complaining of an infinite
loop in the video driver. Sometimes the program will exit
normally if you simply start and exit with small drawing
activity, but a reasonable amount of image drawing
activity seems to cause Windows XP to hang on exit. While
difficult to pinpoint (since you are left with no errors
while your machine reboots), it is probably due to a new
incompatibility between the java runtime and video driver.
I do not have this problem with any JDK previous to the
1.4.1 beta release.

REGRESSION.  Last worked in version 1.4

STEPS TO FOLLOW TO REPRODUCE THE PROBLEM :
1. Compile and run attached program (or any other java
application with sufficient image placement activity).
2. Select "Open Image..." from the File menu and select a
*.jpg format image that would fit onscreen at full size.
For example, a 640 x 480 image is fine.
3. Click on different locations of the loaded image to
scramble the 'pieces', observing that the draw
functionality is working. Scramble the image pieces, as
this seems to be the source of the freeze on application
exit.
4. Exit the application and watch Windows freeze.

EXPECTED VERSUS ACTUAL BEHAVIOR :
Java runtime should gracefully exit, as it almost always
has on previous releases of the JVM. Instead, Java appears
to be the cause of Windows freezing on exit.

ERROR MESSAGES/STACK TRACES THAT OCCUR :
There are no error messages, nor output from the JVM.

REPRODUCIBILITY :
This bug can be reproduced always.

---------- BEGIN SOURCE ----------
package scratch;
// Freeze.java	William Dubel	June 30, 2003

import java.io.*;
import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
import java.awt.image.*;
import java.util.ArrayList;
import java.util.Collections;
import javax.swing.border.TitledBorder;

public class Freeze extends JFrame  {
	private JMenuItem newGame, quit, about;
	private JMenu fileMenu;
	private JPanel puzzleArea, puzzleGrid;
	private JMenuBar bar;
	private JFileChooser fileChooser = new JFileChooser();
	private Container c;
	private int tiles = 25;
	private ArrayList pieces = new ArrayList();
	private Game currentGame;
	private MouseHandler mouseHandler = new MouseHandler();
	private ItemHandler itemHandler = new ItemHandler();

	public Freeze() {
		super("ImageTest");
		c = getContentPane();

		fileChooser.setFileSelectionMode(JFileChooser.FILES_ONLY);

		setJMenuBar(bar = new JMenuBar());
		(fileMenu = new JMenu("File")).setMnemonic('F');
		(newGame = new JMenuItem("Open
Image...",'N')).addActionListener(itemHandler);
		(quit = new JMenuItem("Exit",'x')).addActionListener
(itemHandler);
		fileMenu.add(newGame);
		fileMenu.add(quit);
		bar.add(fileMenu);

		(puzzleGrid = new JPanel()).setLayout(new GridLayout(1, 1));
		puzzleGrid.setSize(300,300);

		(puzzleArea = new JPanel()).setLayout(new FlowLayout());
		puzzleArea.add(puzzleGrid);
		puzzleArea.setBorder(new TitledBorder("Click on image to
scramble"));

		c.add(puzzleArea,BorderLayout.CENTER);
		setSize(500,400);

		addWindowListener( new WindowAdapter()	{
			public void windowClosing( WindowEvent e ) {
				System.exit(0);
			}
		}   );
	}

	private class MouseHandler extends MouseAdapter {
		private int first = -1;
		public void mousePressed(MouseEvent e)  {
			if (first<0 || first >= tiles)  {
				for (int n=0; n<tiles; n++) if (e.getSource()
==pieces.get(n))
				{
					((Piece)pieces.get(first =
n)).setSelected(true);
					((Piece)pieces.get(first)).repaint();
				}
			}
			else    {
				for (int n=0; n<tiles; n++)
					if (e.getSource()==pieces.get(n))   {
						((Piece)pieces.get(n)).repaint
();
						((Piece)pieces.get
(first)).setSelected(false);

						puzzleGrid.removeAll();
						Collections.swap(pieces,
first, n);

						for (int i=0; i<tiles; i++)
puzzleGrid.add((Piece)pieces.get(i));
						puzzleGrid.validate();
					}
				first = -1;
			}
		}
	}

	private class ItemHandler implements ActionListener {
		public void actionPerformed( ActionEvent e )    {
			if (e.getSource()==newGame) {
				int answer = -1;
				if (answer!=JOptionPane.CANCEL_OPTION)  {
					int result = fileChooser.showOpenDialog
(c);
					if (result!
=JFileChooser.CANCEL_OPTION) {
						File filename =
fileChooser.getSelectedFile();

						currentGame = new Game
((filename), tiles);

						puzzleArea.remove(puzzleGrid);
						puzzleGrid = new JPanel();
						puzzleGrid.setLayout(new
GridLayout(currentGame.getRows(), currentGame.getCols()));

						pieces = new ArrayList();
						for (int i=0; i<tiles; i++) {
							Piece temp = new Piece
(currentGame, i+1);
							temp.addMouseListener
(mouseHandler);
							pieces.add(temp);
							puzzleGrid.add(temp);
						}
						puzzleGrid.setSize(	(int)
currentGame.getSize().getWidth(),
									
				(int)currentGame.getSize().getHeight()	);
						puzzleArea.add(puzzleGrid);
						pack();
						puzzleGrid.validate();
					}
				}
			}
			if (e.getSource()==quit)        {       System.exit
(0);         }
		}
	}

	public static void main(String [] args) {
		JFrame puzzlegame = new Freeze();
		puzzlegame.setVisible(true);
	}
}

class Game  {
	private ImageIcon imageObject;
	private int complexity;

	public Game(File filename, int complexity)  {
		this.complexity = complexity;
		imageObject = new ImageIcon(filename.getPath());
		try	{ while (imageObject.getImageLoadStatus()
==MediaTracker.LOADING) Thread.sleep(10); }
		catch(InterruptedException ie)	{	ie.printStackTrace();
				}
	}

	public Image getImage()					{	return
imageObject.getImage();	}

	public Dimension getSize()				{
		return new Dimension(imageObject.getIconWidth
(),imageObject.getIconHeight());
	}

	public int getRows()	{
		return (int)Math.sqrt(complexity);
	}

	public int getCols()	{
		return getRows();
	}
}

class Piece extends JPanel implements Runnable  {
	private Dimension size;
	private Image mini;
	private int order;
	private MediaTracker loader;
	private boolean selected = false;

	public Piece(Game currentGame, int tile)    {
		size = new Dimension((int)(currentGame.getSize().getWidth
()/currentGame.getCols()),
									(int)
(currentGame.getSize().getHeight()/currentGame.getRows()));
		mini = currentGame.getImage();

		CropImageFilter cropFilter =
			new CropImageFilter(	(((tile%currentGame.getCols()!
=0) ? tile%currentGame.getCols()
									
		: currentGame.getCols())-1)*(int)size.getWidth(),

									
	(int)((Math.ceil((double)tile/currentGame.getCols())-1)*size.getHeight
()),
									
	(int)size.getWidth(),
									
	(int)size.getHeight());

		mini = createImage(new FilteredImageSource(mini.getSource
(),cropFilter));
		order = tile;

		loader = new MediaTracker(this);
		loader.addImage(mini, order);
		new Thread(this).start();
	}

	public void run()   {
		try
		{	while (loader.statusID(order, true)!
=MediaTracker.COMPLETE) Thread.sleep(20);	}
		catch(InterruptedException ie)		{
	ie.printStackTrace();		}
		repaint();
	}

	public Dimension getPreferredSize()		{		return
size;				}
	public boolean isSelected()				{
	return selected;			}
	public void setSelected(boolean yes)	{		selected = yes;
			    }

	public void paintComponent(Graphics g)  {
		Graphics2D g2 = (Graphics2D)g;

		if (loader.statusID(order, true)!=MediaTracker.COMPLETE)    {
			GradientPaint gp = new GradientPaint(0.0f, 0.0f,
Color.lightGray,
				   (float)size.getWidth(), (float)
size.getHeight(), Color.darkGray);
			g2.setPaint(gp);
			g2.fillRect(0, 0, (int)size.getWidth()-1, (int)
size.getHeight()-1);
		}
		else    {
			g2.drawImage(mini, 0, 0, (int)size.getWidth(), (int)
size.getHeight(), this);
			if (selected)
			{
				g2.draw3DRect(1, 1, (int)size.getWidth()-3,
(int)size.getHeight()-3, false);
				g2.draw3DRect(0, 0, (int)size.getWidth()-1,
(int)size.getHeight()-1, false);
			}
		}
	}
}
---------- END SOURCE ----------

Release Regression From : hopper-beta
The above release value was the last known release where this 
bug was known to work. Since then there has been a regression.

(Review ID: 158715) 
======================================================================

                                    

Comments
WORK AROUND

Use -Dsun.java2d.noddraw=true
This prevents our use of DirectX for rendering and apparently (according to the
submitter) avoids whatever the freeze problem is.  

The flag
	-Dsun.java2d.d3d=false
is also worth trying (and a better, less constraining flag to use), but the
user has not yet been able to verify whether this avoids the bug since the
publicly release build is currently b14, which does not yet fully implement
the d3d=false functionality.

###@###.### 2002-07-23


-Dsun.java2d.d3d=false doesn't avoid the problem with 1.4.1-beta-b14. However, this option helps to avoid problem with 1.4.1_01-bo1.
###@###.### 2002-10-31
                                     
2002-07-23
EVALUATION

 Still can't reproduce this problem.  Targetting it for mantis.

This seems very card-specific.  We have tried it on very similar platforms,
including XP with the Radeon 7200 VE (a variation on the 7200 that lacks the
geometry engine) and have still not seen the crash here.  We need to pick up
the exact same card and see if that helps reproduce the problem.  The user
has only seen the problem on the one machine with this configuration, although
they did test with 2 different drivers (the original XP driver as well as a
more recent ATI driver) and both drivers produced the bug.

###@###.### 2002-07-23

The user does not experience the freeze when running with the noddraw flag.
It is also worth trying the sun.java2d.d3d=false flag (but the user needs
a later build past the 1.4.1 b14 build they are running with now to try
this flag).
###@###.### 2002-07-23

I am finally able to reproduce this problem.  I installed a Radeon 7500 with
driver version 5.13.1.6043 and I now get either a hang or a Blue Screen Of 
Death upon app exit.  

Note the workarounds: both the noddraw=true and d3d=false flags prevent the
problem from occurring.  Thus the problem must be somewhere in our 
d3d usage.

One strange thing I noticed during my debugging so far was that the 
creation of d3d devices fails with the error E_NOINTERFACE (0x80004002).
This is curious for two reasons: 
	- I don't know why we would fail, or why failing that call 
	(CreateDevice) would fail with that error (it is not even spec'd
	to return that error).
	- Since we fail creating the device, we should not be using
	d3d to render anything (lpSurface for those DDrawSurface objects
	is NULL).  So there should be very little interaction with d3d
	that gets us into trouble (except, presumably, these calls to
	CreateDevice).

###@###.### 2002-08-29

The error I was getting above (E_NOINTERFACE) was erroneous; this was actually
an error I was getting when trying to create a d3d device on my secondary 
device when running multimon.  Since that other device is a Matrox Mill II,
which does not support d3d, getting an error during d3d device creation 
is not too surprising.

I disabled my second monitor to reduce the confusion and still consistently
blue-screen upon app exit.  I no longer get any ddraw/d3d errors during device
creation (or at any other time).  And I can step, using the debugger, all the
way through the exit from DllMain during PROCESS_DETACH, so there is nothing
obvious that is causing this problem.  Presumably we are trouncing on memory
somewhere, but I don't have any leads yet.  It is worth trying to reproduce
this problem in a native app to narrow it down.

###@###.### 2002-09-03

I have attached a test case which is a purely native app that shows the
problem.  Apparently the problem is that we create too many d3d devices.
Currently, whenever we want d3d capabilities on a ddraw surface, we create
a d3d device for that surface.  The DirectX docs suggest that having one
d3d device per ddraw object (not per ddraw surface) is a better way to
go for performance reasons; the context switching inherent in multiple d3d
devices is too expensive.  They do not happen to mention that having too many
devices can also hang the system (this is presumably a driver bug that we
just happened to bump into on these ATI boards).

Although the real fix for the crash lies with ATI (I don't think the OS
should crash because of what we are doing), we can and should fix the problem
in our code by moving to a system of sharing a single d3d device, as suggested
by the DirectX documention.

There are issues with this approach, such as:
	- we need to do a SetRenderTarget whenever we are rendering into 
	a new ddraw surface since the single d3d device may currently be
	set to a different render target.
	- in addition to changing the rendering target, we also need to
	update the viewport if the dimensions of the new surface differ
	from the previous one.
	- we need to lock around accesses to this shared device since we 
	have potential multi-threaded rendering issues when rendering to
	different surfaces.

The test case (4713003.ManyD3dDevices.zip.Z) is a zipped project file from
a DevStudio application.  You can either run the app in the Debug folder
directly or build the application and run it within DevStudio.  
By changing the "NUM_OFFSCREEN_SURFACES" variable at the top of
DDHelloWorld.cpp you can affect the crash.  I found that the crash only
occurred when this value was greater than 8 (the value is currently set to
9 in that file).

Forwarding the bug to the engineer currently working on the single-device
approach.

###@###.### 2002-09-06
                                     
2002-09-06
PUBLIC COMMENTS

.
                                     
2004-06-10
CONVERTED DATA

BugTraq+ Release Management Values

COMMIT TO FIX:
1.4.1_02
mantis

FIXED IN:
1.4.1_02
mantis

INTEGRATED IN:
1.4.1_02
mantis
mantis-b07


                                     
2004-06-14



Hardware and Software, Engineered to Work Together