Skip to content

Commit 45b9f50

Browse files
Remove weak references from LogContext childContexts when the child is GC'd (#153)
Should prevent unbounded growth of the context list for contexts with lots of short-lived children.
1 parent 6673f0f commit 45b9f50

File tree

1 file changed

+17
-7
lines changed

1 file changed

+17
-7
lines changed

src/main/kotlin/org/jitsi/utils/logging2/LogContext.kt

Lines changed: 17 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
*/
1616
package org.jitsi.utils.logging2
1717

18+
import java.lang.ref.Cleaner
1819
import java.lang.ref.WeakReference
1920

2021
/**
@@ -48,22 +49,30 @@ class LogContext private constructor(
4849
var formattedContext: String = formatContext(ancestorsContext + context)
4950
private set
5051

51-
/** Child [LogContext]s of this [LogContext] (which will be notified anytime this context changes) */
52-
private val childContexts: MutableList<WeakReference<LogContext>> = ArrayList()
53-
5452
@Synchronized
5553
private fun updateFormattedContext() {
5654
val combined = ancestorsContext + context
5755
formattedContext = formatContext(combined)
5856
updateChildren(combined)
5957
}
6058

59+
/** Child [LogContext]s of this [LogContext] (which will be notified anytime this context changes) */
60+
private val childContexts = mutableMapOf<Long, WeakReference<LogContext>>()
61+
62+
private var nextKey = 0L
63+
6164
@Synchronized
6265
fun createSubContext(childContextData: Map<String, String>) = LogContext(
6366
ancestorsContext + context,
6467
childContextData
6568
).also {
66-
childContexts.add(WeakReference(it))
69+
val key = nextKey++
70+
childContexts[key] = WeakReference(it)
71+
CLEANER.register(it) {
72+
synchronized(this@LogContext) {
73+
childContexts.remove(key)
74+
}
75+
}
6776
}
6877

6978
fun addContext(key: String, value: String) = addContext(mapOf(key to value))
@@ -82,9 +91,8 @@ class LogContext private constructor(
8291

8392
/** Notify children of changes in this context */
8493
@Synchronized
85-
private fun updateChildren(newAncestorContext: Map<String, String>) = childContexts.apply {
86-
removeIf { it.get() == null }
87-
forEach { it.get()?.ancestorsContext = newAncestorContext }
94+
private fun updateChildren(newAncestorContext: Map<String, String>) = childContexts.values.forEach {
95+
it.get()?.ancestorsContext = newAncestorContext
8896
}
8997

9098
override fun toString() = formattedContext
@@ -93,6 +101,8 @@ class LogContext private constructor(
93101
const val CONTEXT_START_TOKEN = "["
94102
const val CONTEXT_END_TOKEN = "]"
95103

104+
private val CLEANER = Cleaner.create()
105+
96106
private fun formatContext(context: Map<String, String>): String {
97107
val s = context.entries.joinToString(separator = " ") { "${it.key}=${it.value}" }
98108
return if (s.isEmpty()) "" else "$CONTEXT_START_TOKEN$s$CONTEXT_END_TOKEN"

0 commit comments

Comments
 (0)