Permalink
Browse files

SLING-7432 - Thread pool clean up code can lead to infinite loops in

ThreadLocal.get

Save and restore the size and threshold fields of the ThreadLocalMap as
well as the entries. Not saving them would lead to situations there the
ThreadLocalMap would be full and not resized, since the initial size and
threshold values were being kept.
  • Loading branch information...
rombert committed Jan 23, 2018
1 parent 21a553d commit afa59d668ac39004510fcd4366298f62179736c4
@@ -41,8 +41,13 @@
/** this field is in class {@code ThreadLocal.ThreadLocalMap.Entry} and contains an object referencing the actual thread local
* variable */
private static Field threadLocalEntryValueField;
/** this field is in the class {@code ThreadLocal.ThreadLocalMap} and contains the number of the entries */
private static Field threadLocalMapSizeField;
/** this field is in the class {@code ThreadLocal.ThreadLocalMap} and next resize threshold */
private static Field threadLocalMapThresholdField;
private static IllegalStateException reflectionException;
public ThreadLocalCleaner(ThreadLocalChangeListener listener) {
if (threadLocalsField == null) {
initReflectionFields();
@@ -65,6 +70,8 @@ private static synchronized void initReflectionFields() throws IllegalStateExcep
tableField = field(threadLocalMapClass, "table");
threadLocalMapEntryClass = inner(threadLocalMapClass, "Entry");
threadLocalEntryValueField = field(threadLocalMapEntryClass, "value");
threadLocalMapSizeField = field(threadLocalMapClass, "size");
threadLocalMapThresholdField = field(threadLocalMapClass, "threshold");
} catch (NoSuchFieldException e) {
reflectionException = new IllegalStateException(
"Could not locate threadLocals field in class Thread. " +
@@ -169,12 +176,19 @@ private static Field field(Class<?> c, String name)
}
private static final ThreadLocal<Reference<?>[]> copyOfThreadLocals = new ThreadLocal<>();
private static final ThreadLocal<Integer> copyOfThreadLocalsSize = new ThreadLocal<>();
private static final ThreadLocal<Integer> copyOfThreadLocalsThreshold = new ThreadLocal<>();
private static final ThreadLocal<Reference<?>[]> copyOfInheritableThreadLocals = new ThreadLocal<>();
private static final ThreadLocal<Integer> copyOfInheritableThreadLocalsSize = new ThreadLocal<>();
private static final ThreadLocal<Integer> copyOfInheritableThreadLocalsThreshold = new ThreadLocal<>();
private static void saveOldThreadLocals() {
copyOfThreadLocals.set(copy(threadLocalsField));
copyOfThreadLocalsSize.set(size(threadLocalsField, threadLocalMapSizeField));
copyOfThreadLocalsThreshold.set(size(threadLocalsField, threadLocalMapThresholdField));
copyOfInheritableThreadLocals.set(copy(inheritableThreadLocalsField));
copyOfInheritableThreadLocalsSize.set(size(inheritableThreadLocalsField, threadLocalMapSizeField));
copyOfInheritableThreadLocalsThreshold.set(size(inheritableThreadLocalsField, threadLocalMapThresholdField));
}
private static Reference<?>[] copy(Field field) {
@@ -190,31 +204,43 @@ private static void saveOldThreadLocals() {
}
}
private static Integer size(Field field, Field sizeField) {
try {
Thread thread = Thread.currentThread();
Object threadLocals = field.get(thread);
if (threadLocals == null)
return null;
return (Integer) sizeField.get(threadLocals);
} catch (IllegalAccessException e) {
throw new IllegalStateException("Access denied", e);
}
}
private static void restoreOldThreadLocals() {
try {
restore(inheritableThreadLocalsField, copyOfInheritableThreadLocals.get());
restore(threadLocalsField, copyOfThreadLocals.get());
restore(inheritableThreadLocalsField, copyOfInheritableThreadLocals.get(),
copyOfInheritableThreadLocalsSize.get(), copyOfInheritableThreadLocalsThreshold.get());
restore(threadLocalsField, copyOfThreadLocals.get(),
copyOfThreadLocalsSize.get(), copyOfThreadLocalsThreshold.get());
} finally {
copyOfThreadLocals.remove();
copyOfInheritableThreadLocals.remove();
}
}
private static void restore(Field field, Object value) {
private static void restore(Field field, Object value, Integer size, Integer threshold) {
try {
Thread thread = Thread.currentThread();
if (value == null) {
field.set(thread, null);
} else {
tableField.set(field.get(thread), value);
final Object threadLocals = field.get(thread);
tableField.set(threadLocals, value);
threadLocalMapSizeField.set(threadLocals, size);
threadLocalMapThresholdField.set(threadLocals, threshold);
}
} catch (IllegalAccessException e) {
throw new IllegalStateException("Access denied", e);
}
}
static {
// TODO: move to a place where the exception can be caught!
}
}
@@ -57,7 +57,6 @@ public void setUp() {
}
@Test(timeout = 10000)
@Ignore
public void threadLocalCleanupWorksWithResize() throws Exception {
// configure thread local counts to make sure that

0 comments on commit afa59d6

Please sign in to comment.