8148886: SEGV in sun.java2d.marlin.Renderer._endRendering

Handle reentrancy in both AAShapePipe and MarlinRenderingEngine using new sun.java2d.ReentrantContextProvider implementations

Reviewed-by: flar, prr
This commit is contained in:
Laurent Bourgès 2016-02-23 22:07:27 +01:00
parent c067e0428b
commit 1036ce73ea
13 changed files with 767 additions and 147 deletions

View File

@ -0,0 +1,43 @@
/*
* Copyright (c) 2016, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package sun.java2d;
import java.lang.ref.Reference;
/**
* ReentrantContext is a base class to hold thread-local data supporting
* reentrancy in either a ThreadLocal or a ConcurrentLinkedQueue
*
* @see ReentrantContextProvider
*/
public class ReentrantContext {
// usage stored as a byte
byte usage = ReentrantContextProvider.USAGE_TL_INACTIVE;
/*
* Reference to this instance (hard, soft or weak).
* @see ReentrantContextProvider#refType
*/
Reference<? extends ReentrantContext> reference = null;
}

View File

@ -0,0 +1,169 @@
/*
* Copyright (c) 2016, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package sun.java2d;
import java.lang.ref.Reference;
import java.lang.ref.SoftReference;
import java.lang.ref.WeakReference;
/**
* This abstract ReentrantContextProvider helper class manages the creation,
* storage, and retrieval of concrete ReentrantContext instances which can be
* subclassed to hold cached contextual data.
*
* It supports reentrancy as every call to acquire() provides a new unique context
* instance that must later be returned for reuse by a call to release(ctx)
* (typically in a try/finally block).
*
* It has a couple of abstract implementations which store references in a queue
* and/or thread-local storage.
* The Providers can be configured to hold ReentrantContext instances in memory
* using hard, soft or weak references.
*
* The acquire() and release() methods are used to retrieve and return the contexts.
*
* The {@code newContext()} method remains abstract in all implementations and
* must be provided by the module to create a new subclass of ReentrantContext
* with the appropriate contextual data in it.
*
* Sample Usage:
* - create a subclass ReentrantContextImpl to hold the thread state:
*
* static final class ReentrantContextImpl extends ReentrantContext {
* // specific cached data
* }
*
* - create the appropriate ReentrantContextProvider:
*
* private static final ReentrantContextProvider<ReentrantContextImpl> contextProvider =
* new ReentrantContextProviderTL<ReentrantContextImpl>(ReentrantContextProvider.REF_WEAK)
* {
* @Override
* protected ReentrantContextImpl newContext() {
* return new ReentrantContextImpl();
* }
* };
* ...
* void someMethod() {
* ReentrantContextImpl ctx = contextProvider.acquire();
* try {
* // use the context
* } finally {
* contextProvider.release(ctx);
* }
* }
*
* @param <K> ReentrantContext subclass
*
* @see ReentrantContext
*/
public abstract class ReentrantContextProvider<K extends ReentrantContext>
{
// thread-local storage: inactive
static final byte USAGE_TL_INACTIVE = 0;
// thread-local storage: in use
static final byte USAGE_TL_IN_USE = 1;
// CLQ storage
static final byte USAGE_CLQ = 2;
// hard reference
public static final int REF_HARD = 0;
// soft reference
public static final int REF_SOFT = 1;
// weak reference
public static final int REF_WEAK = 2;
/* members */
// internal reference type
private final int refType;
/**
* Create a new ReentrantContext provider using the given reference type
* among hard, soft or weak
*
* @param refType reference type
*/
protected ReentrantContextProvider(final int refType) {
this.refType = refType;
}
/**
* Create a new ReentrantContext instance
*
* @return new ReentrantContext instance
*/
protected abstract K newContext();
/**
* Give a ReentrantContext instance for the current thread
*
* @return ReentrantContext instance
*/
public abstract K acquire();
/**
* Restore the given ReentrantContext instance for reuse
*
* @param ctx ReentrantContext instance
*/
public abstract void release(K ctx);
@SuppressWarnings("unchecked")
protected final Reference<K> getOrCreateReference(final K ctx) {
if (ctx.reference == null) {
// Create the reference:
switch (refType) {
case REF_HARD:
ctx.reference = new HardReference<K>(ctx);
break;
case REF_SOFT:
ctx.reference = new SoftReference<K>(ctx);
break;
default:
case REF_WEAK:
ctx.reference = new WeakReference<K>(ctx);
break;
}
}
return (Reference<K>) ctx.reference;
}
/* Missing HardReference implementation */
static final class HardReference<V> extends WeakReference<V> {
// kept strong reference:
private final V strongRef;
HardReference(final V referent) {
// no referent needed for the parent WeakReference:
super(null);
this.strongRef = referent;
}
@Override
public V get() {
return strongRef;
}
}
}

View File

@ -0,0 +1,89 @@
/*
* Copyright (c) 2016, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package sun.java2d;
import java.lang.ref.Reference;
import java.util.concurrent.ConcurrentLinkedQueue;
/**
* This ReentrantContextProvider implementation uses one ConcurrentLinkedQueue
* to store all ReentrantContext instances (thread and its child contexts)
*
* Note: this implementation keeps less contexts in memory depending on the
* concurrent active threads in contrary to a ThreadLocal provider. However,
* it is slower in highly concurrent workloads.
*
* @param <K> ReentrantContext subclass
*/
public abstract class ReentrantContextProviderCLQ<K extends ReentrantContext>
extends ReentrantContextProvider<K>
{
// ReentrantContext queue to store all contexts
private final ConcurrentLinkedQueue<Reference<K>> ctxQueue
= new ConcurrentLinkedQueue<Reference<K>>();
/**
* Create a new ReentrantContext provider using the given reference type
* among hard, soft or weak based using a ConcurrentLinkedQueue storage
*
* @param refType reference type
*/
public ReentrantContextProviderCLQ(final int refType) {
super(refType);
}
/**
* Give a ReentrantContext instance for the current thread
*
* @return ReentrantContext instance
*/
@Override
public final K acquire() {
K ctx = null;
// Drain queue if all referent are null:
Reference<K> ref = null;
while ((ctx == null) && ((ref = ctxQueue.poll()) != null)) {
ctx = ref.get();
}
if (ctx == null) {
// create a new ReentrantContext if none is available
ctx = newContext();
ctx.usage = USAGE_CLQ;
}
return ctx;
}
/**
* Restore the given ReentrantContext instance for reuse
*
* @param ctx ReentrantContext instance
*/
@Override
public final void release(final K ctx) {
if (ctx.usage == USAGE_CLQ) {
ctxQueue.offer(getOrCreateReference(ctx));
}
}
}

View File

@ -0,0 +1,123 @@
/*
* Copyright (c) 2016, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package sun.java2d;
import java.lang.ref.Reference;
/**
* This ReentrantContextProvider implementation uses a ThreadLocal to hold
* the first ReentrantContext per thread and a ReentrantContextProviderCLQ to
* store child ReentrantContext instances needed during recursion.
*
* Note: this implementation may keep up to one context in memory per thread.
* Child contexts for recursive uses are stored in the queue using a WEAK
* reference by default unless specified in the 2 argument constructor.
*
* @param <K> ReentrantContext subclass
*/
public abstract class ReentrantContextProviderTL<K extends ReentrantContext>
extends ReentrantContextProvider<K>
{
// Thread-local storage:
private final ThreadLocal<Reference<K>> ctxTL
= new ThreadLocal<Reference<K>>();
// ReentrantContext CLQ provider for child contexts:
private final ReentrantContextProviderCLQ<K> ctxProviderCLQ;
/**
* Create a new ReentrantContext provider using the given reference type
* among hard, soft or weak.
* It uses weak reference for the child contexts.
*
* @param refType reference type
*/
public ReentrantContextProviderTL(final int refType) {
this(refType, REF_WEAK);
}
/**
* Create a new ReentrantContext provider using the given reference types
* among hard, soft or weak
*
* @param refTypeTL reference type used by ThreadLocal
* @param refTypeCLQ reference type used by ReentrantContextProviderCLQ
*/
public ReentrantContextProviderTL(final int refTypeTL, final int refTypeCLQ)
{
super(refTypeTL);
final ReentrantContextProviderTL<K> parent = this;
this.ctxProviderCLQ = new ReentrantContextProviderCLQ<K>(refTypeCLQ) {
@Override
protected K newContext() {
return parent.newContext();
}
};
}
/**
* Give a ReentrantContext instance for the current thread
*
* @return ReentrantContext instance
*/
@Override
public final K acquire() {
K ctx = null;
final Reference<K> ref = ctxTL.get();
if (ref != null) {
ctx = ref.get();
}
if (ctx == null) {
// create a new ReentrantContext if none is available
ctx = newContext();
// update thread local reference:
ctxTL.set(getOrCreateReference(ctx));
}
// Check reentrance:
if (ctx.usage == USAGE_TL_INACTIVE) {
ctx.usage = USAGE_TL_IN_USE;
} else {
// get or create another ReentrantContext from CLQ provider:
ctx = ctxProviderCLQ.acquire();
}
return ctx;
}
/**
* Restore the given ReentrantContext instance for reuse
*
* @param ctx ReentrantContext instance
*/
@Override
public final void release(final K ctx) {
if (ctx.usage == USAGE_TL_IN_USE) {
ctx.usage = USAGE_TL_INACTIVE;
} else {
ctxProviderCLQ.release(ctx);
}
}
}

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2015, 2016, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@ -126,7 +126,7 @@ final class ByteArrayCache implements MarlinConst {
}
if (doChecks) {
check(array, 0, array.length, value);
check(array, fromIndex, toIndex, value);
}
}
@ -135,9 +135,10 @@ final class ByteArrayCache implements MarlinConst {
{
if (doChecks) {
// check zero on full array:
for (int i = fromIndex; i < toIndex; i++) {
for (int i = 0; i < array.length; i++) {
if (array[i] != value) {
logException("Invalid array value at " + i + "\n"
logException("Invalid value at: " + i + " = " + array[i]
+ " from: " + fromIndex + " to: " + toIndex + "\n"
+ Arrays.toString(array), new Throwable());
// ensure array is correctly filled:

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2015, 2016, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@ -127,7 +127,7 @@ final class FloatArrayCache implements MarlinConst {
}
if (doChecks) {
check(array, 0, array.length, value);
check(array, fromIndex, toIndex, value);
}
}
@ -136,9 +136,10 @@ final class FloatArrayCache implements MarlinConst {
{
if (doChecks) {
// check zero on full array:
for (int i = fromIndex; i < toIndex; i++) {
for (int i = 0; i < array.length; i++) {
if (array[i] != value) {
logException("Invalid array value at " + i + "\n"
logException("Invalid value at: " + i + " = " + array[i]
+ " from: " + fromIndex + " to: " + toIndex + "\n"
+ Arrays.toString(array), new Throwable());
// ensure array is correctly filled:

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2015, 2016, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@ -126,7 +126,7 @@ final class IntArrayCache implements MarlinConst {
}
if (doChecks) {
check(array, 0, array.length, value);
check(array, fromIndex, toIndex, value);
}
}
@ -135,9 +135,10 @@ final class IntArrayCache implements MarlinConst {
{
if (doChecks) {
// check zero on full array:
for (int i = fromIndex; i < toIndex; i++) {
for (int i = 0; i < array.length; i++) {
if (array[i] != value) {
logException("Invalid array value at " + i + "\n"
logException("Invalid value at: " + i + " = " + array[i]
+ " from: " + fromIndex + " to: " + toIndex + "\n"
+ Arrays.toString(array), new Throwable());
// ensure array is correctly filled:

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2007, 2015, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2007, 2016, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@ -590,8 +590,8 @@ public final class MarlinCache implements MarlinConst {
alphaRow[to + 1] = 0;
}
if (doChecks) {
IntArrayCache.check(blkFlags, 0, blkFlags.length, 0);
IntArrayCache.check(alphaRow, 0, alphaRow.length, 0);
IntArrayCache.check(blkFlags, blkW, blkE, 0);
IntArrayCache.check(alphaRow, from, px1 - bboxX0, 0);
}
if (doMonitors) {

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2007, 2015, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2007, 2016, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@ -30,11 +30,12 @@ import java.awt.Shape;
import java.awt.geom.AffineTransform;
import java.awt.geom.Path2D;
import java.awt.geom.PathIterator;
import java.lang.ref.Reference;
import java.security.AccessController;
import java.util.concurrent.ConcurrentLinkedQueue;
import static sun.java2d.marlin.MarlinUtils.logInfo;
import sun.awt.geom.PathConsumer2D;
import sun.java2d.ReentrantContextProvider;
import sun.java2d.ReentrantContextProviderCLQ;
import sun.java2d.ReentrantContextProviderTL;
import sun.java2d.pipe.AATileGenerator;
import sun.java2d.pipe.Region;
import sun.java2d.pipe.RenderingEngine;
@ -882,46 +883,50 @@ public class MarlinRenderingEngine extends RenderingEngine
// use ThreadLocal or ConcurrentLinkedQueue to get one RendererContext
private static final boolean useThreadLocal;
// hard reference
static final int REF_HARD = 0;
// soft reference
static final int REF_SOFT = 1;
// weak reference
static final int REF_WEAK = 2;
// reference type stored in either TL or CLQ
static final int REF_TYPE;
// Per-thread RendererContext
private static final ThreadLocal<Object> rdrCtxThreadLocal;
// RendererContext queue when ThreadLocal is disabled
private static final ConcurrentLinkedQueue<Object> rdrCtxQueue;
private static final ReentrantContextProvider<RendererContext> rdrCtxProvider;
// Static initializer to use TL or CLQ mode
static {
// CLQ mode by default:
useThreadLocal = MarlinProperties.isUseThreadLocal();
rdrCtxThreadLocal = (useThreadLocal) ? new ThreadLocal<Object>()
: null;
rdrCtxQueue = (!useThreadLocal) ? new ConcurrentLinkedQueue<Object>()
: null;
// Soft reference by default:
String refType = AccessController.doPrivileged(
final String refType = AccessController.doPrivileged(
new GetPropertyAction("sun.java2d.renderer.useRef",
"soft"));
switch (refType) {
default:
case "soft":
REF_TYPE = REF_SOFT;
REF_TYPE = ReentrantContextProvider.REF_SOFT;
break;
case "weak":
REF_TYPE = REF_WEAK;
REF_TYPE = ReentrantContextProvider.REF_WEAK;
break;
case "hard":
REF_TYPE = REF_HARD;
REF_TYPE = ReentrantContextProvider.REF_HARD;
break;
}
if (useThreadLocal) {
rdrCtxProvider = new ReentrantContextProviderTL<RendererContext>(REF_TYPE)
{
@Override
protected RendererContext newContext() {
return RendererContext.createContext();
}
};
} else {
rdrCtxProvider = new ReentrantContextProviderCLQ<RendererContext>(REF_TYPE)
{
@Override
protected RendererContext newContext() {
return RendererContext.createContext();
}
};
}
}
private static boolean settingsLogged = !enableLogs;
@ -936,13 +941,13 @@ public class MarlinRenderingEngine extends RenderingEngine
String refType;
switch (REF_TYPE) {
default:
case REF_HARD:
case ReentrantContextProvider.REF_HARD:
refType = "hard";
break;
case REF_SOFT:
case ReentrantContextProvider.REF_SOFT:
refType = "soft";
break;
case REF_WEAK:
case ReentrantContextProvider.REF_WEAK:
refType = "weak";
break;
}
@ -1025,22 +1030,7 @@ public class MarlinRenderingEngine extends RenderingEngine
*/
@SuppressWarnings({"unchecked"})
static RendererContext getRendererContext() {
RendererContext rdrCtx = null;
final Object ref = (useThreadLocal) ? rdrCtxThreadLocal.get()
: rdrCtxQueue.poll();
if (ref != null) {
// resolve reference:
rdrCtx = (REF_TYPE == REF_HARD) ? ((RendererContext) ref)
: ((Reference<RendererContext>) ref).get();
}
// create a new RendererContext if none is available
if (rdrCtx == null) {
rdrCtx = RendererContext.createContext();
if (useThreadLocal) {
// update thread local reference:
rdrCtxThreadLocal.set(rdrCtx.reference);
}
}
final RendererContext rdrCtx = rdrCtxProvider.acquire();
if (doMonitors) {
RendererContext.stats.mon_pre_getAATileGenerator.start();
}
@ -1057,8 +1047,6 @@ public class MarlinRenderingEngine extends RenderingEngine
if (doMonitors) {
RendererContext.stats.mon_pre_getAATileGenerator.stop();
}
if (!useThreadLocal) {
rdrCtxQueue.offer(rdrCtx.reference);
}
rdrCtxProvider.release(rdrCtx);
}
}

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2015, 2016, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@ -26,9 +26,10 @@
package sun.java2d.marlin;
import java.awt.geom.Path2D;
import java.lang.ref.SoftReference;
import java.lang.ref.WeakReference;
import java.util.concurrent.atomic.AtomicInteger;
import sun.java2d.ReentrantContext;
import sun.java2d.ReentrantContextProvider;
import static sun.java2d.marlin.ArrayCache.*;
import sun.java2d.marlin.MarlinRenderingEngine.NormalizingPathIterator;
import static sun.java2d.marlin.MarlinUtils.logInfo;
@ -36,7 +37,7 @@ import static sun.java2d.marlin.MarlinUtils.logInfo;
/**
* This class is a renderer context dedicated to a single thread
*/
final class RendererContext implements MarlinConst {
final class RendererContext extends ReentrantContext implements MarlinConst {
// RendererContext creation counter
private static final AtomicInteger contextCount = new AtomicInteger(1);
@ -45,7 +46,7 @@ final class RendererContext implements MarlinConst {
? RendererStats.getInstance(): null;
private static final boolean USE_CACHE_HARD_REF = doStats
|| (MarlinRenderingEngine.REF_TYPE == MarlinRenderingEngine.REF_WEAK);
|| (MarlinRenderingEngine.REF_TYPE == ReentrantContextProvider.REF_WEAK);
/**
* Create a new renderer context
@ -55,6 +56,7 @@ final class RendererContext implements MarlinConst {
static RendererContext createContext() {
final RendererContext newCtx = new RendererContext("ctx"
+ Integer.toString(contextCount.getAndIncrement()));
if (RendererContext.stats != null) {
RendererContext.stats.allContexts.add(newCtx);
}
@ -63,11 +65,6 @@ final class RendererContext implements MarlinConst {
// context name (debugging purposes)
final String name;
/*
* Reference to this instance (hard, soft or weak).
* @see MarlinRenderingEngine#REF_TYPE
*/
final Object reference;
// Smallest object used as Cleaner's parent reference
final Object cleanerObj = new Object();
// dirty flag indicating an exception occured during pipeline in pathTo()
@ -101,7 +98,7 @@ final class RendererContext implements MarlinConst {
/**
* Constructor
*
* @param name
* @param name context name (debugging)
*/
RendererContext(final String name) {
if (logCreateContext) {
@ -124,20 +121,6 @@ final class RendererContext implements MarlinConst {
stroker = new Stroker(this);
dasher = new Dasher(this);
// Create the reference to this instance (hard, soft or weak):
switch (MarlinRenderingEngine.REF_TYPE) {
default:
case MarlinRenderingEngine.REF_HARD:
reference = this;
break;
case MarlinRenderingEngine.REF_SOFT:
reference = new SoftReference<RendererContext>(this);
break;
case MarlinRenderingEngine.REF_WEAK:
reference = new WeakReference<RendererContext>(this);
break;
}
}
/**

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2015, 2016, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@ -27,7 +27,7 @@ package sun.java2d.marlin;
public final class Version {
private static final String version = "marlin-0.7.3-Unsafe-OpenJDK";
private static final String version = "marlin-0.7.3.2-Unsafe-OpenJDK";
public static String getVersion() {
return version;

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 1997, 2015, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 1997, 2016, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@ -28,7 +28,11 @@ import java.awt.BasicStroke;
import java.awt.Rectangle;
import java.awt.Shape;
import java.awt.geom.Rectangle2D;
import java.util.concurrent.ConcurrentLinkedQueue;
import sun.awt.SunHints;
import sun.java2d.ReentrantContext;
import sun.java2d.ReentrantContextProvider;
import sun.java2d.ReentrantContextProviderTL;
import sun.java2d.SunGraphics2D;
/**
@ -38,28 +42,31 @@ import sun.java2d.SunGraphics2D;
* This class sets up the Generator and computes the alpha tiles
* and then passes them on to a CompositePipe object for painting.
*/
public class AAShapePipe
public final class AAShapePipe
implements ShapeDrawPipe, ParallelogramPipe
{
static RenderingEngine renderengine = RenderingEngine.getInstance();
static final RenderingEngine renderengine = RenderingEngine.getInstance();
// Per-thread TileState (~1K very small so do not use any Weak Reference)
private static final ThreadLocal<TileState> tileStateThreadLocal =
new ThreadLocal<TileState>() {
@Override
protected TileState initialValue() {
return new TileState();
}
};
private static final ReentrantContextProvider<TileState> tileStateProvider =
new ReentrantContextProviderTL<TileState>(
ReentrantContextProvider.REF_HARD)
{
@Override
protected TileState newContext() {
return new TileState();
}
};
CompositePipe outpipe;
final CompositePipe outpipe;
public AAShapePipe(CompositePipe pipe) {
outpipe = pipe;
}
@Override
public void draw(SunGraphics2D sg, Shape s) {
BasicStroke bs;
final BasicStroke bs;
if (sg.stroke instanceof BasicStroke) {
bs = (BasicStroke) sg.stroke;
@ -71,10 +78,12 @@ public class AAShapePipe
renderPath(sg, s, bs);
}
@Override
public void fill(SunGraphics2D sg, Shape s) {
renderPath(sg, s, null);
}
@Override
public void fillParallelogram(SunGraphics2D sg,
double ux1, double uy1,
double ux2, double uy2,
@ -82,21 +91,23 @@ public class AAShapePipe
double dx1, double dy1,
double dx2, double dy2)
{
Region clip = sg.getCompClip();
final TileState ts = tileStateThreadLocal.get();
final int[] abox = ts.abox;
final TileState ts = tileStateProvider.acquire();
try {
final int[] abox = ts.abox;
AATileGenerator aatg =
renderengine.getAATileGenerator(x, y, dx1, dy1, dx2, dy2, 0, 0,
clip, abox);
if (aatg == null) {
// Nothing to render
return;
final AATileGenerator aatg =
renderengine.getAATileGenerator(x, y, dx1, dy1, dx2, dy2, 0, 0,
sg.getCompClip(), abox);
if (aatg != null) {
renderTiles(sg, ts.computeBBox(ux1, uy1, ux2, uy2),
aatg, abox, ts);
}
} finally {
tileStateProvider.release(ts);
}
renderTiles(sg, ts.computeBBox(ux1, uy1, ux2, uy2), aatg, abox, ts);
}
@Override
public void drawParallelogram(SunGraphics2D sg,
double ux1, double uy1,
double ux2, double uy2,
@ -105,52 +116,61 @@ public class AAShapePipe
double dx2, double dy2,
double lw1, double lw2)
{
Region clip = sg.getCompClip();
final TileState ts = tileStateThreadLocal.get();
final int[] abox = ts.abox;
final TileState ts = tileStateProvider.acquire();
try {
final int[] abox = ts.abox;
AATileGenerator aatg =
renderengine.getAATileGenerator(x, y, dx1, dy1, dx2, dy2, lw1, lw2,
clip, abox);
if (aatg == null) {
// Nothing to render
return;
final AATileGenerator aatg =
renderengine.getAATileGenerator(x, y, dx1, dy1, dx2, dy2, lw1,
lw2, sg.getCompClip(), abox);
if (aatg != null) {
// Note that bbox is of the original shape, not the wide path.
// This is appropriate for handing to Paint methods...
renderTiles(sg, ts.computeBBox(ux1, uy1, ux2, uy2),
aatg, abox, ts);
}
} finally {
tileStateProvider.release(ts);
}
// Note that bbox is of the original shape, not the wide path.
// This is appropriate for handing to Paint methods...
renderTiles(sg, ts.computeBBox(ux1, uy1, ux2, uy2), aatg, abox, ts);
}
public void renderPath(SunGraphics2D sg, Shape s, BasicStroke bs) {
boolean adjust = (bs != null &&
final boolean adjust = (bs != null &&
sg.strokeHint != SunHints.INTVAL_STROKE_PURE);
boolean thin = (sg.strokeState <= SunGraphics2D.STROKE_THINDASHED);
final boolean thin = (sg.strokeState <= SunGraphics2D.STROKE_THINDASHED);
Region clip = sg.getCompClip();
final TileState ts = tileStateThreadLocal.get();
final int[] abox = ts.abox;
final TileState ts = tileStateProvider.acquire();
try {
final int[] abox = ts.abox;
AATileGenerator aatg =
renderengine.getAATileGenerator(s, sg.transform, clip,
bs, thin, adjust, abox);
if (aatg == null) {
// Nothing to render
return;
final AATileGenerator aatg =
renderengine.getAATileGenerator(s, sg.transform, sg.getCompClip(),
bs, thin, adjust, abox);
if (aatg != null) {
renderTiles(sg, s, aatg, abox, ts);
}
} finally {
tileStateProvider.release(ts);
}
renderTiles(sg, s, aatg, abox, ts);
}
public void renderTiles(SunGraphics2D sg, Shape s,
AATileGenerator aatg, int abox[], TileState ts)
final AATileGenerator aatg,
final int[] abox, final TileState ts)
{
Object context = null;
try {
// reentrance: outpipe may also use AAShapePipe:
context = outpipe.startSequence(sg, s,
ts.computeDevBox(abox),
abox);
// copy of int[] abox as local variables for performance:
final int x0 = abox[0];
final int y0 = abox[1];
final int x1 = abox[2];
final int y1 = abox[3];
final int tw = aatg.getTileWidth();
final int th = aatg.getTileHeight();
@ -158,16 +178,15 @@ public class AAShapePipe
final byte[] alpha = ts.getAlphaTile(tw * th);
byte[] atile;
for (int y = abox[1]; y < abox[3]; y += th) {
int h = Math.min(th, abox[3] - y);
for (int y = y0; y < y1; y += th) {
final int h = Math.min(th, y1 - y);
for (int x = abox[0]; x < abox[2]; x += tw) {
int w = Math.min(tw, abox[2] - x);
for (int x = x0; x < x1; x += tw) {
final int w = Math.min(tw, x1 - x);
int a = aatg.getTypicalAlpha();
if (a == 0x00 ||
outpipe.needTile(context, x, y, w, h) == false)
{
final int a = aatg.getTypicalAlpha();
if (a == 0x00 || !outpipe.needTile(context, x, y, w, h)) {
aatg.nextTile();
outpipe.skipTile(context, x, y);
continue;
@ -180,8 +199,7 @@ public class AAShapePipe
aatg.getAlpha(alpha, 0, tw);
}
outpipe.renderPathTile(context, atile, 0, tw,
x, y, w, h);
outpipe.renderPathTile(context, atile, 0, tw, x, y, w, h);
}
}
} finally {
@ -193,7 +211,7 @@ public class AAShapePipe
}
// Tile state used by AAShapePipe
static final class TileState {
static final class TileState extends ReentrantContext {
// cached tile (32 x 32 tile by default)
private byte[] theTile = new byte[32 * 32];
// dirty aabox array
@ -240,5 +258,4 @@ public class AAShapePipe
return box;
}
}
}

View File

@ -0,0 +1,205 @@
/*
* Copyright (c) 2016, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
import java.awt.Color;
import java.awt.Graphics2D;
import java.awt.Paint;
import java.awt.PaintContext;
import java.awt.Rectangle;
import java.awt.RenderingHints;
import java.awt.TexturePaint;
import java.awt.geom.AffineTransform;
import java.awt.geom.Ellipse2D;
import java.awt.geom.Rectangle2D;
import java.awt.image.BufferedImage;
import java.awt.image.ColorModel;
import java.awt.image.Raster;
import java.io.File;
import java.io.IOException;
import java.util.Locale;
import java.util.logging.Handler;
import java.util.logging.LogRecord;
import java.util.logging.Logger;
import javax.imageio.ImageIO;
/**
* @test
* @bug 8148886
* @summary Verifies that Marlin supports reentrant operations (ThreadLocal)
* like in custom Paint or custom Composite
* @run main CrashPaintTest
*/
public class CrashPaintTest {
static final boolean SAVE_IMAGE = false;
public static void main(String argv[]) {
Locale.setDefault(Locale.US);
// initialize j.u.l Looger:
final Logger log = Logger.getLogger("sun.java2d.marlin");
log.addHandler(new Handler() {
@Override
public void publish(LogRecord record) {
Throwable th = record.getThrown();
// detect any Throwable:
if (th != null) {
System.out.println("Test failed:\n" + record.getMessage());
th.printStackTrace(System.out);
throw new RuntimeException("Test failed: ", th);
}
}
@Override
public void flush() {
}
@Override
public void close() throws SecurityException {
}
});
// enable Marlin logging & internal checks:
System.setProperty("sun.java2d.renderer.log", "true");
System.setProperty("sun.java2d.renderer.useLogger", "true");
System.setProperty("sun.java2d.renderer.doChecks", "true");
// Force using thread-local storage:
System.setProperty("sun.java2d.renderer.useThreadLocal", "true");
// Force smaller pixelsize to force using array caches:
System.setProperty("sun.java2d.renderer.pixelsize", "256");
final int width = 300;
final int height = 300;
final BufferedImage image = new BufferedImage(width, height,
BufferedImage.TYPE_INT_ARGB);
final Graphics2D g2d = (Graphics2D) image.getGraphics();
try {
g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
RenderingHints.VALUE_ANTIALIAS_ON);
g2d.setBackground(Color.WHITE);
g2d.clearRect(0, 0, width, height);
final Ellipse2D.Double ellipse
= new Ellipse2D.Double(0, 0, width, height);
final Paint paint = new CustomPaint(100);
for (int i = 0; i < 20; i++) {
final long start = System.nanoTime();
g2d.setPaint(paint);
g2d.fill(ellipse);
g2d.setColor(Color.GREEN);
g2d.draw(ellipse);
final long time = System.nanoTime() - start;
System.out.println("paint: duration= " + (1e-6 * time) + " ms.");
}
if (SAVE_IMAGE) {
try {
final File file = new File("CrashPaintTest.png");
System.out.println("Writing file: "
+ file.getAbsolutePath());
ImageIO.write(image, "PNG", file);
} catch (IOException ex) {
System.out.println("Writing file failure:");
ex.printStackTrace();
}
}
// Check image on few pixels:
final Raster raster = image.getData();
// 170, 175 = blue
checkPixel(raster, 170, 175, Color.BLUE.getRGB());
// 50, 50 = blue
checkPixel(raster, 50, 50, Color.BLUE.getRGB());
// 190, 110 = pink
checkPixel(raster, 190, 110, Color.PINK.getRGB());
// 280, 210 = pink
checkPixel(raster, 280, 210, Color.PINK.getRGB());
} finally {
g2d.dispose();
}
}
private static void checkPixel(final Raster raster,
final int x, final int y,
final int expected) {
final int[] rgb = (int[]) raster.getDataElements(x, y, null);
if (rgb[0] != expected) {
throw new IllegalStateException("bad pixel at (" + x + ", " + y
+ ") = " + rgb[0] + " expected: " + expected);
}
}
private static class CustomPaint extends TexturePaint {
private int size;
CustomPaint(final int size) {
super(new BufferedImage(size, size,
BufferedImage.TYPE_INT_ARGB),
new Rectangle2D.Double(0, 0, size, size)
);
this.size = size;
}
@Override
public PaintContext createContext(ColorModel cm,
Rectangle deviceBounds,
Rectangle2D userBounds,
AffineTransform at,
RenderingHints hints) {
// Fill bufferedImage using
final Graphics2D g2d = (Graphics2D) getImage().getGraphics();
try {
g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
RenderingHints.VALUE_ANTIALIAS_ON);
g2d.setBackground(Color.PINK);
g2d.clearRect(0, 0, size, size);
g2d.setColor(Color.BLUE);
g2d.drawRect(0, 0, size, size);
g2d.fillOval(size / 10, size / 10,
size * 8 / 10, size * 8 / 10);
} finally {
g2d.dispose();
}
return super.createContext(cm, deviceBounds, userBounds, at, hints);
}
}
}