From: <ls...@us...> - 2009-03-13 20:11:36
|
Revision: 5099 http://jnode.svn.sourceforge.net/jnode/?rev=5099&view=rev Author: lsantha Date: 2009-03-13 20:11:26 +0000 (Fri, 13 Mar 2009) Log Message: ----------- OpenJDK integration. Modified Paths: -------------- trunk/all/conf/openjdk-annotations.properties trunk/core/src/classpath/vm/java/lang/Thread.java Added Paths: ----------- trunk/core/src/openjdk/java/java/lang/InheritableThreadLocal.java trunk/core/src/openjdk/java/java/lang/ThreadLocal.java Removed Paths: ------------- trunk/core/src/classpath/java/java/lang/InheritableThreadLocal.java trunk/core/src/classpath/java/java/lang/ThreadLocal.java Modified: trunk/all/conf/openjdk-annotations.properties =================================================================== --- trunk/all/conf/openjdk-annotations.properties 2009-03-13 20:07:21 UTC (rev 5098) +++ trunk/all/conf/openjdk-annotations.properties 2009-03-13 20:11:26 UTC (rev 5099) @@ -5,16 +5,17 @@ # package/className.class=<annotations list separated by a ','> # supported annotations : SharedStatics, MagicPermission -java/lang/Throwable.class=MagicPermission -java/util/Currency.class=SharedStatics -sun/misc/SharedSecrets.class=SharedStatics -sun/misc/Unsafe.class=SharedStatics java/awt/EventQueue.class=SharedStatics +java/awt/image/DataBuffer.class=SharedStatics java/awt/KeyboardFocusManager.class=SharedStatics java/awt/Toolkit.class=SharedStatics +java/lang/ThreadLocal.class=SharedStatics +java/lang/Throwable.class=MagicPermission +java/util/Currency.class=SharedStatics +java/util/concurrent/locks/LockSupport.class=SharedStatics sun/awt/AppContext.class=SharedStatics sun/awt/AWTAutoShutdown.class=SharedStatics sun/awt/image/SunWritableRaster.class=SharedStatics sun/java2d/Disposer.class=SharedStatics -java/awt/image/DataBuffer.class=SharedStatics -java/util/concurrent/locks/LockSupport.class=SharedStatics +sun/misc/SharedSecrets.class=SharedStatics +sun/misc/Unsafe.class=SharedStatics Deleted: trunk/core/src/classpath/java/java/lang/InheritableThreadLocal.java =================================================================== --- trunk/core/src/classpath/java/java/lang/InheritableThreadLocal.java 2009-03-13 20:07:21 UTC (rev 5098) +++ trunk/core/src/classpath/java/java/lang/InheritableThreadLocal.java 2009-03-13 20:11:26 UTC (rev 5099) @@ -1,119 +0,0 @@ -/* InheritableThreadLocal -- a ThreadLocal which inherits values across threads - Copyright (C) 2000, 2001, 2002, 2003, 2005 Free Software Foundation, Inc. - -This file is part of GNU Classpath. - -GNU Classpath is free software; you can redistribute it and/or modify -it under the terms of the GNU General Public License as published by -the Free Software Foundation; either version 2, or (at your option) -any later version. - -GNU Classpath 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 for more details. - -You should have received a copy of the GNU General Public License -along with GNU Classpath; see the file COPYING. If not, write to the -Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA -02110-1301 USA. - -Linking this library statically or dynamically with other modules is -making a combined work based on this library. Thus, the terms and -conditions of the GNU General Public License cover the whole -combination. - -As a special exception, the copyright holders of this library give you -permission to link this library with independent modules to produce an -executable, regardless of the license terms of these independent -modules, and to copy and distribute the resulting executable under -terms of your choice, provided that you also meet, for each linked -independent module, the terms and conditions of the license of that -module. An independent module is a module which is not derived from -or based on this library. If you modify this library, you may extend -this exception to your version of the library, but you are not -obligated to do so. If you do not wish to do so, delete this -exception statement from your version. */ - -package java.lang; - -import java.util.Iterator; -import java.util.WeakHashMap; - -/** - * A ThreadLocal whose value is inherited by child Threads. The value of the - * InheritableThreadLocal associated with the (parent) Thread is copied to - * the new (child) Thread at the moment of creation. - * - * <p>It is possible to make the value associated with the child Thread a - * function of the value that is associated with the parent Thread by - * overriding the <code>childValue()</code> method. The utility of this class - * is in transferring items like User ID or Transaction ID across threads - * automatically. - * - * @author Mark Wielaard (ma...@kl...) - * @author Eric Blake (eb...@em...) - * @author Tom Tromey (tr...@re...) - * @author Andrew John Hughes (gnu...@me...) - * @see ThreadLocal - * @since 1.2 - * @status updated to 1.4 - */ -public class InheritableThreadLocal<T> extends ThreadLocal<T> -{ - - /** - * Creates a new InheritableThreadLocal that has no values associated - * with it yet. - */ - public InheritableThreadLocal() - { - } - - /** - * Determines the value associated with a newly created child Thread as a - * function of the value associated with the currently executing (parent) - * Thread. The default implementation just returns the parentValue. - * - * @param parentValue the value of this object in the parent thread at - * the moment of creation of the child - * @return the initial value for the child thread - */ - protected Object childValue(Object parentValue) - { - return parentValue; - } - - /** - * Generates the childValues of all <code>InheritableThreadLocal</code>s - * that are in the heritage of the current Thread for the newly created - * childThread. Should be called from the contructor Thread. - * - * @param childThread the newly created thread, to inherit from this thread - * @see Thread#Thread(ThreadGroup, Runnable, String) - */ - static void newChildThread(Thread childThread) - { - // The currentThread is the parent of the new thread. - Thread parentThread = Thread.currentThread(); - if (parentThread.locals != null) - { - Iterator keys = parentThread.locals.keySet().iterator(); - while (keys.hasNext()) - { - ThreadLocal.Key key = (ThreadLocal.Key)keys.next(); - if (key.get() instanceof InheritableThreadLocal) - { - InheritableThreadLocal local = (InheritableThreadLocal)key.get(); - Object parentValue = parentThread.locals.get(key); - Object childValue = local.childValue(parentValue == NULL - ? null : parentValue); - if (childThread.locals == null) - childThread.locals = new WeakHashMap(); - childThread.locals.put(key, (childValue == null - ? NULL : childValue)); - } - } - } - } -} Deleted: trunk/core/src/classpath/java/java/lang/ThreadLocal.java =================================================================== --- trunk/core/src/classpath/java/java/lang/ThreadLocal.java 2009-03-13 20:07:21 UTC (rev 5098) +++ trunk/core/src/classpath/java/java/lang/ThreadLocal.java 2009-03-13 20:11:26 UTC (rev 5099) @@ -1,181 +0,0 @@ -/* ThreadLocal -- a variable with a unique value per thread - Copyright (C) 2000, 2002, 2003 Free Software Foundation, Inc. - -This file is part of GNU Classpath. - -GNU Classpath is free software; you can redistribute it and/or modify -it under the terms of the GNU General Public License as published by -the Free Software Foundation; either version 2, or (at your option) -any later version. - -GNU Classpath 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 for more details. - -You should have received a copy of the GNU General Public License -along with GNU Classpath; see the file COPYING. If not, write to the -Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA -02110-1301 USA. - -Linking this library statically or dynamically with other modules is -making a combined work based on this library. Thus, the terms and -conditions of the GNU General Public License cover the whole -combination. - -As a special exception, the copyright holders of this library give you -permission to link this library with independent modules to produce an -executable, regardless of the license terms of these independent -modules, and to copy and distribute the resulting executable under -terms of your choice, provided that you also meet, for each linked -independent module, the terms and conditions of the license of that -module. An independent module is a module which is not derived from -or based on this library. If you modify this library, you may extend -this exception to your version of the library, but you are not -obligated to do so. If you do not wish to do so, delete this -exception statement from your version. */ - -package java.lang; - -import java.util.Map; - - -/** - * ThreadLocal objects have a different state associated with every - * Thread that accesses them. Every access to the ThreadLocal object - * (through the <code>get()</code> and <code>set()</code> methods) - * only affects the state of the object as seen by the currently - * executing Thread. - * - * <p>The first time a ThreadLocal object is accessed on a particular - * Thread, the state for that Thread's copy of the local variable is set by - * executing the method <code>initialValue()</code>. - * </p> - * - * <p>An example how you can use this: - * </p> - * - * <pre> - * class Connection - * { - * private static ThreadLocal owner = new ThreadLocal() - * { - * public Object initialValue() - * { - * return("nobody"); - * } - * }; - * ... - * } - * </pre> - * - * <p>Now all instances of connection can see who the owner of the currently - * executing Thread is by calling <code>owner.get()</code>. By default any - * Thread would be associated with 'nobody'. But the Connection object could - * offer a method that changes the owner associated with the Thread on - * which the method was called by calling <code>owner.put("somebody")</code>. - * (Such an owner changing method should then be guarded by security checks.) - * </p> - * - * <p>When a Thread is garbage collected all references to values of - * the ThreadLocal objects associated with that Thread are removed. - * </p> - * - * @author Mark Wielaard (ma...@kl...) - * @author Eric Blake (eb...@em...) - * @since 1.2 - * @status updated to 1.5 - */ -public class ThreadLocal<T> -{ - /** - * Placeholder to distinguish between uninitialized and null set by the - * user. Do not expose this to the public. Package visible for use by - * InheritableThreadLocal - */ - static final Object NULL = new Object(); - - /** - * Serves as a key for the Thread.locals WeakHashMap. - * We can't use "this", because a subclass may override equals/hashCode - * and we need to use object identity for the map. - */ - final Key key = new Key(); - - class Key - { - ThreadLocal get() - { - return ThreadLocal.this; - } - } - - /** - * Creates a ThreadLocal object without associating any value to it yet. - */ - public ThreadLocal() - { - } - - /** - * Called once per thread on the first invocation of get(), if set() was - * not already called. The default implementation returns <code>null</code>. - * Often, this method is overridden to create the appropriate initial object - * for the current thread's view of the ThreadLocal. - * - * @return the initial value of the variable in this thread - */ - protected T initialValue() - { - return null; - } - - /** - * Gets the value associated with the ThreadLocal object for the currently - * executing Thread. If this is the first time the current thread has called - * get(), and it has not already called set(), the value is obtained by - * <code>initialValue()</code>. - * - * @return the value of the variable in this thread - */ - public T get() - { - Map map = Thread.getThreadLocals(); - // Note that we don't have to synchronize, as only this thread will - // ever modify the map. - Object value = map.get(key); - if (value == null) - { - value = initialValue(); - map.put(key, value == null ? NULL : value); - } - return value == NULL ? null : (T) value; - } - - /** - * Sets the value associated with the ThreadLocal object for the currently - * executing Thread. This overrides any existing value associated with the - * current Thread and prevents <code>initialValue()</code> from being - * called if this is the first access to this ThreadLocal in this Thread. - * - * @param value the value to set this thread's view of the variable to - */ - public void set(T value) - { - Map map = Thread.getThreadLocals(); - // Note that we don't have to synchronize, as only this thread will - // ever modify the map. - map.put(key, value == null ? NULL : value); - } - - /** - * Removes the value associated with the ThreadLocal object for the - * currently executing Thread. - * @since 1.5 - */ - public void remove() - { - Map map = Thread.getThreadLocals(); - map.remove(this); -} -} Modified: trunk/core/src/classpath/vm/java/lang/Thread.java =================================================================== --- trunk/core/src/classpath/vm/java/lang/Thread.java 2009-03-13 20:07:21 UTC (rev 5098) +++ trunk/core/src/classpath/vm/java/lang/Thread.java 2009-03-13 20:11:26 UTC (rev 5099) @@ -39,7 +39,6 @@ package java.lang; import java.util.Map; -import java.util.WeakHashMap; import java.util.HashMap; @@ -101,7 +100,6 @@ * @see #start() * @see ThreadLocal * @since 1.0 - * @status updated to 1.4 */ @SharedStatics public class Thread implements Runnable @@ -141,15 +139,20 @@ private final Thread parent; /** The default exception handler. */ - private static UncaughtExceptionHandler defaultHandler; + private static UncaughtExceptionHandler defaultHandler; - /** Thread local storage. Package accessible for use by - * InheritableThreadLocal. + /* ThreadLocal values pertaining to this thread. This map is maintained + * by the ThreadLocal class. */ + ThreadLocal.ThreadLocalMap threadLocals = null; + + /* + * InheritableThreadLocal values pertaining to this thread. This map is + * maintained by the InheritableThreadLocal class. */ - WeakHashMap locals; + ThreadLocal.ThreadLocalMap inheritableThreadLocals = null; /** The uncaught exception handler. */ - UncaughtExceptionHandler exceptionHandler; + UncaughtExceptionHandler exceptionHandler; /** * Name and number of threads created. Used only for generating unique names. @@ -158,8 +161,7 @@ */ private static final HashMap<String, Integer> nameMap = new HashMap<String, Integer>(); - private static final JNodePermission GETVMTHREAD_PERM = new JNodePermission( - "getVmThread"); + private static final JNodePermission GETVMTHREAD_PERM = new JNodePermission("getVmThread"); /** * Allocates a new <code>Thread</code> object. This constructor has @@ -345,8 +347,9 @@ this.vmThread = VmProcessor.current().createThread(this); this.vmThread.setPriority(current.getPriority()); this.vmThread.updateName(); - - InheritableThreadLocal.newChildThread(this); // FDy : CLASSPATH patch ? + + if (parent.inheritableThreadLocals != null) + this.inheritableThreadLocals = ThreadLocal.createInheritedMap(parent.inheritableThreadLocals); } /** @@ -396,7 +399,8 @@ this.vmThread.setPriority(current.getPriority()); this.vmThread.updateName(); - InheritableThreadLocal.newChildThread(this); // FDy : CLASSPATH patch ? + if (parent.inheritableThreadLocals != null) + this.inheritableThreadLocals = ThreadLocal.createInheritedMap(parent.inheritableThreadLocals); } /** @@ -431,8 +435,11 @@ this.vmThread = VmProcessor.current().createThread(isolatedStatics, this); this.vmThread.setPriority(this.getPriority()); this.vmThread.updateName(); - - InheritableThreadLocal.newChildThread(this); // FDy : CLASSPATH patch ? + + //todo review it: should thread locals be inherited accorss isolates? Probably not... + ThreadLocal.ThreadLocalMap parentLocals = currentThread().inheritableThreadLocals; + if (parentLocals != null) + this.inheritableThreadLocals = ThreadLocal.createInheritedMap(parentLocals); } /** @@ -1067,19 +1074,6 @@ } /** - * Returns the map used by ThreadLocal to store the thread local values. - */ - static Map getThreadLocals() { - Thread thread = currentThread(); - Map locals = thread.locals; - if (locals == null) - { - locals = thread.locals = new WeakHashMap(); - } - return locals; - } - - /** * Assigns the given <code>UncaughtExceptionHandler</code> to this * thread. This will then be called if the thread terminates due * to an uncaught exception, pre-empting that of the @@ -1213,6 +1207,8 @@ public final void onExit() { if (vmThread.isStopping()) { group.remove(this); + threadLocals = null; + inheritableThreadLocals = null; } } @@ -1466,5 +1462,4 @@ // blocker = b; // } } - } Added: trunk/core/src/openjdk/java/java/lang/InheritableThreadLocal.java =================================================================== --- trunk/core/src/openjdk/java/java/lang/InheritableThreadLocal.java (rev 0) +++ trunk/core/src/openjdk/java/java/lang/InheritableThreadLocal.java 2009-03-13 20:11:26 UTC (rev 5099) @@ -0,0 +1,84 @@ +/* + * Copyright 1998-2004 Sun Microsystems, Inc. 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. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + */ + +package java.lang; +import java.lang.ref.*; + +/** + * This class extends <tt>ThreadLocal</tt> to provide inheritance of values + * from parent thread to child thread: when a child thread is created, the + * child receives initial values for all inheritable thread-local variables + * for which the parent has values. Normally the child's values will be + * identical to the parent's; however, the child's value can be made an + * arbitrary function of the parent's by overriding the <tt>childValue</tt> + * method in this class. + * + * <p>Inheritable thread-local variables are used in preference to + * ordinary thread-local variables when the per-thread-attribute being + * maintained in the variable (e.g., User ID, Transaction ID) must be + * automatically transmitted to any child threads that are created. + * + * @author Josh Bloch and Doug Lea + * @see ThreadLocal + * @since 1.2 + */ + +public class InheritableThreadLocal<T> extends ThreadLocal<T> { + /** + * Computes the child's initial value for this inheritable thread-local + * variable as a function of the parent's value at the time the child + * thread is created. This method is called from within the parent + * thread before the child is started. + * <p> + * This method merely returns its input argument, and should be overridden + * if a different behavior is desired. + * + * @param parentValue the parent thread's value + * @return the child thread's initial value + */ + protected T childValue(T parentValue) { + return parentValue; + } + + /** + * Get the map associated with a ThreadLocal. + * + * @param t the current thread + */ + ThreadLocalMap getMap(Thread t) { + return t.inheritableThreadLocals; + } + + /** + * Create the map associated with a ThreadLocal. + * + * @param t the current thread + * @param firstValue value for the initial entry of the table. + * @param map the map to store. + */ + void createMap(Thread t, T firstValue) { + t.inheritableThreadLocals = new ThreadLocalMap(this, firstValue); + } +} Added: trunk/core/src/openjdk/java/java/lang/ThreadLocal.java =================================================================== --- trunk/core/src/openjdk/java/java/lang/ThreadLocal.java (rev 0) +++ trunk/core/src/openjdk/java/java/lang/ThreadLocal.java 2009-03-13 20:11:26 UTC (rev 5099) @@ -0,0 +1,697 @@ +/* + * Copyright 1997-2007 Sun Microsystems, Inc. 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. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + */ + +package java.lang; +import java.lang.ref.*; +import java.util.concurrent.atomic.AtomicInteger; +import java.util.concurrent.atomic.AtomicInteger; + +/** + * This class provides thread-local variables. These variables differ from + * their normal counterparts in that each thread that accesses one (via its + * <tt>get</tt> or <tt>set</tt> method) has its own, independently initialized + * copy of the variable. <tt>ThreadLocal</tt> instances are typically private + * static fields in classes that wish to associate state with a thread (e.g., + * a user ID or Transaction ID). + * + * <p>For example, the class below generates unique identifiers local to each + * thread. + * A thread's id is assigned the first time it invokes <tt>ThreadId.get()</tt> + * and remains unchanged on subsequent calls. + * <pre> + * import java.util.concurrent.atomic.AtomicInteger; + * + * public class ThreadId { + * // Atomic integer containing the next thread ID to be assigned + * private static final AtomicInteger nextId = new AtomicInteger(0); + * + * // Thread local variable containing each thread's ID + * private static final ThreadLocal<Integer> threadId = + * new ThreadLocal<Integer>() { + * @Override protected Integer initialValue() { + * return nextId.getAndIncrement(); + * } + * }; + * + * // Returns the current thread's unique ID, assigning it if necessary + * public static int get() { + * return threadId.get(); + * } + * } + * </pre> + * <p>Each thread holds an implicit reference to its copy of a thread-local + * variable as long as the thread is alive and the <tt>ThreadLocal</tt> + * instance is accessible; after a thread goes away, all of its copies of + * thread-local instances are subject to garbage collection (unless other + * references to these copies exist). + * + * @author Josh Bloch and Doug Lea + * @since 1.2 + */ +public class ThreadLocal<T> { + /** + * ThreadLocals rely on per-thread linear-probe hash maps attached + * to each thread (Thread.threadLocals and + * inheritableThreadLocals). The ThreadLocal objects act as keys, + * searched via threadLocalHashCode. This is a custom hash code + * (useful only within ThreadLocalMaps) that eliminates collisions + * in the common case where consecutively constructed ThreadLocals + * are used by the same threads, while remaining well-behaved in + * less common cases. + */ + private final int threadLocalHashCode = nextHashCode(); + + /** + * The next hash code to be given out. Updated atomically. Starts at + * zero. + */ +//jnode todo: unsing AtomicInteger here breaks the boot process, why? +// private static AtomicInteger nextHashCode = +// new AtomicInteger(); + private static Integer nextHashCode = 0; + + /** + * The difference between successively generated hash codes - turns + * implicit sequential thread-local IDs into near-optimally spread + * multiplicative hash values for power-of-two-sized tables. + */ + private static final int HASH_INCREMENT = 0x61c88647; + + /** + * Returns the next hash code. + */ + private static int nextHashCode() { +// return nextHashCode.getAndAdd(HASH_INCREMENT); + return nextHashCode += HASH_INCREMENT; + } + + /** + * Returns the current thread's "initial value" for this + * thread-local variable. This method will be invoked the first + * time a thread accesses the variable with the {@link #get} + * method, unless the thread previously invoked the {@link #set} + * method, in which case the <tt>initialValue</tt> method will not + * be invoked for the thread. Normally, this method is invoked at + * most once per thread, but it may be invoked again in case of + * subsequent invocations of {@link #remove} followed by {@link #get}. + * + * <p>This implementation simply returns <tt>null</tt>; if the + * programmer desires thread-local variables to have an initial + * value other than <tt>null</tt>, <tt>ThreadLocal</tt> must be + * subclassed, and this method overridden. Typically, an + * anonymous inner class will be used. + * + * @return the initial value for this thread-local + */ + protected T initialValue() { + return null; + } + + /** + * Creates a thread local variable. + */ + public ThreadLocal() { + } + + /** + * Returns the value in the current thread's copy of this + * thread-local variable. If the variable has no value for the + * current thread, it is first initialized to the value returned + * by an invocation of the {@link #initialValue} method. + * + * @return the current thread's value of this thread-local + */ + public T get() { + Thread t = Thread.currentThread(); + ThreadLocalMap map = getMap(t); + if (map != null) { + ThreadLocalMap.Entry e = map.getEntry(this); + if (e != null) + return (T)e.value; + } + return setInitialValue(); + } + + /** + * Variant of set() to establish initialValue. Used instead + * of set() in case user has overridden the set() method. + * + * @return the initial value + */ + private T setInitialValue() { + T value = initialValue(); + Thread t = Thread.currentThread(); + ThreadLocalMap map = getMap(t); + if (map != null) + map.set(this, value); + else + createMap(t, value); + return value; + } + + /** + * Sets the current thread's copy of this thread-local variable + * to the specified value. Most subclasses will have no need to + * override this method, relying solely on the {@link #initialValue} + * method to set the values of thread-locals. + * + * @param value the value to be stored in the current thread's copy of + * this thread-local. + */ + public void set(T value) { + Thread t = Thread.currentThread(); + ThreadLocalMap map = getMap(t); + if (map != null) + map.set(this, value); + else + createMap(t, value); + } + + /** + * Removes the current thread's value for this thread-local + * variable. If this thread-local variable is subsequently + * {@linkplain #get read} by the current thread, its value will be + * reinitialized by invoking its {@link #initialValue} method, + * unless its value is {@linkplain #set set} by the current thread + * in the interim. This may result in multiple invocations of the + * <tt>initialValue</tt> method in the current thread. + * + * @since 1.5 + */ + public void remove() { + ThreadLocalMap m = getMap(Thread.currentThread()); + if (m != null) + m.remove(this); + } + + /** + * Get the map associated with a ThreadLocal. Overridden in + * InheritableThreadLocal. + * + * @param t the current thread + * @return the map + */ + ThreadLocalMap getMap(Thread t) { + return t.threadLocals; + } + + /** + * Create the map associated with a ThreadLocal. Overridden in + * InheritableThreadLocal. + * + * @param t the current thread + * @param firstValue value for the initial entry of the map + * @param map the map to store. + */ + void createMap(Thread t, T firstValue) { + t.threadLocals = new ThreadLocalMap(this, firstValue); + } + + /** + * Factory method to create map of inherited thread locals. + * Designed to be called only from Thread constructor. + * + * @param parentMap the map associated with parent thread + * @return a map containing the parent's inheritable bindings + */ + static ThreadLocalMap createInheritedMap(ThreadLocalMap parentMap) { + return new ThreadLocalMap(parentMap); + } + + /** + * Method childValue is visibly defined in subclass + * InheritableThreadLocal, but is internally defined here for the + * sake of providing createInheritedMap factory method without + * needing to subclass the map class in InheritableThreadLocal. + * This technique is preferable to the alternative of embedding + * instanceof tests in methods. + */ + T childValue(T parentValue) { + throw new UnsupportedOperationException(); + } + + /** + * ThreadLocalMap is a customized hash map suitable only for + * maintaining thread local values. No operations are exported + * outside of the ThreadLocal class. The class is package private to + * allow declaration of fields in class Thread. To help deal with + * very large and long-lived usages, the hash table entries use + * WeakReferences for keys. However, since reference queues are not + * used, stale entries are guaranteed to be removed only when + * the table starts running out of space. + */ + static class ThreadLocalMap { + + /** + * The entries in this hash map extend WeakReference, using + * its main ref field as the key (which is always a + * ThreadLocal object). Note that null keys (i.e. entry.get() + * == null) mean that the key is no longer referenced, so the + * entry can be expunged from table. Such entries are referred to + * as "stale entries" in the code that follows. + */ + static class Entry { //jnode + /** The value associated with this ThreadLocal. */ + Object value; + ThreadLocal k; + + public ThreadLocal get() { + return k; + } + + public void clear() { + k = null; + } + + Entry(ThreadLocal k, Object v) { + this.k = k; + value = v; + } + } + + /** + * The initial capacity -- MUST be a power of two. + */ + private static final int INITIAL_CAPACITY = 16; + + /** + * The table, resized as necessary. + * table.length MUST always be a power of two. + */ + private Entry[] table; + + /** + * The number of entries in the table. + */ + private int size = 0; + + /** + * The next size value at which to resize. + */ + private int threshold; // Default to 0 + + /** + * Set the resize threshold to maintain at worst a 2/3 load factor. + */ + private void setThreshold(int len) { + threshold = len * 2 / 3; + } + + /** + * Increment i modulo len. + */ + private static int nextIndex(int i, int len) { + return ((i + 1 < len) ? i + 1 : 0); + } + + /** + * Decrement i modulo len. + */ + private static int prevIndex(int i, int len) { + return ((i - 1 >= 0) ? i - 1 : len - 1); + } + + /** + * Construct a new map initially containing (firstKey, firstValue). + * ThreadLocalMaps are constructed lazily, so we only create + * one when we have at least one entry to put in it. + */ + ThreadLocalMap(ThreadLocal firstKey, Object firstValue) { + table = new Entry[INITIAL_CAPACITY]; + int i = firstKey.threadLocalHashCode & (INITIAL_CAPACITY - 1); + table[i] = new Entry(firstKey, firstValue); + size = 1; + setThreshold(INITIAL_CAPACITY); + } + + /** + * Construct a new map including all Inheritable ThreadLocals + * from given parent map. Called only by createInheritedMap. + * + * @param parentMap the map associated with parent thread. + */ + private ThreadLocalMap(ThreadLocalMap parentMap) { + Entry[] parentTable = parentMap.table; + int len = parentTable.length; + setThreshold(len); + table = new Entry[len]; + + for (int j = 0; j < len; j++) { + Entry e = parentTable[j]; + if (e != null) { + ThreadLocal key = e.get(); + if (key != null) { + Object value = key.childValue(e.value); + Entry c = new Entry(key, value); + int h = key.threadLocalHashCode & (len - 1); + while (table[h] != null) + h = nextIndex(h, len); + table[h] = c; + size++; + } + } + } + } + + /** + * Get the entry associated with key. This method + * itself handles only the fast path: a direct hit of existing + * key. It otherwise relays to getEntryAfterMiss. This is + * designed to maximize performance for direct hits, in part + * by making this method readily inlinable. + * + * @param key the thread local object + * @return the entry associated with key, or null if no such + */ + private Entry getEntry(ThreadLocal key) { + int i = key.threadLocalHashCode & (table.length - 1); + Entry e = table[i]; + if (e != null && e.get() == key) + return e; + else + return getEntryAfterMiss(key, i, e); + } + + /** + * Version of getEntry method for use when key is not found in + * its direct hash slot. + * + * @param key the thread local object + * @param i the table index for key's hash code + * @param e the entry at table[i] + * @return the entry associated with key, or null if no such + */ + private Entry getEntryAfterMiss(ThreadLocal key, int i, Entry e) { + Entry[] tab = table; + int len = tab.length; + + while (e != null) { + ThreadLocal k = e.get(); + if (k == key) + return e; + if (k == null) + expungeStaleEntry(i); + else + i = nextIndex(i, len); + e = tab[i]; + } + return null; + } + + /** + * Set the value associated with key. + * + * @param key the thread local object + * @param value the value to be set + */ + private void set(ThreadLocal key, Object value) { + + // We don't use a fast path as with get() because it is at + // least as common to use set() to create new entries as + // it is to replace existing ones, in which case, a fast + // path would fail more often than not. + + Entry[] tab = table; + int len = tab.length; + int i = key.threadLocalHashCode & (len-1); + + for (Entry e = tab[i]; + e != null; + e = tab[i = nextIndex(i, len)]) { + ThreadLocal k = e.get(); + + if (k == key) { + e.value = value; + return; + } + + if (k == null) { + replaceStaleEntry(key, value, i); + return; + } + } + + tab[i] = new Entry(key, value); + int sz = ++size; + if (!cleanSomeSlots(i, sz) && sz >= threshold) + rehash(); + } + + /** + * Remove the entry for key. + */ + private void remove(ThreadLocal key) { + Entry[] tab = table; + int len = tab.length; + int i = key.threadLocalHashCode & (len-1); + for (Entry e = tab[i]; + e != null; + e = tab[i = nextIndex(i, len)]) { + if (e.get() == key) { + e.clear(); + expungeStaleEntry(i); + return; + } + } + } + + /** + * Replace a stale entry encountered during a set operation + * with an entry for the specified key. The value passed in + * the value parameter is stored in the entry, whether or not + * an entry already exists for the specified key. + * + * As a side effect, this method expunges all stale entries in the + * "run" containing the stale entry. (A run is a sequence of entries + * between two null slots.) + * + * @param key the key + * @param value the value to be associated with key + * @param staleSlot index of the first stale entry encountered while + * searching for key. + */ + private void replaceStaleEntry(ThreadLocal key, Object value, + int staleSlot) { + Entry[] tab = table; + int len = tab.length; + Entry e; + + // Back up to check for prior stale entry in current run. + // We clean out whole runs at a time to avoid continual + // incremental rehashing due to garbage collector freeing + // up refs in bunches (i.e., whenever the collector runs). + int slotToExpunge = staleSlot; + for (int i = prevIndex(staleSlot, len); + (e = tab[i]) != null; + i = prevIndex(i, len)) + if (e.get() == null) + slotToExpunge = i; + + // Find either the key or trailing null slot of run, whichever + // occurs first + for (int i = nextIndex(staleSlot, len); + (e = tab[i]) != null; + i = nextIndex(i, len)) { + ThreadLocal k = e.get(); + + // If we find key, then we need to swap it + // with the stale entry to maintain hash table order. + // The newly stale slot, or any other stale slot + // encountered above it, can then be sent to expungeStaleEntry + // to remove or rehash all of the other entries in run. + if (k == key) { + e.value = value; + + tab[i] = tab[staleSlot]; + tab[staleSlot] = e; + + // Start expunge at preceding stale entry if it exists + if (slotToExpunge == staleSlot) + slotToExpunge = i; + cleanSomeSlots(expungeStaleEntry(slotToExpunge), len); + return; + } + + // If we didn't find stale entry on backward scan, the + // first stale entry seen while scanning for key is the + // first still present in the run. + if (k == null && slotToExpunge == staleSlot) + slotToExpunge = i; + } + + // If key not found, put new entry in stale slot + tab[staleSlot].value = null; + tab[staleSlot] = new Entry(key, value); + + // If there are any other stale entries in run, expunge them + if (slotToExpunge != staleSlot) + cleanSomeSlots(expungeStaleEntry(slotToExpunge), len); + } + + /** + * Expunge a stale entry by rehashing any possibly colliding entries + * lying between staleSlot and the next null slot. This also expunges + * any other stale entries encountered before the trailing null. See + * Knuth, Section 6.4 + * + * @param staleSlot index of slot known to have null key + * @return the index of the next null slot after staleSlot + * (all between staleSlot and this slot will have been checked + * for expunging). + */ + private int expungeStaleEntry(int staleSlot) { + Entry[] tab = table; + int len = tab.length; + + // expunge entry at staleSlot + tab[staleSlot].value = null; + tab[staleSlot] = null; + size--; + + // Rehash until we encounter null + Entry e; + int i; + for (i = nextIndex(staleSlot, len); + (e = tab[i]) != null; + i = nextIndex(i, len)) { + ThreadLocal k = e.get(); + if (k == null) { + e.value = null; + tab[i] = null; + size--; + } else { + int h = k.threadLocalHashCode & (len - 1); + if (h != i) { + tab[i] = null; + + // Unlike Knuth 6.4 Algorithm R, we must scan until + // null because multiple entries could have been stale. + while (tab[h] != null) + h = nextIndex(h, len); + tab[h] = e; + } + } + } + return i; + } + + /** + * Heuristically scan some cells looking for stale entries. + * This is invoked when either a new element is added, or + * another stale one has been expunged. It performs a + * logarithmic number of scans, as a balance between no + * scanning (fast but retains garbage) and a number of scans + * proportional to number of elements, that would find all + * garbage but would cause some insertions to take O(n) time. + * + * @param i a position known NOT to hold a stale entry. The + * scan starts at the element after i. + * + * @param n scan control: <tt>log2(n)</tt> cells are scanned, + * unless a stale entry is found, in which case + * <tt>log2(table.length)-1</tt> additional cells are scanned. + * When called from insertions, this parameter is the number + * of elements, but when from replaceStaleEntry, it is the + * table length. (Note: all this could be changed to be either + * more or less aggressive by weighting n instead of just + * using straight log n. But this version is simple, fast, and + * seems to work well.) + * + * @return true if any stale entries have been removed. + */ + private boolean cleanSomeSlots(int i, int n) { + boolean removed = false; + Entry[] tab = table; + int len = tab.length; + do { + i = nextIndex(i, len); + Entry e = tab[i]; + if (e != null && e.get() == null) { + n = len; + removed = true; + i = expungeStaleEntry(i); + } + } while ( (n >>>= 1) != 0); + return removed; + } + + /** + * Re-pack and/or re-size the table. First scan the entire + * table removing stale entries. If this doesn't sufficiently + * shrink the size of the table, double the table size. + */ + private void rehash() { + expungeStaleEntries(); + + // Use lower threshold for doubling to avoid hysteresis + if (size >= threshold - threshold / 4) + resize(); + } + + /** + * Double the capacity of the table. + */ + private void resize() { + Entry[] oldTab = table; + int oldLen = oldTab.length; + int newLen = oldLen * 2; + Entry[] newTab = new Entry[newLen]; + int count = 0; + + for (int j = 0; j < oldLen; ++j) { + Entry e = oldTab[j]; + if (e != null) { + ThreadLocal k = e.get(); + if (k == null) { + e.value = null; // Help the GC + } else { + int h = k.threadLocalHashCode & (newLen - 1); + while (newTab[h] != null) + h = nextIndex(h, newLen); + newTab[h] = e; + count++; + } + } + } + + setThreshold(newLen); + size = count; + table = newTab; + } + + /** + * Expunge all stale entries in the table. + */ + private void expungeStaleEntries() { + Entry[] tab = table; + int len = tab.length; + for (int j = 0; j < len; j++) { + Entry e = tab[j]; + if (e != null && e.get() == null) + expungeStaleEntry(j); + } + } + } +} This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |