8148886: SEGV in sun.java2d.marlin.Renderer._endRendering
Summary: handle reentrancy in both AAShapePipe and MarlinRenderingEngine using new sun.java2d.ReentrantContextProvider implementations
Reviewed-by: flar, prr
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/src/java.desktop/share/classes/sun/java2d/ReentrantContext.java Tue Feb 23 22:07:27 2016 +0100
@@ -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;
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/src/java.desktop/share/classes/sun/java2d/ReentrantContextProvider.java Tue Feb 23 22:07:27 2016 +0100
@@ -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;
+ }
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/src/java.desktop/share/classes/sun/java2d/ReentrantContextProviderCLQ.java Tue Feb 23 22:07:27 2016 +0100
@@ -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));
+ }
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/src/java.desktop/share/classes/sun/java2d/ReentrantContextProviderTL.java Tue Feb 23 22:07:27 2016 +0100
@@ -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);
+ }
+ }
+}
--- a/jdk/src/java.desktop/share/classes/sun/java2d/marlin/ByteArrayCache.java Tue Feb 23 09:11:19 2016 -0800
+++ b/jdk/src/java.desktop/share/classes/sun/java2d/marlin/ByteArrayCache.java Tue Feb 23 22:07:27 2016 +0100
@@ -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 @@
}
if (doChecks) {
- check(array, 0, array.length, value);
+ check(array, fromIndex, toIndex, value);
}
}
@@ -135,9 +135,10 @@
{
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:
--- a/jdk/src/java.desktop/share/classes/sun/java2d/marlin/FloatArrayCache.java Tue Feb 23 09:11:19 2016 -0800
+++ b/jdk/src/java.desktop/share/classes/sun/java2d/marlin/FloatArrayCache.java Tue Feb 23 22:07:27 2016 +0100
@@ -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 @@
}
if (doChecks) {
- check(array, 0, array.length, value);
+ check(array, fromIndex, toIndex, value);
}
}
@@ -136,9 +136,10 @@
{
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:
--- a/jdk/src/java.desktop/share/classes/sun/java2d/marlin/IntArrayCache.java Tue Feb 23 09:11:19 2016 -0800
+++ b/jdk/src/java.desktop/share/classes/sun/java2d/marlin/IntArrayCache.java Tue Feb 23 22:07:27 2016 +0100
@@ -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 @@
}
if (doChecks) {
- check(array, 0, array.length, value);
+ check(array, fromIndex, toIndex, value);
}
}
@@ -135,9 +135,10 @@
{
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:
--- a/jdk/src/java.desktop/share/classes/sun/java2d/marlin/MarlinCache.java Tue Feb 23 09:11:19 2016 -0800
+++ b/jdk/src/java.desktop/share/classes/sun/java2d/marlin/MarlinCache.java Tue Feb 23 22:07:27 2016 +0100
@@ -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 @@
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) {
--- a/jdk/src/java.desktop/share/classes/sun/java2d/marlin/MarlinRenderingEngine.java Tue Feb 23 09:11:19 2016 -0800
+++ b/jdk/src/java.desktop/share/classes/sun/java2d/marlin/MarlinRenderingEngine.java Tue Feb 23 22:07:27 2016 +0100
@@ -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.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 @@
// 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 @@
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 @@
*/
@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 @@
if (doMonitors) {
RendererContext.stats.mon_pre_getAATileGenerator.stop();
}
- if (!useThreadLocal) {
- rdrCtxQueue.offer(rdrCtx.reference);
- }
+ rdrCtxProvider.release(rdrCtx);
}
}
--- a/jdk/src/java.desktop/share/classes/sun/java2d/marlin/RendererContext.java Tue Feb 23 09:11:19 2016 -0800
+++ b/jdk/src/java.desktop/share/classes/sun/java2d/marlin/RendererContext.java Tue Feb 23 22:07:27 2016 +0100
@@ -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 @@
/**
* 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 @@
? 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 @@
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 @@
// 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 @@
/**
* Constructor
*
- * @param name
+ * @param name context name (debugging)
*/
RendererContext(final String name) {
if (logCreateContext) {
@@ -124,20 +121,6 @@
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;
- }
}
/**
--- a/jdk/src/java.desktop/share/classes/sun/java2d/marlin/Version.java Tue Feb 23 09:11:19 2016 -0800
+++ b/jdk/src/java.desktop/share/classes/sun/java2d/marlin/Version.java Tue Feb 23 22:07:27 2016 +0100
@@ -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 @@
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;
--- a/jdk/src/java.desktop/share/classes/sun/java2d/pipe/AAShapePipe.java Tue Feb 23 09:11:19 2016 -0800
+++ b/jdk/src/java.desktop/share/classes/sun/java2d/pipe/AAShapePipe.java Tue Feb 23 22:07:27 2016 +0100
@@ -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.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 @@
* 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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
}
// 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 @@
return box;
}
}
-
}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/sun/java2d/marlin/CrashPaintTest.java Tue Feb 23 22:07:27 2016 +0100
@@ -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);
+ }
+ }
+}