summaryrefslogtreecommitdiff
path: root/simulator/opendc-utils/src/main
diff options
context:
space:
mode:
authorFabian Mastenbroek <mail.fabianm@gmail.com>2021-03-16 20:13:51 +0100
committerFabian Mastenbroek <mail.fabianm@gmail.com>2021-03-16 20:13:51 +0100
commit9ab482d0afd773703f78d51a2ba8a160896f03c6 (patch)
treed547d55c69c4d5bd97bc0cc220f990ccde53f2ed /simulator/opendc-utils/src/main
parent6a2a5423479696e8dc28885be27cc3e3252f28b0 (diff)
utils: Prevent memory leakage in TimerScheduler
This change fixes a possible memory leakage issue in TimerScheduler when a large number of timers was scheduled for the same timestamp.
Diffstat (limited to 'simulator/opendc-utils/src/main')
-rw-r--r--simulator/opendc-utils/src/main/kotlin/org/opendc/utils/TimerScheduler.kt27
1 files changed, 18 insertions, 9 deletions
diff --git a/simulator/opendc-utils/src/main/kotlin/org/opendc/utils/TimerScheduler.kt b/simulator/opendc-utils/src/main/kotlin/org/opendc/utils/TimerScheduler.kt
index bb6f3299..9f40f26a 100644
--- a/simulator/opendc-utils/src/main/kotlin/org/opendc/utils/TimerScheduler.kt
+++ b/simulator/opendc-utils/src/main/kotlin/org/opendc/utils/TimerScheduler.kt
@@ -59,7 +59,9 @@ public class TimerScheduler<T>(private val coroutineScope: CoroutineScope, priva
* The scheduling job.
*/
private val job = coroutineScope.launch {
+ val timers = timers
val queue = queue
+ val clock = clock
var next: Long? = channel.receive()
while (true) {
@@ -176,17 +178,24 @@ public class TimerScheduler<T>(private val coroutineScope: CoroutineScope, priva
require(timestamp >= now) { "Timestamp must be in the future" }
check(job.isActive) { "Timer is stopped" }
- val timer = Timer(key, timestamp, block)
-
timers.compute(key) { _, old ->
- old?.isCancelled = true
- timer
- }
- queue.add(timer)
+ if (old?.timestamp == timestamp) {
+ // Fast-path: timer for the same timestamp already exists
+ old
+ } else {
+ // Slow-path: cancel old timer and replace it with new timer
+ val timer = Timer(key, timestamp, block)
- // Check if we need to push the interruption forward
- if (queue.peek() == timer) {
- channel.sendBlocking(timer.timestamp)
+ old?.isCancelled = true
+ queue.add(timer)
+
+ // Check if we need to push the interruption forward
+ if (queue.peek() == timer) {
+ channel.sendBlocking(timer.timestamp)
+ }
+
+ timer
+ }
}
}