org.elasticsearch.common.thread.ThreadLocals Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of elasticsearch Show documentation
Show all versions of elasticsearch Show documentation
Elasticsearch subproject :server
/*
* Licensed to Elastic Search and Shay Banon under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. Elastic Search 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 org.elasticsearch.common.thread;
import org.elasticsearch.common.logging.ESLogger;
import org.elasticsearch.common.logging.Loggers;
import java.lang.ref.Reference;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
/**
* @author kimchy (shay.banon)
*/
public class ThreadLocals {
private static final ESLogger logger = Loggers.getLogger(ThreadLocals.class);
public static class CleanableValue {
private T value;
public CleanableValue(T value) {
this.value = value;
}
public T get() {
return value;
}
public void set(T value) {
this.value = value;
}
}
public static void clearReferencesThreadLocals() {
try {
Thread[] threads = getThreads();
// Make the fields in the Thread class that store ThreadLocals
// accessible
Field threadLocalsField = Thread.class.getDeclaredField("threadLocals");
threadLocalsField.setAccessible(true);
Field inheritableThreadLocalsField = Thread.class.getDeclaredField("inheritableThreadLocals");
inheritableThreadLocalsField.setAccessible(true);
// Make the underlying array of ThreadLoad.ThreadLocalMap.Entry objects
// accessible
Class tlmClass = Class.forName("java.lang.ThreadLocal$ThreadLocalMap");
Field tableField = tlmClass.getDeclaredField("table");
tableField.setAccessible(true);
for (int i = 0; i < threads.length; i++) {
Object threadLocalMap;
if (threads[i] != null) {
// Clear the first map
threadLocalMap = threadLocalsField.get(threads[i]);
clearThreadLocalMap(threadLocalMap, tableField);
// Clear the second map
threadLocalMap =
inheritableThreadLocalsField.get(threads[i]);
clearThreadLocalMap(threadLocalMap, tableField);
}
}
} catch (Exception e) {
logger.debug("Failed to clean thread locals", e);
}
}
/*
* Clears the given thread local map object. Also pass in the field that
* points to the internal table to save re-calculating it on every
* call to this method.
*/
private static void clearThreadLocalMap(Object map, Field internalTableField) throws NoSuchMethodException, IllegalAccessException, NoSuchFieldException, InvocationTargetException {
if (map != null) {
Method mapRemove = map.getClass().getDeclaredMethod("remove", ThreadLocal.class);
mapRemove.setAccessible(true);
Object[] table = (Object[]) internalTableField.get(map);
int staleEntriesCount = 0;
if (table != null) {
for (int j = 0; j < table.length; j++) {
if (table[j] != null) {
boolean remove = false;
// Check the key
Object key = ((Reference) table[j]).get();
// Check the value
Field valueField = table[j].getClass().getDeclaredField("value");
valueField.setAccessible(true);
Object value = valueField.get(table[j]);
if ((value != null && CleanableValue.class.isAssignableFrom(value.getClass()))) {
remove = true;
}
if (remove) {
Object[] args = new Object[4];
if (key != null) {
args[0] = key.getClass().getCanonicalName();
args[1] = key.toString();
}
args[2] = value.getClass().getCanonicalName();
args[3] = value.toString();
if (logger.isDebugEnabled()) {
logger.trace("ThreadLocal with key of type [{0}] (value [{1}]) and a value of type [{2}] (value [{3}]): The ThreadLocal has been forcibly removed.", args);
}
if (key == null) {
staleEntriesCount++;
} else {
mapRemove.invoke(map, key);
}
}
}
}
}
if (staleEntriesCount > 0) {
Method mapRemoveStale = map.getClass().getDeclaredMethod("expungeStaleEntries");
mapRemoveStale.setAccessible(true);
mapRemoveStale.invoke(map);
}
}
}
/*
* Get the set of current threads as an array.
*/
private static Thread[] getThreads() {
// Get the current thread group
ThreadGroup tg = Thread.currentThread().getThreadGroup();
// Find the root thread group
while (tg.getParent() != null) {
tg = tg.getParent();
}
int threadCountGuess = tg.activeCount() + 50;
Thread[] threads = new Thread[threadCountGuess];
int threadCountActual = tg.enumerate(threads);
// Make sure we don't miss any threads
while (threadCountActual == threadCountGuess) {
threadCountGuess *= 2;
threads = new Thread[threadCountGuess];
// Note tg.enumerate(Thread[]) silently ignores any threads that
// can't fit into the array
threadCountActual = tg.enumerate(threads);
}
return threads;
}
}