Skip to content

Commit

Permalink
UNDERTOW-2309 Prevent memory leak in DefaultByteBufferPool
Browse files Browse the repository at this point in the history
  • Loading branch information
TomasHofman committed Sep 27, 2023
1 parent 510bb19 commit 326fef6
Showing 1 changed file with 29 additions and 1 deletion.
30 changes: 29 additions & 1 deletion core/src/main/java/io/undertow/server/DefaultByteBufferPool.java
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,9 @@
import java.nio.ByteBuffer;
import java.util.ArrayDeque;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentLinkedQueue;
import java.util.concurrent.atomic.AtomicIntegerFieldUpdater;

Expand All @@ -39,7 +41,7 @@
*/
public class DefaultByteBufferPool implements ByteBufferPool {

private final ThreadLocal<ThreadLocalData> threadLocalCache = new ThreadLocal<>();
private final ThreadLocalCache threadLocalCache = new ThreadLocalCache();
// Access requires synchronization on the threadLocalDataList instance
private final List<WeakReference<ThreadLocalData>> threadLocalDataList = new ArrayList<>();
private final ConcurrentLinkedQueue<ByteBuffer> queue = new ConcurrentLinkedQueue<>();
Expand Down Expand Up @@ -228,6 +230,7 @@ public void close() {
local.buffers.clear();
}
ref.clear();
threadLocalCache.remove(local);
}
threadLocalDataList.clear();
}
Expand Down Expand Up @@ -332,4 +335,29 @@ protected void finalize() throws Throwable {
}
}

// This is used instead of Java ThreadLocal class. Unlike in the ThreadLocal class, the remove() method in this
// class can be called by a different thread than the one that initialized the data.
private static class ThreadLocalCache {

Map<Thread, ThreadLocalData> localsByThread = new HashMap<>();

ThreadLocalData get() {
return localsByThread.get(Thread.currentThread());
}

void set(ThreadLocalData threadLocalData) {
localsByThread.put(Thread.currentThread(), threadLocalData);
}

void remove(ThreadLocalData threadLocalData) {
// Find the entry containing given data instance and remove it from the map.
for (Thread thread: localsByThread.keySet()) {
if (threadLocalData.equals(localsByThread.get(thread))) {
localsByThread.remove(thread, threadLocalData);
break;
}
}
}
}

}

0 comments on commit 326fef6

Please # to comment.