You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@harmony.apache.org by te...@apache.org on 2008/06/26 11:44:31 UTC
svn commit: r671836 - in /harmony/enhanced:
classlib/trunk/modules/luni/src/main/java/java/lang/
drlvm/trunk/vm/vmcore/src/kernel_classes/javasrc/java/lang/
Author: tellison
Date: Thu Jun 26 02:44:31 2008
New Revision: 671836
URL: http://svn.apache.org/viewvc?rev=671836&view=rev
Log:
Apply modified version of 'cl-vm-threadlocal-rc1.patch' for
HARMONY-5703 ([drlvm][performance] j.l.ThreadLocal performance optimization).
Moved ThreadLocal into DRLVM's kernel classes since it has a new contract with Thread that won't work with the IBM VME implementation.
Added:
harmony/enhanced/drlvm/trunk/vm/vmcore/src/kernel_classes/javasrc/java/lang/ThreadLocal.java (with props)
Modified:
harmony/enhanced/classlib/trunk/modules/luni/src/main/java/java/lang/ThreadLocal.java
harmony/enhanced/drlvm/trunk/vm/vmcore/src/kernel_classes/javasrc/java/lang/Thread.java
Modified: harmony/enhanced/classlib/trunk/modules/luni/src/main/java/java/lang/ThreadLocal.java
URL: http://svn.apache.org/viewvc/harmony/enhanced/classlib/trunk/modules/luni/src/main/java/java/lang/ThreadLocal.java?rev=671836&r1=671835&r2=671836&view=diff
==============================================================================
--- harmony/enhanced/classlib/trunk/modules/luni/src/main/java/java/lang/ThreadLocal.java (original)
+++ harmony/enhanced/classlib/trunk/modules/luni/src/main/java/java/lang/ThreadLocal.java Thu Jun 26 02:44:31 2008
@@ -17,6 +17,12 @@
package java.lang;
+/*
+ * Note that the Harmony VM "DRLVM" uses a different implementation of ThreadLocal.
+ * See DRLVM's classes here:
+ * http://svn.apache.org/viewvc/harmony/enhanced/drlvm/trunk/vm/vmcore/src/kernel_classes/
+ */
+
/**
* A ThreadLocal is a variable that has a per-thread value. Different Threads
* may reference the same ThreadLocal object, but the values they observe will
Modified: harmony/enhanced/drlvm/trunk/vm/vmcore/src/kernel_classes/javasrc/java/lang/Thread.java
URL: http://svn.apache.org/viewvc/harmony/enhanced/drlvm/trunk/vm/vmcore/src/kernel_classes/javasrc/java/lang/Thread.java?rev=671836&r1=671835&r2=671836&view=diff
==============================================================================
--- harmony/enhanced/drlvm/trunk/vm/vmcore/src/kernel_classes/javasrc/java/lang/Thread.java (original)
+++ harmony/enhanced/drlvm/trunk/vm/vmcore/src/kernel_classes/javasrc/java/lang/Thread.java Thu Jun 26 02:44:31 2008
@@ -103,12 +103,6 @@
*/
private Runnable target;
- /**
- * This map is used to provide <code>ThreadLocal</code> functionality.
- * Maps <code>ThreadLocal</code> object to value. Lazy initialization is
- * used to avoid circular dependance.
- */
- private Map<ThreadLocal<Object>, Object> localValues = null;
/**
* Uncaught exception handler for this thread
@@ -160,6 +154,13 @@
* references.
*/
private static final int GC_WATERMARK_MAX_COUNT = 700;
+
+ /*
+ * ThreadLocal values: local and inheritable
+ *
+ */
+ ThreadLocal.Values localValues;
+ ThreadLocal.Values inheritableValues;
/**
* @com.intel.drl.spec_ref
@@ -251,6 +252,12 @@
SecurityUtils.putContext(this, AccessController.getContext());
// adding the thread to the thread group should be the last action
group.add(this);
+
+ Thread parent = currentThread();
+ if (parent != null && parent.inheritableValues != null) {
+ inheritableValues = new ThreadLocal.Values(parent.inheritableValues);
+ }
+
}
/**
@@ -288,7 +295,7 @@
this.name = (name != THREAD) ? this.name = name.toString() :
THREAD + threadId;
- initializeInheritableLocalValues(currentThread);
+
checkGCWatermark();
@@ -304,6 +311,12 @@
SecurityUtils.putContext(this, AccessController.getContext());
checkAccess();
+
+ Thread parent = currentThread();
+ if (parent != null && parent.inheritableValues != null) {
+ inheritableValues = new ThreadLocal.Values(parent.inheritableValues);
+ }
+
}
/**
@@ -933,86 +946,6 @@
}
/**
- * Associates the value specified to the <code>ThreadLocal</code> object
- * given. <br>
- * This nethod is designed to provide <code>ThreadLocal</code>
- * functionality.
- */
- void setThreadLocal(ThreadLocal<Object> local, Object value) {
- if (localValues == null) {
- localValues = new IdentityHashMap<ThreadLocal<Object>, Object>();
- }
- localValues.put(local, value);
- }
-
- /**
- * Returns the value associated with the <code>ThreadLocal</code> object
- * specified. If no value is associated, returns the value produced by
- * <code>initialValue()</code> method called for this object and
- * associates this value to <code>ThreadLocal</code> object. <br>
- * This nethod is designed to provide <code>ThreadLocal</code>
- * functionality.
- */
- Object getThreadLocal(ThreadLocal<Object> local) {
- Object value;
- if (localValues == null) {
- localValues = new IdentityHashMap<ThreadLocal<Object>, Object>();
- value = local.initialValue();
- localValues.put(local, value);
- return value;
- }
- value = localValues.get(local);
- if (value != null) {
- return value;
- } else {
- if (localValues.containsKey(local)) {
- return null;
- } else {
- value = local.initialValue();
- localValues.put(local, value);
- return value;
- }
- }
- }
-
- /**
- * Removes the association (if any) between the <code>ThreadLocal</code> object
- * given and this thread's value. <br>
- * This nethod is designed to provide <code>ThreadLocal</code>
- * functionality.
- */
- void removeLocalValue(ThreadLocal<Object> local) {
- if (localValues != null) {
- localValues.remove(local);
- }
- }
-
- /**
- * Initializes local values represented by
- * <code>InheritableThreadLocal</code> objects having local values for the
- * parent thread <br>
- * This method is designed to provide the functionality of
- * <code>InheritableThreadLocal</code> class <br>
- * This method should be called from <code>Thread</code>'s constructor.
- */
- private void initializeInheritableLocalValues(Thread parent)
- {
- Map<ThreadLocal<Object>, Object> parentLocalValues = parent.localValues;
- if (parentLocalValues == null) {
- return;
- }
- localValues = new IdentityHashMap<ThreadLocal<Object>, Object>(parentLocalValues.size());
- for (Iterator<ThreadLocal<Object>> it = parentLocalValues.keySet().iterator(); it.hasNext();) {
- ThreadLocal<Object> local = it.next();
- if (local instanceof InheritableThreadLocal) {
- Object parentValue = parentLocalValues.get(local);
- InheritableThreadLocal<Object> iLocal = (InheritableThreadLocal<Object>) local;
- localValues.put(local, iLocal.childValue(parentValue));
- }
- }
- }
-
- /**
* generates a unique thread ID
*/
private static synchronized long getNextThreadId()
Added: harmony/enhanced/drlvm/trunk/vm/vmcore/src/kernel_classes/javasrc/java/lang/ThreadLocal.java
URL: http://svn.apache.org/viewvc/harmony/enhanced/drlvm/trunk/vm/vmcore/src/kernel_classes/javasrc/java/lang/ThreadLocal.java?rev=671836&view=auto
==============================================================================
--- harmony/enhanced/drlvm/trunk/vm/vmcore/src/kernel_classes/javasrc/java/lang/ThreadLocal.java (added)
+++ harmony/enhanced/drlvm/trunk/vm/vmcore/src/kernel_classes/javasrc/java/lang/ThreadLocal.java Thu Jun 26 02:44:31 2008
@@ -0,0 +1,524 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package java.lang;
+
+import java.lang.ref.WeakReference;
+import java.lang.ref.Reference;
+import java.util.concurrent.atomic.AtomicInteger;
+
+/**
+ * A variable for which each thread has its own value. Supports {@code null}
+ * values.
+ *
+ * @see java.lang.Thread
+ * @author Bob Lee
+ */
+public class ThreadLocal<T> {
+
+ /* Thanks to Josh Bloch and Doug Lea for code reviews and impl advice. */
+
+ /**
+ * Creates a new thread local variable.
+ */
+ public ThreadLocal() {}
+
+ /**
+ * Returns the value of this variable for the current thread. If an entry
+ * doesn't yet exist for this variable on this thread, this method will
+ * create an entry, populating the value with the result of
+ * {@link #initialValue()}.
+ */
+ @SuppressWarnings("unchecked")
+ public T get() {
+ // Optimized for the fast path.
+ Thread currentThread = Thread.currentThread();
+ Values values = values(currentThread);
+ if (values != null) {
+ Object[] table = values.table;
+ int index = hash & values.mask;
+ if (this.reference == table[index]) {
+ return (T) table[index + 1];
+ }
+ } else {
+ values = initializeValues(currentThread);
+ }
+
+ return (T) values.getAfterMiss(this);
+ }
+
+ /**
+ * Provides the initial value of this variable for the current thread.
+ * The default implementation returns {@code null}.
+ */
+ protected T initialValue() {
+ return null;
+ }
+
+ /**
+ * Sets the value of this variable for the current thread. If set to
+ * null, the value will be set to null and the underlying entry will still
+ * be present.
+ */
+ public void set(T value) {
+ Thread currentThread = Thread.currentThread();
+ Values values = values(currentThread);
+ if (values == null) {
+ values = initializeValues(currentThread);
+ }
+ values.put(this, value);
+ }
+
+ /**
+ * Removes the entry for this variable in the current thread. If this call
+ * is followed by a {@link #get()} before a {@link #set(Object)},
+ * {@code #get()} will call {@link #initialValue()} and create a new
+ * entry with the resulting value.
+ */
+ public void remove() {
+ Thread currentThread = Thread.currentThread();
+ Values values = values(currentThread);
+ if (values != null) {
+ values.remove(this);
+ }
+ }
+
+ /**
+ * Creates Values instance for this thread and variable type.
+ */
+ Values initializeValues(Thread current) {
+ return current.localValues = new Values();
+ }
+
+ /**
+ * Gets Values instance for this thread and variable type.
+ */
+ Values values(Thread current) {
+ return current.localValues;
+ }
+
+ /** Weak reference to this thread local instance. */
+ private final Reference<ThreadLocal<T>> reference
+ = new WeakReference<ThreadLocal<T>>(this);
+
+ /** Hash counter. */
+ private static AtomicInteger hashCounter = new AtomicInteger(0);
+
+ /**
+ * Internal hash. We deliberately don't bother with #hashCode().
+ * Hashes must be even. This ensures that the result of
+ * (hash & (table.length - 1)) points to a key and not a value.
+ *
+ * We increment by Doug Lea's Magic Number(TM) (*2 since keys are in
+ * every other bucket) to help prevent clustering.
+ */
+ private final int hash = hashCounter.getAndAdd(0x61c88647 << 1);
+
+ /**
+ * Per-thread map of ThreadLocal instances to values.
+ */
+ static class Values {
+
+ /**
+ * Size must always be a power of 2.
+ */
+ private static final int INITIAL_SIZE = 16;
+
+ /**
+ * Placeholder for deleted entries.
+ */
+ private static final Object TOMBSTONE = new Object();
+
+ /**
+ * Map entries. Contains alternating keys (ThreadLocal) and values.
+ * The length is always a power of 2.
+ */
+ private Object[] table;
+
+ /** Used to turn hashes into indices. */
+ private int mask;
+
+ /** Number of live entries. */
+ private int size;
+
+ /** Number of tombstones. */
+ private int tombstones;
+
+ /** Maximum number of live entries and tombstones. */
+ private int maximumLoad;
+
+ /** Points to the next cell to clean up. */
+ private int clean;
+
+ /**
+ * Constructs a new, empty instance.
+ */
+ Values() {
+ initializeTable(INITIAL_SIZE);
+ this.size = 0;
+ this.tombstones = 0;
+ }
+
+ /**
+ * Used for InheritableThreadLocals.
+ */
+ Values(Values fromParent) {
+ this.table = fromParent.table.clone();
+ this.mask = fromParent.mask;
+ this.size = fromParent.size;
+ this.tombstones = fromParent.tombstones;
+ this.maximumLoad = fromParent.maximumLoad;
+ this.clean = fromParent.clean;
+ inheritValues(fromParent);
+ }
+
+ /**
+ * Inherits values from a parent thread.
+ */
+ @SuppressWarnings({"unchecked"})
+ private void inheritValues(Values fromParent) {
+ // Transfer values from parent to child thread.
+ Object[] table = this.table;
+ for (int i = table.length - 2; i >= 0; i -= 2) {
+ Object k = table[i];
+
+ if (k == null || k == TOMBSTONE) {
+ // Skip this entry.
+ continue;
+ }
+
+ // The table can only contain null, tombstones and references.
+ Reference<InheritableThreadLocal<?>> reference
+ = (Reference<InheritableThreadLocal<?>>) k;
+ // Raw type enables us to pass in an Object below.
+ InheritableThreadLocal key = reference.get();
+ if (key != null) {
+ // Replace value with filtered value.
+ // We should just let exceptions bubble out and tank
+ // the thread creation
+ table[i + 1] = key.childValue(fromParent.table[i + 1]);
+ } else {
+ // The key was reclaimed.
+ table[i] = TOMBSTONE;
+ table[i + 1] = null;
+ fromParent.table[i] = TOMBSTONE;
+ fromParent.table[i + 1] = null;
+
+ tombstones++;
+ fromParent.tombstones++;
+
+ size--;
+ fromParent.size--;
+ }
+ }
+ }
+
+ /**
+ * Creates a new, empty table with the given capacity.
+ */
+ private void initializeTable(int capacity) {
+ this.table = new Object[capacity << 1];
+ this.mask = table.length - 1;
+ this.clean = 0;
+ this.maximumLoad = capacity * 2 / 3; // 2/3
+ }
+
+ /**
+ * Cleans up after garbage-collected thread locals.
+ */
+ private void cleanUp() {
+ if (rehash()) {
+ // If we rehashed, we needn't clean up (clean up happens as
+ // a side effect).
+ return;
+ }
+
+ if (size == 0) {
+ // No live entries == nothing to clean.
+ return;
+ }
+
+ // Clean log(table.length) entries picking up where we left off
+ // last time.
+ int index = clean;
+ Object[] table = this.table;
+ for (int counter = table.length; counter > 0; counter >>= 1,
+ index = next(index)) {
+ Object k = table[index];
+
+ if (k == TOMBSTONE || k == null) {
+ continue; // on to next entry
+ }
+
+ // The table can only contain null, tombstones and references.
+ @SuppressWarnings("unchecked")
+ Reference<ThreadLocal<?>> reference
+ = (Reference<ThreadLocal<?>>) k;
+ if (reference.get() == null) {
+ // This thread local was reclaimed by the garbage collector.
+ table[index] = TOMBSTONE;
+ table[index + 1] = null;
+ tombstones++;
+ size--;
+ }
+ }
+
+ // Point cursor to next index.
+ clean = index;
+ }
+
+ /**
+ * Rehashes the table, expanding or contracting it as necessary.
+ * Gets rid of tombstones. Returns true if a rehash occurred.
+ * We must rehash every time we fill a null slot; we depend on the
+ * presence of null slots to end searches (otherwise, we'll infinitely
+ * loop).
+ */
+ private boolean rehash() {
+ if (tombstones + size < maximumLoad) {
+ return false;
+ }
+
+ int capacity = table.length >> 1;
+
+ // Default to the same capacity. This will create a table of the
+ // same size and move over the live entries, analogous to a
+ // garbage collection. This should only happen if you churn a
+ // bunch of thread local garbage (removing and reinserting
+ // the same thread locals over and over will overwrite tombstones
+ // and not fill up the table).
+ int newCapacity = capacity;
+
+ if (size > (capacity >> 1)) {
+ // More than 1/2 filled w/ live entries.
+ // Double size.
+ newCapacity = capacity << 1;
+ }
+
+ Object[] oldTable = this.table;
+
+ // Allocate new table.
+ initializeTable(newCapacity);
+
+ // We won't have any tombstones after this.
+ this.tombstones = 0;
+
+ // If we have no live entries, we can quit here.
+ if (size == 0) {
+ return true;
+ }
+
+ // Move over entries.
+ for (int i = oldTable.length - 2; i >= 0; i -= 2) {
+ Object k = oldTable[i];
+ if (k == null || k == TOMBSTONE) {
+ // Skip this entry.
+ continue;
+ }
+
+ // The table can only contain null, tombstones and references.
+ @SuppressWarnings("unchecked")
+ Reference<ThreadLocal<?>> reference
+ = (Reference<ThreadLocal<?>>) k;
+ ThreadLocal<?> key = reference.get();
+ if (key != null) {
+ // Entry is still live. Move it over.
+ add(key, oldTable[i + 1]);
+ } else {
+ // The key was reclaimed.
+ size--;
+ }
+ }
+
+ return true;
+ }
+
+ /**
+ * Adds an entry during rehashing. Compared to put(), this method
+ * doesn't have to clean up, check for existing entries, account for
+ * tombstones, etc.
+ */
+ void add(ThreadLocal<?> key, Object value) {
+ for (int index = key.hash & mask;; index = next(index)) {
+ Object k = table[index];
+ if (k == null) {
+ table[index] = key.reference;
+ table[index + 1] = value;
+ return;
+ }
+ }
+ }
+
+ /**
+ * Sets entry for given ThreadLocal to given value, creating an
+ * entry if necessary.
+ */
+ void put(ThreadLocal<?> key, Object value) {
+ cleanUp();
+
+ // Keep track of first tombstone. That's where we want to go back
+ // and add an entry if necessary.
+ int firstTombstone = -1;
+
+ for (int index = key.hash & mask;; index = next(index)) {
+ Object k = table[index];
+
+ if (k == key.reference) {
+ // Replace existing entry.
+ table[index + 1] = value;
+ return;
+ }
+
+ if (k == null) {
+ if (firstTombstone == -1) {
+ // Fill in null slot.
+ table[index] = key.reference;
+ table[index + 1] = value;
+ size++;
+ return;
+ }
+
+ // Go back and replace first tombstone.
+ table[firstTombstone] = key.reference;
+ table[firstTombstone + 1] = value;
+ tombstones--;
+ size++;
+ return;
+ }
+
+ // Remember first tombstone.
+ if (firstTombstone == -1 && k == TOMBSTONE) {
+ firstTombstone = index;
+ }
+ }
+ }
+
+ /**
+ * Gets value for given ThreadLocal after not finding it in the first
+ * slot.
+ */
+ Object getAfterMiss(ThreadLocal<?> key) {
+ Object[] table = this.table;
+ int index = key.hash & mask;
+
+ // If the first slot is empty, the search is over.
+ if (table[index] == null) {
+ Object value = key.initialValue();
+
+ // If the table is still the same and the slot is still empty...
+ if (this.table == table && table[index] == null) {
+ table[index] = key.reference;
+ table[index + 1] = value;
+ size++;
+
+ cleanUp();
+ return value;
+ }
+
+ // The table changed during initialValue().
+ put(key, value);
+ return value;
+ }
+
+ // Keep track of first tombstone. That's where we want to go back
+ // and add an entry if necessary.
+ int firstTombstone = -1;
+
+ // Continue search.
+ for (index = next(index);; index = next(index)) {
+ Object reference = table[index];
+ if (reference == key.reference) {
+ return table[index + 1];
+ }
+
+ // If no entry was found...
+ if (reference == null) {
+ Object value = key.initialValue();
+
+ // If the table is still the same...
+ if (this.table == table) {
+ // If we passed a tombstone and that slot still
+ // contains a tombstone...
+ if (firstTombstone > -1
+ && table[firstTombstone] == TOMBSTONE) {
+ table[firstTombstone] = key.reference;
+ table[firstTombstone + 1] = value;
+ tombstones--;
+ size++;
+
+ // No need to clean up here. We aren't filling
+ // in a null slot.
+ return value;
+ }
+
+ // If this slot is still empty...
+ if (table[index] == null) {
+ table[index] = key.reference;
+ table[index + 1] = value;
+ size++;
+
+ cleanUp();
+ return value;
+ }
+ }
+
+ // The table changed during initialValue().
+ put(key, value);
+ return value;
+ }
+
+ if (firstTombstone == -1 && reference == TOMBSTONE) {
+ // Keep track of this tombstone so we can overwrite it.
+ firstTombstone = index;
+ }
+ }
+ }
+
+ /**
+ * Removes entry for the given ThreadLocal.
+ */
+ void remove(ThreadLocal<?> key) {
+ cleanUp();
+
+ for (int index = key.hash & mask;; index = next(index)) {
+ Object reference = table[index];
+
+ if (reference == key.reference) {
+ // Success!
+ table[index] = TOMBSTONE;
+ table[index + 1] = null;
+ tombstones++;
+ size--;
+ return;
+ }
+
+ if (reference == null) {
+ // No entry found.
+ return;
+ }
+ }
+ }
+
+ /**
+ * Gets the next index. If we're at the end of the table, we wrap back
+ * around to 0.
+ */
+ private int next(int index) {
+ return (index + 2) & mask;
+ }
+ }
+}
Propchange: harmony/enhanced/drlvm/trunk/vm/vmcore/src/kernel_classes/javasrc/java/lang/ThreadLocal.java
------------------------------------------------------------------------------
svn:eol-style = native