# HG changeset patch # User dl # Date 1387449119 0 # Node ID 9a4f43c9b15a2b5936a7a6fc900643eb6d368eed # Parent a1ee9743f4ee165eae59389a020f2552f895dac8 8026155: Enhance ForkJoin pool Reviewed-by: chegar, alanb, ahgross diff -r a1ee9743f4ee -r 9a4f43c9b15a jdk/src/share/classes/java/util/concurrent/ForkJoinPool.java --- a/jdk/src/share/classes/java/util/concurrent/ForkJoinPool.java Wed Jul 05 19:25:40 2017 +0200 +++ b/jdk/src/share/classes/java/util/concurrent/ForkJoinPool.java Thu Dec 19 10:31:59 2013 +0000 @@ -49,6 +49,9 @@ import java.util.concurrent.RunnableFuture; import java.util.concurrent.ThreadLocalRandom; import java.util.concurrent.TimeUnit; +import java.security.AccessControlContext; +import java.security.ProtectionDomain; +import java.security.Permissions; /** * An {@link ExecutorService} for running {@link ForkJoinTask}s. @@ -140,6 +143,9 @@ *
  • {@code java.util.concurrent.ForkJoinPool.common.exceptionHandler} * - the class name of a {@link UncaughtExceptionHandler} * + * If a {@link SecurityManager} is present and no factory is + * specified, then the default pool uses a factory supplying + * threads that have no {@link Permissions} enabled. * The system class loader is used to load these classes. * Upon any error in establishing these settings, default parameters * are used. It is possible to disable or limit the use of threads in @@ -501,6 +507,16 @@ * task status checks) in inapplicable cases amounts to an odd * form of limited spin-wait before blocking in ForkJoinTask.join. * + * As a more appropriate default in managed environments, unless + * overridden by system properties, we use workers of subclass + * InnocuousForkJoinWorkerThread when there is a SecurityManager + * present. These workers have no permissions set, do not belong + * to any user-defined ThreadGroup, and erase all ThreadLocals + * after executing any top-level task (see WorkQueue.runTask). The + * associated mechanics (mainly in ForkJoinWorkerThread) may be + * JVM-dependent and must access particular Thread class fields to + * achieve this effect. + * * Style notes * =========== * @@ -882,6 +898,7 @@ */ final void runTask(ForkJoinTask task) { if ((currentSteal = task) != null) { + ForkJoinWorkerThread thread; task.doExec(); ForkJoinTask[] a = array; int md = mode; @@ -899,6 +916,8 @@ t.doExec(); } } + if ((thread = owner) != null) // no need to do in finally clause + thread.afterTopLevelExec(); } } @@ -1155,7 +1174,7 @@ * Increment for seed generators. See class ThreadLocal for * explanation. */ - private static final int SEED_INCREMENT = 0x61c88647; + private static final int SEED_INCREMENT = 0x9e3779b9; /* * Bits and masks for control variables @@ -2084,12 +2103,10 @@ ((c & ~AC_MASK) | ((c & AC_MASK) + AC_UNIT)))); } - if ((b = q.base) - q.top < 0 && (t = q.pollAt(b)) != null) { - (w.currentSteal = t).doExec(); - w.currentSteal = ps; - } + if ((b = q.base) - q.top < 0 && (t = q.pollAt(b)) != null) + w.runTask(t); } - else if (active) { // decrement active count without queuing + else if (active) { // decrement active count without queuing long nc = ((c = ctl) & ~AC_MASK) | ((c & AC_MASK) - AC_UNIT); if ((int)(nc >> AC_SHIFT) + parallelism == 0) break; // bypass decrement-then-increment @@ -3282,8 +3299,7 @@ */ private static ForkJoinPool makeCommonPool() { int parallelism = -1; - ForkJoinWorkerThreadFactory factory - = defaultForkJoinWorkerThreadFactory; + ForkJoinWorkerThreadFactory factory = null; UncaughtExceptionHandler handler = null; try { // ignore exceptions in accessing/parsing properties String pp = System.getProperty @@ -3302,7 +3318,12 @@ getSystemClassLoader().loadClass(hp).newInstance()); } catch (Exception ignore) { } - + if (factory == null) { + if (System.getSecurityManager() == null) + factory = defaultForkJoinWorkerThreadFactory; + else // use security-managed default + factory = new InnocuousForkJoinWorkerThreadFactory(); + } if (parallelism < 0 && // default 1 less than #cores (parallelism = Runtime.getRuntime().availableProcessors() - 1) <= 0) parallelism = 1; @@ -3312,4 +3333,38 @@ "ForkJoinPool.commonPool-worker-"); } + /** + * Factory for innocuous worker threads + */ + static final class InnocuousForkJoinWorkerThreadFactory + implements ForkJoinWorkerThreadFactory { + + /** + * An ACC to restrict permissions for the factory itself. + * The constructed workers have no permissions set. + */ + private static final AccessControlContext innocuousAcc; + static { + Permissions innocuousPerms = new Permissions(); + innocuousPerms.add(modifyThreadPermission); + innocuousPerms.add(new RuntimePermission( + "enableContextClassLoaderOverride")); + innocuousPerms.add(new RuntimePermission( + "modifyThreadGroup")); + innocuousAcc = new AccessControlContext(new ProtectionDomain[] { + new ProtectionDomain(null, innocuousPerms) + }); + } + + public final ForkJoinWorkerThread newThread(ForkJoinPool pool) { + return (ForkJoinWorkerThread.InnocuousForkJoinWorkerThread) + java.security.AccessController.doPrivileged( + new java.security.PrivilegedAction() { + public ForkJoinWorkerThread run() { + return new ForkJoinWorkerThread. + InnocuousForkJoinWorkerThread(pool); + }}, innocuousAcc); + } + } + } diff -r a1ee9743f4ee -r 9a4f43c9b15a jdk/src/share/classes/java/util/concurrent/ForkJoinWorkerThread.java --- a/jdk/src/share/classes/java/util/concurrent/ForkJoinWorkerThread.java Wed Jul 05 19:25:40 2017 +0200 +++ b/jdk/src/share/classes/java/util/concurrent/ForkJoinWorkerThread.java Thu Dec 19 10:31:59 2013 +0000 @@ -35,6 +35,9 @@ package java.util.concurrent; +import java.security.AccessControlContext; +import java.security.ProtectionDomain; + /** * A thread managed by a {@link ForkJoinPool}, which executes * {@link ForkJoinTask}s. @@ -61,6 +64,10 @@ * completes. This leads to a visibility race, that is tolerated * by requiring that the workQueue field is only accessed by the * owning thread. + * + * Support for (non-public) subclass InnocuousForkJoinWorkerThread + * requires that we break quite a lot of encapulation (via Unsafe) + * both here and in the subclass to access and set Thread fields. */ final ForkJoinPool pool; // the pool this thread works in @@ -80,6 +87,18 @@ } /** + * Version for InnocuousForkJoinWorkerThread + */ + ForkJoinWorkerThread(ForkJoinPool pool, ThreadGroup threadGroup, + AccessControlContext acc) { + super(threadGroup, null, "aForkJoinWorkerThread"); + U.putOrderedObject(this, INHERITEDACCESSCONTROLCONTEXT, acc); + eraseThreadLocals(); // clear before registering + this.pool = pool; + this.workQueue = pool.registerWorker(this); + } + + /** * Returns the pool hosting this thread. * * @return the pool @@ -131,21 +150,128 @@ * {@link ForkJoinTask}s. */ public void run() { - Throwable exception = null; - try { - onStart(); - pool.runWorker(workQueue); - } catch (Throwable ex) { - exception = ex; - } finally { + if (workQueue.array == null) { // only run once + Throwable exception = null; try { - onTermination(exception); + onStart(); + pool.runWorker(workQueue); } catch (Throwable ex) { - if (exception == null) - exception = ex; + exception = ex; } finally { - pool.deregisterWorker(this, exception); + try { + onTermination(exception); + } catch (Throwable ex) { + if (exception == null) + exception = ex; + } finally { + pool.deregisterWorker(this, exception); + } } } } + + /** + * Erases ThreadLocals by nulling out Thread maps + */ + final void eraseThreadLocals() { + U.putObject(this, THREADLOCALS, null); + U.putObject(this, INHERITABLETHREADLOCALS, null); + } + + /** + * Non-public hook method for InnocuousForkJoinWorkerThread + */ + void afterTopLevelExec() { + } + + // Set up to allow setting thread fields in constructor + private static final sun.misc.Unsafe U; + private static final long THREADLOCALS; + private static final long INHERITABLETHREADLOCALS; + private static final long INHERITEDACCESSCONTROLCONTEXT; + static { + try { + U = sun.misc.Unsafe.getUnsafe(); + Class tk = Thread.class; + THREADLOCALS = U.objectFieldOffset + (tk.getDeclaredField("threadLocals")); + INHERITABLETHREADLOCALS = U.objectFieldOffset + (tk.getDeclaredField("inheritableThreadLocals")); + INHERITEDACCESSCONTROLCONTEXT = U.objectFieldOffset + (tk.getDeclaredField("inheritedAccessControlContext")); + + } catch (Exception e) { + throw new Error(e); + } + } + + /** + * A worker thread that has no permissions, is not a member of any + * user-defined ThreadGroup, and erases all ThreadLocals after + * running each top-level task. + */ + static final class InnocuousForkJoinWorkerThread extends ForkJoinWorkerThread { + /** The ThreadGroup for all InnocuousForkJoinWorkerThreads */ + private static final ThreadGroup innocuousThreadGroup = + createThreadGroup(); + + /** An AccessControlContext supporting no privileges */ + private static final AccessControlContext INNOCUOUS_ACC = + new AccessControlContext( + new ProtectionDomain[] { + new ProtectionDomain(null, null) + }); + + InnocuousForkJoinWorkerThread(ForkJoinPool pool) { + super(pool, innocuousThreadGroup, INNOCUOUS_ACC); + } + + @Override // to erase ThreadLocals + void afterTopLevelExec() { + eraseThreadLocals(); + } + + @Override // to always report system loader + public ClassLoader getContextClassLoader() { + return ClassLoader.getSystemClassLoader(); + } + + @Override // to silently fail + public void setUncaughtExceptionHandler(UncaughtExceptionHandler x) { } + + @Override // paranoically + public void setContextClassLoader(ClassLoader cl) { + throw new SecurityException("setContextClassLoader"); + } + + /** + * Returns a new group with the system ThreadGroup (the + * topmost, parentless group) as parent. Uses Unsafe to + * traverse Thread group and ThreadGroup parent fields. + */ + private static ThreadGroup createThreadGroup() { + try { + sun.misc.Unsafe u = sun.misc.Unsafe.getUnsafe(); + Class tk = Thread.class; + Class gk = ThreadGroup.class; + long tg = u.objectFieldOffset(tk.getDeclaredField("group")); + long gp = u.objectFieldOffset(gk.getDeclaredField("parent")); + ThreadGroup group = (ThreadGroup) + u.getObject(Thread.currentThread(), tg); + while (group != null) { + ThreadGroup parent = (ThreadGroup)u.getObject(group, gp); + if (parent == null) + return new ThreadGroup(group, + "InnocuousForkJoinWorkerThreadGroup"); + group = parent; + } + } catch (Exception e) { + throw new Error(e); + } + // fall through if null as cannot-happen safeguard + throw new Error("Cannot create ThreadGroup"); + } + } + } +