Uploaded image for project: 'JDK'
  1. JDK
  2. JDK-8096274

[Font] ConcurrentModificationException in PrismFontFactory

    XMLWordPrintable

Details

    • Bug
    • Resolution: Fixed
    • P3
    • 8u40
    • 8u5, 8u25, 8u40
    • javafx
    • Windows 8.1 with JDK 8u40 b11 early access, Linux Mint with JDK 8u40 b11 early access.

    Description

      I can't find a really good sample code, because the situation in which this can be reproduced is quite specific to the implementation I am using, but I am going to provide the steps to reproduce, and I think that solution for this problem doesn't require much of instructions:

      1. Create JavaFX application, set Scene to the Stage and apply test.css to which has font facets declared in it:
      @font-face {
          font-family: SomeFont;
          src: url(SomeFont.ttf');
          font-weight: regular;
      }

      @font-face {
          font-family: SomeFont;
          src: url(SomeFonBoldt.ttf');
          font-weight: bold;
      }

      there are also declarations for italic, etc.
      The fonts are loaded in the application fine, they are applied and shown properly.

      2. Perform Stage.hide() and after that Stage.show() followed by:

      Scene newScene = new Scene(root);
      newScene.getStyleSheet().add(getClass().getResource("test.css").toExternalForm());
      stage.setScene(newScene);

      This action calls on PrismFontFactory.loadEmbeddedFont() which calls PrismFontFactory.removeEmbeddedFont(String fullName) just before adding that loaded font to embeddedFonts HashMap.

      Inside PrismFontFactory.removeEmbeddedFont iteration trough CompositeFontResource map "compResourceMap" is performed (I don't know how compResourceMap is populated, didn't check it).

      Then it comes to these lines of code:

      for (String key : compResourceMap.keySet()) {
         CompositeFontResource compFont = compResourceMap.get(key);
         if (compFont.getSlotResource(0) == font) {
            compResourceMap.remove(key);
          }
       }

      After that this exception will be thrown:

      Exception in thread "JavaFX Application Thread" java.util.ConcurrentModificationException
      at java.util.HashMap$HashIterator.nextNode(HashMap.java:1429)
      at java.util.HashMap$KeyIterator.next(HashMap.java:1453)
      at com.sun.javafx.font.PrismFontFactory.removeEmbeddedFont(PrismFontFactory.java:1554)
      at com.sun.javafx.font.PrismFontFactory.loadEmbeddedFont(PrismFontFactory.java:1638)
      at com.sun.javafx.font.PrismFontFactory.loadEmbeddedFont(PrismFontFactory.java:1529)
      at com.sun.javafx.font.PrismFontLoader.loadFont(PrismFontLoader.java:99)
      at javafx.scene.text.Font.loadFont(Font.java:400)
      at com.sun.javafx.css.StyleManager.loadStylesheetUnPrivileged(StyleManager.java:1001)
      at com.sun.javafx.css.StyleManager.loadStylesheet(StyleManager.java:798)
      at com.sun.javafx.css.StyleManager.gatherSceneStylesheets(StyleManager.java:1338)
      at com.sun.javafx.css.StyleManager.findMatchingStyles(StyleManager.java:1405)
      at javafx.scene.CssStyleHelper.createStyleHelper(CssStyleHelper.java:115)
      at javafx.scene.Node.impl_processCSS(Node.java:8877)
      at javafx.scene.Parent.impl_processCSS(Parent.java:1250)
      at javafx.scene.Node.processCSS(Node.java:8732)
      at javafx.scene.Scene.doCSSPass(Scene.java:569)
      at javafx.scene.Scene.access$3500(Scene.java:201)
      at javafx.scene.Scene$ScenePulseListener.pulse(Scene.java:2385)
      at com.sun.javafx.tk.Toolkit$3.run(Toolkit.java:321)
      at com.sun.javafx.tk.Toolkit$3.run(Toolkit.java:319)
      at java.security.AccessController.doPrivileged(Native Method)
      at com.sun.javafx.tk.Toolkit.runPulse(Toolkit.java:319)
      at com.sun.javafx.tk.Toolkit.firePulse(Toolkit.java:348)
      at com.sun.javafx.tk.quantum.QuantumToolkit.pulse(QuantumToolkit.java:479)
      at com.sun.javafx.tk.quantum.QuantumToolkit.pulse(QuantumToolkit.java:460)
      at com.sun.javafx.tk.quantum.QuantumToolkit$13.run(QuantumToolkit.java:327)
      at com.sun.glass.ui.InvokeLaterDispatcher$Future.run(InvokeLaterDispatcher.java:95)
      at com.sun.glass.ui.gtk.GtkApplication._runLoop(Native Method)
      at com.sun.glass.ui.gtk.GtkApplication.access$200(GtkApplication.java:48)
      at com.sun.glass.ui.gtk.GtkApplication$6$1.run(GtkApplication.java:149)
      at java.lang.Thread.run(Thread.java:745)

      Reason for this exception is that there is more than one element inside "compResourceMap" HashMap (HashMap<String, CompositeFontResource>) and there is this line of code:

      compResourceMap.remove(key);

      which tries to remove element from HashMap during it's iteration which causes java.util.ConcurrentModificationException because you shouldn't remove elements from HashMap during it's iteration. Check HashMap#HashIterator#nextNode which trows exception if the number of elements inside HashMap doesn't fit to expected number of elements. This is not the problem if HashMap has only one entry, but compResourceMap has more than one entry.

      The worst thing about it is that this exception crashes the entire JavaFX Application Thread unless DefaultUncaughtExceptionHandler is set to JavaFX thread.

      I have downloaded today's JDK 8u40 b11 from https://jdk8.java.net/download.html, and checked it, the behavior and code in PrismFontFactory#removeEmbeddedFont is the same.

      Attachments

        Activity

          People

            prr Philip Race
            duke J. Duke
            Votes:
            0 Vote for this issue
            Watchers:
            6 Start watching this issue

            Dates

              Created:
              Updated:
              Resolved:
              Imported: