Interesting, so this can happen with any custom Paint or Composite. Yes, re-entrance detection should be done.

Just out of curiosity - the 10% performance hit for the CLQ storage vs. thread-local - that was specifically only for the time it takes to retreive the Renderer, right? I can't imagine why the entire rendering operation would take 10% longer for any mechanism that retrieves a cached Renderer - unless perhaps it was missing the cache and allocating a new one every time? I would think a simple synchronized block around a small cache of Renderers would be nearly imperceptible compared to the amount of time spent inside the rendering process...

                        ...jim

On 2/3/2016 5:28 AM, Laurent Bourgès wrote:
Phil,

bug https://bugs.openjdk.java.net/browse/JDK-8148886

Thanks for creating the bug !

I spent some time tracking down the bug and make the diagnostic:

1. I enabled diagnostics:

*-Dsun.java2d.renderer.log=true*

*-Dsun.java2d.renderer.doChecks=true*

**

-Dsun.java2d.renderer.doStats=true

-Dsun.java2d.renderer.logUnsafeMalloc=true

I also enabled MarlinConst.doLogBounds in the Marlin code to have more
details ... but it is not necessary.

2. The array checks detect invalid arrays (edgeBuckets / edgeBucketCounts)

WARNING: Invalid array value at: *16438* = 24 from: 0 to: 312
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, ...]

java.lang.Throwable
             at
org.marlin.pisces.IntArrayCache.check(IntArrayCache.java:139)
             at org.marlin.pisces.IntArrayCache.fill(IntArrayCache.java:128)
             at org.marlin.pisces.Renderer.dispose(Renderer.java:649)

             at
org.marlin.pisces.MarlinTileGenerator.dispose(MarlinTileGenerator.java:65)
*            at sun.java2d.pipe.AAShapePipe.renderPath(AAShapePipe.java:150)
*            at sun.java2d.pipe.AAShapePipe.fill(AAShapePipe.java:75)
         at
sun.java2d.pipe.PixelToParallelogramConverter.fill(PixelToParallelogramConverter.java:164)
         at sun.java2d.pipe.ValidatePipe.fill(ValidatePipe.java:160)
*        at sun.java2d.SunGraphics2D.fill(SunGraphics2D.java:2527)*

         at org.faceless.pdf2.af
<http://org.faceless.pdf2.af>$b.a(af$b.java:178)
         at org.faceless.pdf2.af.a(af.java:843)
         at org.faceless.pdf2.af.f(af.java:666)
         at org.faceless.pdf2.am.a(am.java:726)
         at org.faceless.pdf2.am.a(am.java:237)
         at org.faceless.pdf2.b1.b(b1.java:441)
         at org.faceless.pdf2.b1.a(b1.java:415)
         at org.faceless.pdf2.b_.createContext(b_.java:101)

         at
sun.java2d.pipe.AlphaPaintPipe.startSequence(AlphaPaintPipe.java:84)
         at sun.java2d.pipe.AAShapePipe.renderTiles(AAShapePipe.java:163)
*        at sun.java2d.pipe.AAShapePipe.renderPath(AAShapePipe.java:149)
*        at sun.java2d.pipe.AAShapePipe.fill(AAShapePipe.java:75)
     at
sun.java2d.pipe.PixelToParallelogramConverter.fill(PixelToParallelogramConverter.java:164)
     at sun.java2d.pipe.ValidatePipe.fill(ValidatePipe.java:160)
*    at sun.java2d.SunGraphics2D.fill(SunGraphics2D.java:2527)*

     at org.faceless.pdf2.af
<http://org.faceless.pdf2.af>$b.a(af$b.java:178)
     at org.faceless.pdf2.af.a(af.java:843)
     at org.faceless.pdf2.af.f(af.java:666)
     at org.faceless.pdf2.am.a(am.java:726)
     at org.faceless.pdf2.am.a(am.java:237)
     at org.faceless.pdf2.am.a(am.java:941)
     at org.faceless.pdf2.am.parsePage(am.java:154)
     at org.faceless.pdf2.PagePainter.a(PagePainter.java:638)
     at org.faceless.pdf2.PagePainter.a(PagePainter.java:362)
     at org.faceless.pdf2.PagePainter.getSubImage(PagePainter.java:341)
     at org.faceless.pdf2.PagePainter.getImage(PagePainter.java:310)
     at PDFToImage.main(PDFToImage.java:155)

Conclusion:

There is a reentrance issue in Marlin when it uses thread local storage
(default):

*SunGraphics2D.fill()* -> AAShapePipe.renderTiles() ->
AlphaPaintPipe.startSequence() ->
org.faceless.pdf2.b_.createContext() -> *SunGraphics2D.fill() ...
*

1. Reentrance is not supported when the RendererContext is stored in a
thread-local variable (and reused among invocations).

The segfault happens as the edgeBuckets / edgeBucketCounts arrays are
empty (not zero-filled) and these arrays store "java pointers" (integer)
to the Unsafe edge array !

2. I tested CLQ storage (concurrent linked queue) (~10% slower): it is
not affected by this problem as the RendererContext is removed / added
to the pool (not reused in recursion).

Please use the simple workaround (CLQ):

-Dsun.java2d.renderer.useThreadLocal=false


I will then look at a proper solution: detect reentrance and use CLQ to
store child RendererContexts ?

Regards,

Laurent


Le 2 févr. 2016 21:26, "Phil Race" <[email protected]
<mailto:[email protected]>> a écrit :

    I have filed a bug https://bugs.openjdk.java.net/browse/JDK-8148886

    I also downloaded the test case and see the crash.

    FYI if you want to continue to test JDK9 builds and this is a blocker
    for you (I suppose it is), then until it is fixed you can do

    -Dsun.java2d.renderer=sun.dc.DuctusRenderingEngine
    or
    -Dsun.java2d.renderer=sun.java2d.pisces.PiscesRenderingEngine

    -phil.

Reply via email to