|
Quick Lists
|
|
Bug ID:
|
6640532
|
|
Votes
|
0
|
|
Synopsis
|
Graphics.getFontMetrics() throws NullPointerException
|
|
Category
|
java:classes_2d
|
|
Reported Against
|
|
|
Release Fixed
|
7(b28),
6u10(b10) (Bug ID:2157370)
|
|
State
|
10-Fix Delivered,
bug
|
|
Priority:
|
3-Medium
|
|
Related Bugs
|
|
|
Submit Date
|
11-DEC-2007
|
|
Description
|
FULL PRODUCT VERSION :
JRE 1.6.0_03-b05
ADDITIONAL OS VERSION INFORMATION :
Linux 2.6.21-1.3194.fc7 #1 SMP Wed May 23 22:35:01 EDT 2007 i686 i686 i386 GNU/Linux
A DESCRIPTION OF THE PROBLEM :
Graphics.getFontMetrics() throws NullPointerException.
Happens on FontMetrics.charsWidth also.
The problem seems to be in sun.font.FontManager.addToPool(). The following assertion fails.
// is it possible for this to be the same font?
assert fontFileCache[lastPoolIndex] != font;
This can happen when the sun.font.TrueTypeFont.readBlock() was executed, and during execution if the threads were interrupted. Several threads executing readBlock() needs to be interrupted, for this problem to show up. In such a scenario, FontManager cache might be containing multiple references to the same TrueType font. When FontManager.addToPool attempts to close a font file which is same as the one getting added to pool, we get this exception.
One solution would be in the TrueTypeFont.open(), assign the fontfile after adding it to pool.
STEPS TO FOLLOW TO REPRODUCE THE PROBLEM :
Run the attached testcase and keep selecting entries in the combo box which contains the list of fonts. Selecting a entry in the Combox enables getting the fontmetrics of that font.
EXPECTED VERSUS ACTUAL BEHAVIOR :
EXPECTED -
Should not throw NPE
ACTUAL -
throws NPE
ERROR MESSAGES/STACK TRACES THAT OCCUR :
Exception in thread "Thread-1" java.lang.NullPointerException
at sun.font.TrueTypeFont.readBlock(Unknown Source)
at sun.font.TrueTypeFont.readBlock(Unknown Source)
at sun.font.TrueTypeFont.createScaler(Native Method)
at sun.font.TrueTypeFont.getScaler(Unknown Source)
at sun.font.FileFontStrike.<init>(Unknown Source)
at sun.font.FileFont.createStrike(Unknown Source)
at sun.font.Font2D.getStrike(Unknown Source)
at sun.font.Font2D.getStrike(Unknown Source)
at sun.font.CompositeStrike.getStrikeForSlot(Unknown Source)
at sun.font.CompositeStrike.getFontMetrics(Unknown Source)
at sun.font.FontDesignMetrics.initMatrixAndMetrics(Unknown Source)
at sun.font.FontDesignMetrics.<init>(Unknown Source)
at sun.font.FontDesignMetrics.getMetrics(Unknown Source)
at sun.java2d.SunGraphics2D.getFontMetrics(Unknown Source)
at lab.FontNPETest$2.run(FontNPETest.java:57)
at java.lang.Thread.run(Unknown Source)
REPRODUCIBILITY :
This bug can be reproduced always.
---------- BEGIN SOURCE ----------
package lab;
import java.awt.Font;
import java.awt.FontMetrics;
import java.awt.Graphics;
import java.awt.GraphicsEnvironment;
import java.awt.Image;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JComboBox;
import javax.swing.JFrame;
public class FontNPETest {
private static Font gfont = null;
public static void main(String[] args) {
final JFrame fr = new JFrame("FontTest");
final JComboBox box = createCombo();
fr.getContentPane().add(box);
fr.pack();
fr.setVisible(true);
fr.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
startFontManipulation(fr);
}
private static JComboBox createCombo() {
final JComboBox box = new JComboBox(
GraphicsEnvironment.getLocalGraphicsEnvironment().getAllFonts());
box.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
Font f = (Font)box.getSelectedItem();
f = f.deriveFont(12f);
setFont(f);
}
});
return box;
}
private static synchronized Font getFont() {
return gfont;
}
private static synchronized void setFont(Font f) {
gfont = f;
}
private static void startFontManipulation(final JFrame fr) {
Thread t1 = new Thread(new Runnable() {
public void run() {
while(true) {
Font f = getFont();
if(f != null) {
Image img = fr.createImage(100, 100);
Graphics g = img.getGraphics();
FontMetrics fm = g.getFontMetrics(gfont);
final Thread thisT = Thread.currentThread();
Thread t = new Thread(new Runnable() {
public void run() {
thisT.interrupt();
}
});
t.start();
try {
fm.charsWidth("ABCDE".toCharArray(), 0, 5);
} catch(NullPointerException e) {
e.printStackTrace();
}
g.dispose();
}
}
}
});
t1.start();
}
}
---------- END SOURCE ----------
CUSTOMER SUBMITTED WORKAROUND :
Don't interrupt the thread.
Posted Date : 2007-12-11 23:32:03.0
|
|
Work Around
|
N/A
|
|
Evaluation
|
If the thread is being interrupted repeatedly, its possible we
end up with the same font filling a number of slots, and since
this code is called only from within an open() call, to register
that the font file is open for reading, its a bad idea to close() it
as happens here, since its destined for almost immediate use :
// is it possible for this to be the same font?
assert fontFileCache[lastPoolIndex] != font;
/* replace with new font, poolSize is unchanged. */
fontFileCache[lastPoolIndex].close();
fontFileCache[lastPoolIndex] = font;
Need to revise this code to detect the addition of duplicates.
I can also see how we can get a deadlock when updating the pool.
Its hard to capture this without fixing the first problem, after
which I was able to see an instance
Thread-A locks font1, then tries to acquire the pool lock
at sun.font.FontManager.addToPool(FontManager.java:287)
- waiting to lock <0xf47a6fb8> (a [Lsun.font.FileFont;)
at sun.font.TrueTypeFont.open(TrueTypeFont.java:282)
- locked <0xf485cec0> (a sun.font.TrueTypeFont)
at sun.font.TrueTypeFont.readBlock(TrueTypeFont.java:313)
- locked <0xf485cec0> (a sun.font.TrueTypeFont)
meanwhile Thread-B first acquires a lock on font2,
then the pool lock, and then needs the lock on font1 to close() it
and hence free it from the pool :
at sun.font.TrueTypeFont.close(TrueTypeFont.java:304)
- waiting to lock <0xf485cec0> (a sun.font.TrueTypeFont)
at sun.font.FontManager.addToPool(FontManager.java:309)
- locked <0xf47a6fb8> (a [Lsun.font.FileFont;)
at sun.font.TrueTypeFont.open(TrueTypeFont.java:282)
- locked <0xf485d5f0> (a sun.font.TrueTypeFont)
at sun.font.TrueTypeFont.getTableBuffer(TrueTypeFont.java:819)
- locked <0xf485d5f0> (a sun.font.TrueTypeFont)
The simplest fix here is for close() to be moved outside the synchronised block.
Posted Date : 2007-12-13 18:13:23.0
|
|
Comments
|
Submitted On 26-DEC-2007
This may be related: we have a customer who has been experiencing lockups of our terminal emulation software with a java.lang.AssertionError (apparently without any detail message)
at sun.font.FontManager.addToPool
at sun.font.TrueTypeFont.open
at sun.font.TrueTypeFont.readBlock
at sun.font.TrueTypeFont.readBlock
at sun.font.FileFont.getGlyphImage
at sun.font.FileFontStrike.getGlyphImagePtrs
at sun.font.GlyphList.mapChars
at sun.font.GlyphList.setFromString
at sun.java2d.pip.GlyphListPipe.drawString
at sun.java2d.SunGraphics2D.drawString
in response to code that does a drawString of a single character, in response to a keystroke. The user was able to duplicate the problem simply by typing any alpha character while holding down both ALT and SHIFT. We have been unable to duplicate the problem, and presently do not have further information about the end-user's circumstances, beyond the fact that the user is on some version of Java 6.
PLEASE NOTE: JDK6 is formerly known as Project Mustang
|
|
|
 |