From 6a2a5423479696e8dc28885be27cc3e3252f28b0 Mon Sep 17 00:00:00 2001 From: Fabian Mastenbroek Date: Wed, 30 Dec 2020 14:03:12 +0100 Subject: simulator: Add generic framework for resource consumption modeling This change adds a generic framework for modeling resource consumptions and adapts opendc-simulator-compute to model machines and VMs on top of this framework. This framework anticipates the addition of additional resource types such as memory, disk and network to the OpenDC codebase. --- .../opendc-utils/src/main/kotlin/org/opendc/utils/TimerScheduler.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'simulator/opendc-utils') 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 ff116443..bb6f3299 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 @@ -51,7 +51,7 @@ public class TimerScheduler(private val coroutineScope: CoroutineScope, priva private val timers = mutableMapOf() /** - * The channel to communicate with the + * The channel to communicate with the scheduling job. */ private val channel = Channel(Channel.CONFLATED) -- cgit v1.2.3 From 9ab482d0afd773703f78d51a2ba8a160896f03c6 Mon Sep 17 00:00:00 2001 From: Fabian Mastenbroek Date: Tue, 16 Mar 2021 20:13:51 +0100 Subject: 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. --- .../main/kotlin/org/opendc/utils/TimerScheduler.kt | 27 ++++++++++++++-------- 1 file changed, 18 insertions(+), 9 deletions(-) (limited to 'simulator/opendc-utils') 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(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(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 + } } } -- cgit v1.2.3 From bb3b8e207a08edff81b8c2fe30b476c94bfea086 Mon Sep 17 00:00:00 2001 From: Fabian Mastenbroek Date: Wed, 17 Mar 2021 16:23:48 +0100 Subject: simulator: Make hypervisors generic for the resource type This change moves the hypervisor implementations to the opendc-simulator-resources module and makes them generic to the resource type that is being used (e.g., CPU, disk or networking). --- .../main/kotlin/org/opendc/utils/TimerScheduler.kt | 26 ++++++++++++++-------- .../kotlin/org/opendc/utils/TimerSchedulerTest.kt | 14 ++++++------ 2 files changed, 24 insertions(+), 16 deletions(-) (limited to 'simulator/opendc-utils') 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 9f40f26a..49964938 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 @@ -22,24 +22,28 @@ package org.opendc.utils -import kotlinx.coroutines.CoroutineScope -import kotlinx.coroutines.ExperimentalCoroutinesApi +import kotlinx.coroutines.* import kotlinx.coroutines.channels.Channel import kotlinx.coroutines.channels.sendBlocking -import kotlinx.coroutines.launch import kotlinx.coroutines.selects.select import java.time.Clock import java.util.* +import kotlin.coroutines.CoroutineContext import kotlin.math.max /** * A TimerScheduler facilitates scheduled execution of future tasks. * - * @property coroutineScope The [CoroutineScope] to run the tasks in. + * @property context The [CoroutineContext] to run the tasks with. * @property clock The clock to keep track of the time. */ @OptIn(ExperimentalCoroutinesApi::class) -public class TimerScheduler(private val coroutineScope: CoroutineScope, private val clock: Clock) : AutoCloseable { +public class TimerScheduler(context: CoroutineContext, private val clock: Clock) : AutoCloseable { + /** + * The scope in which the scheduler runs. + */ + private val scope = CoroutineScope(context + Job()) + /** * A priority queue containing the tasks to be scheduled in the future. */ @@ -58,7 +62,7 @@ public class TimerScheduler(private val coroutineScope: CoroutineScope, priva /** * The scheduling job. */ - private val job = coroutineScope.launch { + private val job = scope.launch { val timers = timers val queue = queue val clock = clock @@ -71,7 +75,7 @@ public class TimerScheduler(private val coroutineScope: CoroutineScope, priva val delay = next?.let { max(0L, it - clock.millis()) } ?: return@select onTimeout(delay) { - while (queue.isNotEmpty()) { + while (queue.isNotEmpty() && isActive) { val timer = queue.peek() val timestamp = clock.millis() @@ -86,7 +90,11 @@ public class TimerScheduler(private val coroutineScope: CoroutineScope, priva if (!timer.isCancelled) { timers.remove(timer.key) - timer() + try { + timer() + } catch (e: Throwable) { + Thread.getDefaultUncaughtExceptionHandler().uncaughtException(Thread.currentThread(), e) + } } } @@ -101,7 +109,7 @@ public class TimerScheduler(private val coroutineScope: CoroutineScope, priva */ override fun close() { cancelAll() - job.cancel() + scope.cancel() } /** diff --git a/simulator/opendc-utils/src/test/kotlin/org/opendc/utils/TimerSchedulerTest.kt b/simulator/opendc-utils/src/test/kotlin/org/opendc/utils/TimerSchedulerTest.kt index 3a4acc90..1fcb5d38 100644 --- a/simulator/opendc-utils/src/test/kotlin/org/opendc/utils/TimerSchedulerTest.kt +++ b/simulator/opendc-utils/src/test/kotlin/org/opendc/utils/TimerSchedulerTest.kt @@ -38,7 +38,7 @@ internal class TimerSchedulerTest { fun testBasicTimer() { runBlockingTest { val clock = DelayControllerClockAdapter(this) - val scheduler = TimerScheduler(this, clock) + val scheduler = TimerScheduler(coroutineContext, clock) scheduler.startSingleTimer(0, 1000) { scheduler.close() @@ -51,7 +51,7 @@ internal class TimerSchedulerTest { fun testCancelNonExisting() { runBlockingTest { val clock = DelayControllerClockAdapter(this) - val scheduler = TimerScheduler(this, clock) + val scheduler = TimerScheduler(coroutineContext, clock) scheduler.cancel(1) scheduler.close() @@ -62,7 +62,7 @@ internal class TimerSchedulerTest { fun testCancelExisting() { runBlockingTest { val clock = DelayControllerClockAdapter(this) - val scheduler = TimerScheduler(this, clock) + val scheduler = TimerScheduler(coroutineContext, clock) scheduler.startSingleTimer(0, 1000) { assertFalse(false) @@ -81,7 +81,7 @@ internal class TimerSchedulerTest { fun testCancelAll() { runBlockingTest { val clock = DelayControllerClockAdapter(this) - val scheduler = TimerScheduler(this, clock) + val scheduler = TimerScheduler(coroutineContext, clock) scheduler.startSingleTimer(0, 1000) { assertFalse(false) @@ -99,7 +99,7 @@ internal class TimerSchedulerTest { fun testOverride() { runBlockingTest { val clock = DelayControllerClockAdapter(this) - val scheduler = TimerScheduler(this, clock) + val scheduler = TimerScheduler(coroutineContext, clock) scheduler.startSingleTimer(0, 1000) { assertFalse(false) @@ -117,7 +117,7 @@ internal class TimerSchedulerTest { fun testStopped() { runBlockingTest { val clock = DelayControllerClockAdapter(this) - val scheduler = TimerScheduler(this, clock) + val scheduler = TimerScheduler(coroutineContext, clock) scheduler.close() @@ -133,7 +133,7 @@ internal class TimerSchedulerTest { fun testNegativeDelay() { runBlockingTest { val clock = DelayControllerClockAdapter(this) - val scheduler = TimerScheduler(this, clock) + val scheduler = TimerScheduler(coroutineContext, clock) assertThrows { scheduler.startSingleTimer(1, -1) { -- cgit v1.2.3