Skip to content

Commit afa59d6

Browse files
committed
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.
1 parent 21a553d commit afa59d6

File tree

2 files changed

+36
-11
lines changed

2 files changed

+36
-11
lines changed

src/main/java/org/apache/sling/commons/threads/impl/ThreadLocalCleaner.java

Lines changed: 36 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -41,8 +41,13 @@ public class ThreadLocalCleaner {
4141
/** this field is in class {@code ThreadLocal.ThreadLocalMap.Entry} and contains an object referencing the actual thread local
4242
* variable */
4343
private static Field threadLocalEntryValueField;
44+
/** this field is in the class {@code ThreadLocal.ThreadLocalMap} and contains the number of the entries */
45+
private static Field threadLocalMapSizeField;
46+
/** this field is in the class {@code ThreadLocal.ThreadLocalMap} and next resize threshold */
47+
private static Field threadLocalMapThresholdField;
4448
private static IllegalStateException reflectionException;
4549

50+
4651
public ThreadLocalCleaner(ThreadLocalChangeListener listener) {
4752
if (threadLocalsField == null) {
4853
initReflectionFields();
@@ -65,6 +70,8 @@ private static synchronized void initReflectionFields() throws IllegalStateExcep
6570
tableField = field(threadLocalMapClass, "table");
6671
threadLocalMapEntryClass = inner(threadLocalMapClass, "Entry");
6772
threadLocalEntryValueField = field(threadLocalMapEntryClass, "value");
73+
threadLocalMapSizeField = field(threadLocalMapClass, "size");
74+
threadLocalMapThresholdField = field(threadLocalMapClass, "threshold");
6875
} catch (NoSuchFieldException e) {
6976
reflectionException = new IllegalStateException(
7077
"Could not locate threadLocals field in class Thread. " +
@@ -169,12 +176,19 @@ private static Class<?> inner(Class<?> clazz, String name) {
169176
}
170177

171178
private static final ThreadLocal<Reference<?>[]> copyOfThreadLocals = new ThreadLocal<>();
172-
179+
private static final ThreadLocal<Integer> copyOfThreadLocalsSize = new ThreadLocal<>();
180+
private static final ThreadLocal<Integer> copyOfThreadLocalsThreshold = new ThreadLocal<>();
173181
private static final ThreadLocal<Reference<?>[]> copyOfInheritableThreadLocals = new ThreadLocal<>();
182+
private static final ThreadLocal<Integer> copyOfInheritableThreadLocalsSize = new ThreadLocal<>();
183+
private static final ThreadLocal<Integer> copyOfInheritableThreadLocalsThreshold = new ThreadLocal<>();
174184

175185
private static void saveOldThreadLocals() {
176186
copyOfThreadLocals.set(copy(threadLocalsField));
187+
copyOfThreadLocalsSize.set(size(threadLocalsField, threadLocalMapSizeField));
188+
copyOfThreadLocalsThreshold.set(size(threadLocalsField, threadLocalMapThresholdField));
177189
copyOfInheritableThreadLocals.set(copy(inheritableThreadLocalsField));
190+
copyOfInheritableThreadLocalsSize.set(size(inheritableThreadLocalsField, threadLocalMapSizeField));
191+
copyOfInheritableThreadLocalsThreshold.set(size(inheritableThreadLocalsField, threadLocalMapThresholdField));
178192
}
179193

180194
private static Reference<?>[] copy(Field field) {
@@ -190,31 +204,43 @@ private static Reference<?>[] copy(Field field) {
190204
}
191205
}
192206

207+
private static Integer size(Field field, Field sizeField) {
208+
try {
209+
Thread thread = Thread.currentThread();
210+
Object threadLocals = field.get(thread);
211+
if (threadLocals == null)
212+
return null;
213+
return (Integer) sizeField.get(threadLocals);
214+
} catch (IllegalAccessException e) {
215+
throw new IllegalStateException("Access denied", e);
216+
}
217+
}
218+
193219
private static void restoreOldThreadLocals() {
194220
try {
195-
restore(inheritableThreadLocalsField, copyOfInheritableThreadLocals.get());
196-
restore(threadLocalsField, copyOfThreadLocals.get());
221+
restore(inheritableThreadLocalsField, copyOfInheritableThreadLocals.get(),
222+
copyOfInheritableThreadLocalsSize.get(), copyOfInheritableThreadLocalsThreshold.get());
223+
restore(threadLocalsField, copyOfThreadLocals.get(),
224+
copyOfThreadLocalsSize.get(), copyOfThreadLocalsThreshold.get());
197225
} finally {
198226
copyOfThreadLocals.remove();
199227
copyOfInheritableThreadLocals.remove();
200228
}
201229
}
202230

203-
private static void restore(Field field, Object value) {
231+
private static void restore(Field field, Object value, Integer size, Integer threshold) {
204232
try {
205233
Thread thread = Thread.currentThread();
206234
if (value == null) {
207235
field.set(thread, null);
208236
} else {
209-
tableField.set(field.get(thread), value);
237+
final Object threadLocals = field.get(thread);
238+
tableField.set(threadLocals, value);
239+
threadLocalMapSizeField.set(threadLocals, size);
240+
threadLocalMapThresholdField.set(threadLocals, threshold);
210241
}
211242
} catch (IllegalAccessException e) {
212243
throw new IllegalStateException("Access denied", e);
213244
}
214245
}
215-
216-
static {
217-
// TODO: move to a place where the exception can be caught!
218-
219-
}
220246
}

src/test/java/org/apache/sling/commons/threads/impl/ThreadPoolExecutorCleaningThreadLocalsTest.java

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,6 @@ public void setUp() {
5757
}
5858

5959
@Test(timeout = 10000)
60-
@Ignore
6160
public void threadLocalCleanupWorksWithResize() throws Exception {
6261

6362
// configure thread local counts to make sure that

0 commit comments

Comments
 (0)