XChart icon indicating copy to clipboard operation
XChart copied to clipboard

Deadlock in BitmapEncoder

Open flaviolenz opened this issue 4 years ago • 3 comments

I got a CPU running at 100% for about 2hours. A tip from JStack showed this process not ending, which might be the cause: (it was frozen in the same place during all this period)

"getGraficoGrupos-U_pedro-E_Açaí-90" #43 daemon prio=5 os_prio=0 tid=0x00007f71fc00b000 nid=0x12d1 runnable [0x00007f71f11f9000]
   java.lang.Thread.State: RUNNABLE
	at sun.font.SunLayoutEngine.nativeLayout(Native Method)
	at sun.font.SunLayoutEngine.layout(SunLayoutEngine.java:187)
	at sun.font.GlyphLayout$EngineRecord.layout(GlyphLayout.java:685)
	at sun.font.GlyphLayout.layout(GlyphLayout.java:466)
	at sun.font.ExtendedTextSourceLabel.createGV(ExtendedTextSourceLabel.java:329)
	at sun.font.ExtendedTextSourceLabel.getGV(ExtendedTextSourceLabel.java:315)
	at sun.font.ExtendedTextSourceLabel.createLogicalBounds(ExtendedTextSourceLabel.java:225)
	at sun.font.ExtendedTextSourceLabel.getAdvance(ExtendedTextSourceLabel.java:134)
	at java.awt.font.TextLine.init(TextLine.java:281)
	at java.awt.font.TextLine.<init>(TextLine.java:129)
	at java.awt.font.TextLine.fastCreateTextLine(TextLine.java:983)
	at java.awt.font.TextLayout.fastInit(TextLayout.java:612)
	at java.awt.font.TextLayout.<init>(TextLayout.java:393)
	at org.knowm.xchart.internal.chartpart.AxisTickCalculator_.willLabelsFitInTickSpaceHint(AxisTickCalculator_.java:144)
	at org.knowm.xchart.internal.chartpart.AxisTickCalculator_.calculateForEquallySpacedAxisValues(AxisTickCalculator_.java:407)
	at org.knowm.xchart.internal.chartpart.AxisTickCalculator_.calculate(AxisTickCalculator_.java:205)
	at org.knowm.xchart.internal.chartpart.AxisTickCalculator_Number.<init>(AxisTickCalculator_Number.java:47)
	at org.knowm.xchart.internal.chartpart.Axis.getAxisTickCalculator(Axis.java:572)
	at org.knowm.xchart.internal.chartpart.Axis.getYAxisWidthHint(Axis.java:349)
	at org.knowm.xchart.internal.chartpart.Axis.preparePaint(Axis.java:159)
	at org.knowm.xchart.internal.chartpart.AxisPair.paint(AxisPair.java:124)
	at org.knowm.xchart.CategoryChart.paint(CategoryChart.java:308)
	at org.knowm.xchart.BitmapEncoder.getBufferedImage(BitmapEncoder.java:281)
	at org.knowm.xchart.BitmapEncoder.getBitmapBytes(BitmapEncoder.java:264)
	at contahub.cmds.ChartCmd.getImgGraficoGrupos(ChartCmd.java:1468)

my code in line 1468 is nothing strange:

      CategoryChart chart = 
               new CategoryChartBuilder()
...  BUILD THE CHART
      byte[] bytes = BitmapEncoder.getBitmapBytes(chart, BitmapFormat.PNG);

I could not repeat the error.

Any Clue?

flaviolenz avatar Mar 17 '21 15:03 flaviolenz

I have hit this same issue. The deadlock originates at line 399 of AxisTickCalculator_.java in the calculateForEquallySpacedAxisValues method.

At line 393, span is calculated; however, if it is calculated as 0.0, the line below will calculate gridStep as NaN, which in turns results in gridStepInChartSpace being 0.

The call to willLabelsFitInTickSpaceHint() will always return false, and all subsequent calculations of span will be 0.0 and you end up in an infinite loop.

amugofjava avatar Apr 25 '24 14:04 amugofjava

Please find below a minimal example to reproduce this issue:

import org.knowm.xchart.BitmapEncoder;
import org.knowm.xchart.XYChartBuilder;
import java.io.IOException;
import java.util.List;

public class XChartDeadlock {
    public static void main(String[] args) throws IOException {
        var chart = new XYChartBuilder()
                .width(720)
                .height(480)
                .title("Deadlock Example")
                .xAxisTitle("Count")
                .yAxisTitle("Value")
                .build();

        // If the decimal places in the pattern is fewer than the largest data value, the deadlock occurs.
        chart.getStyler().setDecimalPattern("#0.00");

        var xValues = List.of(1, 2, 3, 4, 5, 6, 7, 8);

        // The only value here is 0.001 which is one decimal place below the pattern.
        var yValues = List.of(0.0, 0.0, 0.0, 0.0, 0.001, 0.0, 0.0, 0.0);

        chart.addSeries("main", xValues, yValues);

        BitmapEncoder.getBitmapBytes(chart, BitmapEncoder.BitmapFormat.PNG);
    }
}

The issue occurs when using setDecimalPattern. If your data values contains only one unique value greater than 0, and the number of decimal places of that value is more than you've specified in the pattern, the deadlock occurs.

To fix this example, you can either change the pattern to 3 decimal places or change one of the 0.0 xValues to another value other than 0.001.

I hope I've explained that OK.

amugofjava avatar Apr 25 '24 15:04 amugofjava

Thanks for the example code. I'll try to fix it now...

timmolter avatar Apr 25 '24 18:04 timmolter

Fix pushed, please test.

timmolter avatar May 10 '24 08:05 timmolter

Hi @timmolter,

Yes, all is working correctly now. Thank you for fixing this.

amugofjava avatar May 14 '24 15:05 amugofjava