From 6ca0ae07669d20a5a34ef697610df90754024035 Mon Sep 17 00:00:00 2001 From: Fabian Mastenbroek Date: Wed, 20 Nov 2019 17:51:58 +0100 Subject: refactor: Move build logic to buildSrc --- CONTRIBUTING.md | 0 README.md | 81 +-- build.gradle.kts | 15 +- buildSrc/build.gradle.kts | 46 ++ buildSrc/settings.gradle.kts | 23 + .../src/main/kotlin/dokka-convention.gradle.kts | 33 ++ .../src/main/kotlin/jacoco-aggregation.gradle.kts | 50 ++ .../kotlin/kotlin-library-convention.gradle.kts | 57 +++ buildSrc/src/main/kotlin/library.kt | 44 ++ gradle/dokka.gradle | 47 -- gradle/jacoco.gradle | 55 -- gradle/kotlin.gradle | 64 --- gradle/wrapper/gradle-wrapper.properties | 2 +- odcsim-core/build.gradle.kts | 47 -- .../main/kotlin/com/atlarge/odcsim/ActorContext.kt | 175 ------- .../main/kotlin/com/atlarge/odcsim/ActorPath.kt | 142 ------ .../src/main/kotlin/com/atlarge/odcsim/ActorRef.kt | 50 -- .../main/kotlin/com/atlarge/odcsim/ActorSystem.kt | 76 --- .../com/atlarge/odcsim/ActorSystemFactory.kt | 38 -- .../src/main/kotlin/com/atlarge/odcsim/Behavior.kt | 193 ------- .../main/kotlin/com/atlarge/odcsim/Behaviors.kt | 223 -------- .../src/main/kotlin/com/atlarge/odcsim/Envelope.kt | 57 --- .../src/main/kotlin/com/atlarge/odcsim/Signals.kt | 60 --- .../main/kotlin/com/atlarge/odcsim/StashBuffer.kt | 88 ---- .../src/main/kotlin/com/atlarge/odcsim/Time.kt | 75 --- .../kotlin/com/atlarge/odcsim/TimerScheduler.kt | 92 ---- .../com/atlarge/odcsim/coroutines/Behavior.kt | 116 ----- .../com/atlarge/odcsim/coroutines/dsl/Receive.kt | 66 --- .../com/atlarge/odcsim/coroutines/dsl/Timeout.kt | 42 -- .../com/atlarge/odcsim/internal/ActorContext.kt | 43 -- .../kotlin/com/atlarge/odcsim/internal/Behavior.kt | 48 -- .../atlarge/odcsim/internal/BehaviorInterpreter.kt | 201 -------- .../com/atlarge/odcsim/internal/Coroutines.kt | 182 ------- .../com/atlarge/odcsim/internal/StashBufferImpl.kt | 74 --- .../atlarge/odcsim/internal/TimerSchedulerImpl.kt | 122 ----- .../internal/logging/LocationAwareLoggerImpl.kt | 567 --------------------- .../internal/logging/LocationIgnorantLoggerImpl.kt | 440 ---------------- .../atlarge/odcsim/internal/logging/LoggerImpl.kt | 77 --- .../kotlin/com/atlarge/odcsim/ActorPathTest.kt | 86 ---- .../test/kotlin/com/atlarge/odcsim/BehaviorTest.kt | 77 --- .../kotlin/com/atlarge/odcsim/CoroutinesTest.kt | 63 --- odcsim-engine-omega/build.gradle.kts | 50 -- .../odcsim/engine/omega/OmegaActorSystem.kt | 360 ------------- .../odcsim/engine/omega/OmegaActorSystemFactory.kt | 38 -- .../services/com.atlarge.odcsim.ActorSystemFactory | 1 - .../engine/omega/OmegaActorSystemFactoryTest.kt | 37 -- .../odcsim/engine/omega/OmegaActorSystemTest.kt | 37 -- odcsim-engine-tests/build.gradle.kts | 43 -- .../odcsim/engine/tests/ActorSystemContract.kt | 403 --------------- .../engine/tests/ActorSystemFactoryContract.kt | 73 --- odcsim-testkit/build.gradle.kts | 43 -- .../com/atlarge/odcsim/testkit/BehaviorTestKit.kt | 138 ----- .../kotlin/com/atlarge/odcsim/testkit/TestInbox.kt | 90 ---- .../odcsim/testkit/internal/ActorContextStub.kt | 99 ---- .../odcsim/testkit/internal/ActorSystemStub.kt | 58 --- .../odcsim/testkit/internal/BehaviorTestKitImpl.kt | 112 ---- .../odcsim/testkit/internal/TestInboxImpl.kt | 100 ---- odcsim/README.md | 100 ++++ odcsim/build.gradle.kts | 23 + odcsim/odcsim-core/build.gradle.kts | 38 ++ .../main/kotlin/com/atlarge/odcsim/ActorContext.kt | 175 +++++++ .../main/kotlin/com/atlarge/odcsim/ActorPath.kt | 142 ++++++ .../src/main/kotlin/com/atlarge/odcsim/ActorRef.kt | 50 ++ .../main/kotlin/com/atlarge/odcsim/ActorSystem.kt | 76 +++ .../com/atlarge/odcsim/ActorSystemFactory.kt | 38 ++ .../src/main/kotlin/com/atlarge/odcsim/Behavior.kt | 193 +++++++ .../main/kotlin/com/atlarge/odcsim/Behaviors.kt | 223 ++++++++ .../src/main/kotlin/com/atlarge/odcsim/Envelope.kt | 57 +++ .../src/main/kotlin/com/atlarge/odcsim/Signals.kt | 60 +++ .../main/kotlin/com/atlarge/odcsim/StashBuffer.kt | 88 ++++ .../src/main/kotlin/com/atlarge/odcsim/Time.kt | 75 +++ .../kotlin/com/atlarge/odcsim/TimerScheduler.kt | 92 ++++ .../com/atlarge/odcsim/coroutines/Behavior.kt | 116 +++++ .../com/atlarge/odcsim/coroutines/dsl/Receive.kt | 66 +++ .../com/atlarge/odcsim/coroutines/dsl/Timeout.kt | 42 ++ .../com/atlarge/odcsim/internal/ActorContext.kt | 43 ++ .../kotlin/com/atlarge/odcsim/internal/Behavior.kt | 48 ++ .../atlarge/odcsim/internal/BehaviorInterpreter.kt | 201 ++++++++ .../com/atlarge/odcsim/internal/Coroutines.kt | 182 +++++++ .../com/atlarge/odcsim/internal/StashBufferImpl.kt | 74 +++ .../atlarge/odcsim/internal/TimerSchedulerImpl.kt | 122 +++++ .../internal/logging/LocationAwareLoggerImpl.kt | 567 +++++++++++++++++++++ .../internal/logging/LocationIgnorantLoggerImpl.kt | 440 ++++++++++++++++ .../atlarge/odcsim/internal/logging/LoggerImpl.kt | 77 +++ .../kotlin/com/atlarge/odcsim/ActorPathTest.kt | 86 ++++ .../test/kotlin/com/atlarge/odcsim/BehaviorTest.kt | 77 +++ .../kotlin/com/atlarge/odcsim/CoroutinesTest.kt | 63 +++ odcsim/odcsim-engine-omega/build.gradle.kts | 46 ++ .../odcsim/engine/omega/OmegaActorSystem.kt | 360 +++++++++++++ .../odcsim/engine/omega/OmegaActorSystemFactory.kt | 38 ++ .../services/com.atlarge.odcsim.ActorSystemFactory | 1 + .../engine/omega/OmegaActorSystemFactoryTest.kt | 37 ++ .../odcsim/engine/omega/OmegaActorSystemTest.kt | 37 ++ odcsim/odcsim-engine-tests/build.gradle.kts | 40 ++ .../odcsim/engine/tests/ActorSystemContract.kt | 403 +++++++++++++++ .../engine/tests/ActorSystemFactoryContract.kt | 73 +++ opendc-core/build.gradle.kts | 51 -- .../main/kotlin/com/atlarge/opendc/model/Broker.kt | 40 -- .../kotlin/com/atlarge/opendc/model/Cluster.kt | 56 -- .../kotlin/com/atlarge/opendc/model/Environment.kt | 36 -- .../kotlin/com/atlarge/opendc/model/Identity.kt | 42 -- .../main/kotlin/com/atlarge/opendc/model/Model.kt | 71 --- .../kotlin/com/atlarge/opendc/model/Platform.kt | 103 ---- .../main/kotlin/com/atlarge/opendc/model/User.kt | 35 -- .../main/kotlin/com/atlarge/opendc/model/Zone.kt | 212 -------- .../opendc/model/resources/compute/Machine.kt | 105 ---- .../model/resources/compute/MachineStatus.kt | 33 -- .../model/resources/compute/ProcessingElement.kt | 33 -- .../model/resources/compute/ProcessingUnit.kt | 44 -- .../opendc/model/resources/compute/host/Host.kt | 86 ---- .../compute/scheduling/MachineScheduler.kt | 48 -- .../compute/scheduling/MachineSchedulerLogic.kt | 64 --- .../compute/scheduling/ProcessObserver.kt | 68 --- .../resources/compute/scheduling/ProcessState.kt | 50 -- .../resources/compute/scheduling/ProcessView.kt | 53 -- .../scheduling/SpaceSharedMachineScheduler.kt | 178 ------- .../compute/supervision/MachineSupervisionEvent.kt | 49 -- .../compute/supervision/MachineSupervisor.kt | 67 --- .../com/atlarge/opendc/model/services/Service.kt | 47 -- .../atlarge/opendc/model/services/ServiceMap.kt | 49 -- .../opendc/model/services/ServiceProvider.kt | 64 --- .../services/provisioning/ProvisioningService.kt | 77 --- .../provisioning/SimpleProvisioningService.kt | 125 ----- .../opendc/model/services/resources/HostView.kt | 42 -- .../resources/ResourceManagementService.kt | 121 ----- .../com/atlarge/opendc/model/workload/Workload.kt | 39 -- .../model/workload/application/Application.kt | 47 -- .../model/workload/application/FlopsApplication.kt | 164 ------ .../opendc/model/workload/application/Process.kt | 91 ---- .../workload/application/ProcessSupervisor.kt | 79 --- .../workload/application/FlopsApplicationTest.kt | 128 ----- opendc-experiments-tpds/build.gradle.kts | 50 -- .../opendc/experiments/tpds/TestExperiment.kt | 148 ------ .../src/main/resources/env/setup-test.json | 36 -- .../src/main/resources/log4j2.xml | 52 -- opendc-format-gwf/build.gradle.kts | 48 -- .../opendc/format/trace/gwf/GwfTraceReader.kt | 168 ------ .../opendc/format/trace/gwf/GwfTraceReaderTest.kt | 41 -- opendc-format-gwf/src/test/resources/trace.gwf | 4 - opendc-format-sc18/build.gradle.kts | 50 -- .../opendc/format/environment/sc18/Model.kt | 44 -- .../environment/sc18/Sc18EnvironmentReader.kt | 95 ---- opendc-format/build.gradle.kts | 51 -- .../opendc/format/environment/EnvironmentReader.kt | 38 -- .../com/atlarge/opendc/format/trace/TraceEntry.kt | 55 -- .../com/atlarge/opendc/format/trace/TraceReader.kt | 37 -- .../com/atlarge/opendc/format/trace/TraceWriter.kt | 46 -- opendc-workflows/build.gradle.kts | 51 -- .../services/workflows/StageWorkflowScheduler.kt | 58 --- .../workflows/StageWorkflowSchedulerLogic.kt | 248 --------- .../model/services/workflows/WorkflowScheduler.kt | 47 -- .../services/workflows/WorkflowSchedulerLogic.kt | 55 -- .../services/workflows/WorkflowSchedulerMode.kt | 42 -- .../model/services/workflows/WorkflowService.kt | 193 ------- .../workflows/stages/job/FifoJobSortingPolicy.kt | 37 -- .../workflows/stages/job/JobAdmissionPolicy.kt | 48 -- .../workflows/stages/job/JobSortingPolicy.kt | 44 -- .../workflows/stages/job/NullJobAdmissionPolicy.kt | 40 -- .../workflows/stages/job/RandomJobSortingPolicy.kt | 40 -- .../resources/FirstFitResourceSelectionPolicy.kt | 40 -- .../FunctionalResourceDynamicFilterPolicy.kt | 43 -- .../resources/ResourceDynamicFilterPolicy.kt | 49 -- .../stages/resources/ResourceSelectionPolicy.kt | 48 -- .../workflows/stages/task/FifoTaskSortingPolicy.kt | 37 -- .../stages/task/FunctionalTaskEligibilityPolicy.kt | 38 -- .../stages/task/RandomTaskSortingPolicy.kt | 40 -- .../workflows/stages/task/TaskEligibilityPolicy.kt | 48 -- .../workflows/stages/task/TaskSortingPolicy.kt | 45 -- .../atlarge/opendc/model/workload/workflow/Job.kt | 48 -- .../atlarge/opendc/model/workload/workflow/Task.kt | 48 -- settings.gradle.kts | 13 +- 171 files changed, 4934 insertions(+), 10414 deletions(-) create mode 100644 CONTRIBUTING.md create mode 100644 buildSrc/build.gradle.kts create mode 100644 buildSrc/settings.gradle.kts create mode 100644 buildSrc/src/main/kotlin/dokka-convention.gradle.kts create mode 100644 buildSrc/src/main/kotlin/jacoco-aggregation.gradle.kts create mode 100644 buildSrc/src/main/kotlin/kotlin-library-convention.gradle.kts create mode 100644 buildSrc/src/main/kotlin/library.kt delete mode 100644 gradle/dokka.gradle delete mode 100644 gradle/jacoco.gradle delete mode 100644 gradle/kotlin.gradle delete mode 100644 odcsim-core/build.gradle.kts delete mode 100644 odcsim-core/src/main/kotlin/com/atlarge/odcsim/ActorContext.kt delete mode 100644 odcsim-core/src/main/kotlin/com/atlarge/odcsim/ActorPath.kt delete mode 100644 odcsim-core/src/main/kotlin/com/atlarge/odcsim/ActorRef.kt delete mode 100644 odcsim-core/src/main/kotlin/com/atlarge/odcsim/ActorSystem.kt delete mode 100644 odcsim-core/src/main/kotlin/com/atlarge/odcsim/ActorSystemFactory.kt delete mode 100644 odcsim-core/src/main/kotlin/com/atlarge/odcsim/Behavior.kt delete mode 100644 odcsim-core/src/main/kotlin/com/atlarge/odcsim/Behaviors.kt delete mode 100644 odcsim-core/src/main/kotlin/com/atlarge/odcsim/Envelope.kt delete mode 100644 odcsim-core/src/main/kotlin/com/atlarge/odcsim/Signals.kt delete mode 100644 odcsim-core/src/main/kotlin/com/atlarge/odcsim/StashBuffer.kt delete mode 100644 odcsim-core/src/main/kotlin/com/atlarge/odcsim/Time.kt delete mode 100644 odcsim-core/src/main/kotlin/com/atlarge/odcsim/TimerScheduler.kt delete mode 100644 odcsim-core/src/main/kotlin/com/atlarge/odcsim/coroutines/Behavior.kt delete mode 100644 odcsim-core/src/main/kotlin/com/atlarge/odcsim/coroutines/dsl/Receive.kt delete mode 100644 odcsim-core/src/main/kotlin/com/atlarge/odcsim/coroutines/dsl/Timeout.kt delete mode 100644 odcsim-core/src/main/kotlin/com/atlarge/odcsim/internal/ActorContext.kt delete mode 100644 odcsim-core/src/main/kotlin/com/atlarge/odcsim/internal/Behavior.kt delete mode 100644 odcsim-core/src/main/kotlin/com/atlarge/odcsim/internal/BehaviorInterpreter.kt delete mode 100644 odcsim-core/src/main/kotlin/com/atlarge/odcsim/internal/Coroutines.kt delete mode 100644 odcsim-core/src/main/kotlin/com/atlarge/odcsim/internal/StashBufferImpl.kt delete mode 100644 odcsim-core/src/main/kotlin/com/atlarge/odcsim/internal/TimerSchedulerImpl.kt delete mode 100644 odcsim-core/src/main/kotlin/com/atlarge/odcsim/internal/logging/LocationAwareLoggerImpl.kt delete mode 100644 odcsim-core/src/main/kotlin/com/atlarge/odcsim/internal/logging/LocationIgnorantLoggerImpl.kt delete mode 100644 odcsim-core/src/main/kotlin/com/atlarge/odcsim/internal/logging/LoggerImpl.kt delete mode 100644 odcsim-core/src/test/kotlin/com/atlarge/odcsim/ActorPathTest.kt delete mode 100644 odcsim-core/src/test/kotlin/com/atlarge/odcsim/BehaviorTest.kt delete mode 100644 odcsim-core/src/test/kotlin/com/atlarge/odcsim/CoroutinesTest.kt delete mode 100644 odcsim-engine-omega/build.gradle.kts delete mode 100644 odcsim-engine-omega/src/main/kotlin/com/atlarge/odcsim/engine/omega/OmegaActorSystem.kt delete mode 100644 odcsim-engine-omega/src/main/kotlin/com/atlarge/odcsim/engine/omega/OmegaActorSystemFactory.kt delete mode 100644 odcsim-engine-omega/src/main/resources/META-INF/services/com.atlarge.odcsim.ActorSystemFactory delete mode 100644 odcsim-engine-omega/src/test/kotlin/com/atlarge/odcsim/engine/omega/OmegaActorSystemFactoryTest.kt delete mode 100644 odcsim-engine-omega/src/test/kotlin/com/atlarge/odcsim/engine/omega/OmegaActorSystemTest.kt delete mode 100644 odcsim-engine-tests/build.gradle.kts delete mode 100644 odcsim-engine-tests/src/main/kotlin/com/atlarge/odcsim/engine/tests/ActorSystemContract.kt delete mode 100644 odcsim-engine-tests/src/main/kotlin/com/atlarge/odcsim/engine/tests/ActorSystemFactoryContract.kt delete mode 100644 odcsim-testkit/build.gradle.kts delete mode 100644 odcsim-testkit/src/main/kotlin/com/atlarge/odcsim/testkit/BehaviorTestKit.kt delete mode 100644 odcsim-testkit/src/main/kotlin/com/atlarge/odcsim/testkit/TestInbox.kt delete mode 100644 odcsim-testkit/src/main/kotlin/com/atlarge/odcsim/testkit/internal/ActorContextStub.kt delete mode 100644 odcsim-testkit/src/main/kotlin/com/atlarge/odcsim/testkit/internal/ActorSystemStub.kt delete mode 100644 odcsim-testkit/src/main/kotlin/com/atlarge/odcsim/testkit/internal/BehaviorTestKitImpl.kt delete mode 100644 odcsim-testkit/src/main/kotlin/com/atlarge/odcsim/testkit/internal/TestInboxImpl.kt create mode 100644 odcsim/README.md create mode 100644 odcsim/build.gradle.kts create mode 100644 odcsim/odcsim-core/build.gradle.kts create mode 100644 odcsim/odcsim-core/src/main/kotlin/com/atlarge/odcsim/ActorContext.kt create mode 100644 odcsim/odcsim-core/src/main/kotlin/com/atlarge/odcsim/ActorPath.kt create mode 100644 odcsim/odcsim-core/src/main/kotlin/com/atlarge/odcsim/ActorRef.kt create mode 100644 odcsim/odcsim-core/src/main/kotlin/com/atlarge/odcsim/ActorSystem.kt create mode 100644 odcsim/odcsim-core/src/main/kotlin/com/atlarge/odcsim/ActorSystemFactory.kt create mode 100644 odcsim/odcsim-core/src/main/kotlin/com/atlarge/odcsim/Behavior.kt create mode 100644 odcsim/odcsim-core/src/main/kotlin/com/atlarge/odcsim/Behaviors.kt create mode 100644 odcsim/odcsim-core/src/main/kotlin/com/atlarge/odcsim/Envelope.kt create mode 100644 odcsim/odcsim-core/src/main/kotlin/com/atlarge/odcsim/Signals.kt create mode 100644 odcsim/odcsim-core/src/main/kotlin/com/atlarge/odcsim/StashBuffer.kt create mode 100644 odcsim/odcsim-core/src/main/kotlin/com/atlarge/odcsim/Time.kt create mode 100644 odcsim/odcsim-core/src/main/kotlin/com/atlarge/odcsim/TimerScheduler.kt create mode 100644 odcsim/odcsim-core/src/main/kotlin/com/atlarge/odcsim/coroutines/Behavior.kt create mode 100644 odcsim/odcsim-core/src/main/kotlin/com/atlarge/odcsim/coroutines/dsl/Receive.kt create mode 100644 odcsim/odcsim-core/src/main/kotlin/com/atlarge/odcsim/coroutines/dsl/Timeout.kt create mode 100644 odcsim/odcsim-core/src/main/kotlin/com/atlarge/odcsim/internal/ActorContext.kt create mode 100644 odcsim/odcsim-core/src/main/kotlin/com/atlarge/odcsim/internal/Behavior.kt create mode 100644 odcsim/odcsim-core/src/main/kotlin/com/atlarge/odcsim/internal/BehaviorInterpreter.kt create mode 100644 odcsim/odcsim-core/src/main/kotlin/com/atlarge/odcsim/internal/Coroutines.kt create mode 100644 odcsim/odcsim-core/src/main/kotlin/com/atlarge/odcsim/internal/StashBufferImpl.kt create mode 100644 odcsim/odcsim-core/src/main/kotlin/com/atlarge/odcsim/internal/TimerSchedulerImpl.kt create mode 100644 odcsim/odcsim-core/src/main/kotlin/com/atlarge/odcsim/internal/logging/LocationAwareLoggerImpl.kt create mode 100644 odcsim/odcsim-core/src/main/kotlin/com/atlarge/odcsim/internal/logging/LocationIgnorantLoggerImpl.kt create mode 100644 odcsim/odcsim-core/src/main/kotlin/com/atlarge/odcsim/internal/logging/LoggerImpl.kt create mode 100644 odcsim/odcsim-core/src/test/kotlin/com/atlarge/odcsim/ActorPathTest.kt create mode 100644 odcsim/odcsim-core/src/test/kotlin/com/atlarge/odcsim/BehaviorTest.kt create mode 100644 odcsim/odcsim-core/src/test/kotlin/com/atlarge/odcsim/CoroutinesTest.kt create mode 100644 odcsim/odcsim-engine-omega/build.gradle.kts create mode 100644 odcsim/odcsim-engine-omega/src/main/kotlin/com/atlarge/odcsim/engine/omega/OmegaActorSystem.kt create mode 100644 odcsim/odcsim-engine-omega/src/main/kotlin/com/atlarge/odcsim/engine/omega/OmegaActorSystemFactory.kt create mode 100644 odcsim/odcsim-engine-omega/src/main/resources/META-INF/services/com.atlarge.odcsim.ActorSystemFactory create mode 100644 odcsim/odcsim-engine-omega/src/test/kotlin/com/atlarge/odcsim/engine/omega/OmegaActorSystemFactoryTest.kt create mode 100644 odcsim/odcsim-engine-omega/src/test/kotlin/com/atlarge/odcsim/engine/omega/OmegaActorSystemTest.kt create mode 100644 odcsim/odcsim-engine-tests/build.gradle.kts create mode 100644 odcsim/odcsim-engine-tests/src/main/kotlin/com/atlarge/odcsim/engine/tests/ActorSystemContract.kt create mode 100644 odcsim/odcsim-engine-tests/src/main/kotlin/com/atlarge/odcsim/engine/tests/ActorSystemFactoryContract.kt delete mode 100644 opendc-core/build.gradle.kts delete mode 100644 opendc-core/src/main/kotlin/com/atlarge/opendc/model/Broker.kt delete mode 100644 opendc-core/src/main/kotlin/com/atlarge/opendc/model/Cluster.kt delete mode 100644 opendc-core/src/main/kotlin/com/atlarge/opendc/model/Environment.kt delete mode 100644 opendc-core/src/main/kotlin/com/atlarge/opendc/model/Identity.kt delete mode 100644 opendc-core/src/main/kotlin/com/atlarge/opendc/model/Model.kt delete mode 100644 opendc-core/src/main/kotlin/com/atlarge/opendc/model/Platform.kt delete mode 100644 opendc-core/src/main/kotlin/com/atlarge/opendc/model/User.kt delete mode 100644 opendc-core/src/main/kotlin/com/atlarge/opendc/model/Zone.kt delete mode 100644 opendc-core/src/main/kotlin/com/atlarge/opendc/model/resources/compute/Machine.kt delete mode 100644 opendc-core/src/main/kotlin/com/atlarge/opendc/model/resources/compute/MachineStatus.kt delete mode 100644 opendc-core/src/main/kotlin/com/atlarge/opendc/model/resources/compute/ProcessingElement.kt delete mode 100644 opendc-core/src/main/kotlin/com/atlarge/opendc/model/resources/compute/ProcessingUnit.kt delete mode 100644 opendc-core/src/main/kotlin/com/atlarge/opendc/model/resources/compute/host/Host.kt delete mode 100644 opendc-core/src/main/kotlin/com/atlarge/opendc/model/resources/compute/scheduling/MachineScheduler.kt delete mode 100644 opendc-core/src/main/kotlin/com/atlarge/opendc/model/resources/compute/scheduling/MachineSchedulerLogic.kt delete mode 100644 opendc-core/src/main/kotlin/com/atlarge/opendc/model/resources/compute/scheduling/ProcessObserver.kt delete mode 100644 opendc-core/src/main/kotlin/com/atlarge/opendc/model/resources/compute/scheduling/ProcessState.kt delete mode 100644 opendc-core/src/main/kotlin/com/atlarge/opendc/model/resources/compute/scheduling/ProcessView.kt delete mode 100644 opendc-core/src/main/kotlin/com/atlarge/opendc/model/resources/compute/scheduling/SpaceSharedMachineScheduler.kt delete mode 100644 opendc-core/src/main/kotlin/com/atlarge/opendc/model/resources/compute/supervision/MachineSupervisionEvent.kt delete mode 100644 opendc-core/src/main/kotlin/com/atlarge/opendc/model/resources/compute/supervision/MachineSupervisor.kt delete mode 100644 opendc-core/src/main/kotlin/com/atlarge/opendc/model/services/Service.kt delete mode 100644 opendc-core/src/main/kotlin/com/atlarge/opendc/model/services/ServiceMap.kt delete mode 100644 opendc-core/src/main/kotlin/com/atlarge/opendc/model/services/ServiceProvider.kt delete mode 100644 opendc-core/src/main/kotlin/com/atlarge/opendc/model/services/provisioning/ProvisioningService.kt delete mode 100644 opendc-core/src/main/kotlin/com/atlarge/opendc/model/services/provisioning/SimpleProvisioningService.kt delete mode 100644 opendc-core/src/main/kotlin/com/atlarge/opendc/model/services/resources/HostView.kt delete mode 100644 opendc-core/src/main/kotlin/com/atlarge/opendc/model/services/resources/ResourceManagementService.kt delete mode 100644 opendc-core/src/main/kotlin/com/atlarge/opendc/model/workload/Workload.kt delete mode 100644 opendc-core/src/main/kotlin/com/atlarge/opendc/model/workload/application/Application.kt delete mode 100644 opendc-core/src/main/kotlin/com/atlarge/opendc/model/workload/application/FlopsApplication.kt delete mode 100644 opendc-core/src/main/kotlin/com/atlarge/opendc/model/workload/application/Process.kt delete mode 100644 opendc-core/src/main/kotlin/com/atlarge/opendc/model/workload/application/ProcessSupervisor.kt delete mode 100644 opendc-core/src/test/kotlin/com/atlarge/opendc/model/workload/application/FlopsApplicationTest.kt delete mode 100644 opendc-experiments-tpds/build.gradle.kts delete mode 100644 opendc-experiments-tpds/src/main/kotlin/com/atlarge/opendc/experiments/tpds/TestExperiment.kt delete mode 100644 opendc-experiments-tpds/src/main/resources/env/setup-test.json delete mode 100644 opendc-experiments-tpds/src/main/resources/log4j2.xml delete mode 100644 opendc-format-gwf/build.gradle.kts delete mode 100644 opendc-format-gwf/src/main/kotlin/com/atlarge/opendc/format/trace/gwf/GwfTraceReader.kt delete mode 100644 opendc-format-gwf/src/test/kotlin/com/atlarge/opendc/format/trace/gwf/GwfTraceReaderTest.kt delete mode 100644 opendc-format-gwf/src/test/resources/trace.gwf delete mode 100644 opendc-format-sc18/build.gradle.kts delete mode 100644 opendc-format-sc18/src/main/kotlin/com/atlarge/opendc/format/environment/sc18/Model.kt delete mode 100644 opendc-format-sc18/src/main/kotlin/com/atlarge/opendc/format/environment/sc18/Sc18EnvironmentReader.kt delete mode 100644 opendc-format/build.gradle.kts delete mode 100644 opendc-format/src/main/kotlin/com/atlarge/opendc/format/environment/EnvironmentReader.kt delete mode 100644 opendc-format/src/main/kotlin/com/atlarge/opendc/format/trace/TraceEntry.kt delete mode 100644 opendc-format/src/main/kotlin/com/atlarge/opendc/format/trace/TraceReader.kt delete mode 100644 opendc-format/src/main/kotlin/com/atlarge/opendc/format/trace/TraceWriter.kt delete mode 100644 opendc-workflows/build.gradle.kts delete mode 100644 opendc-workflows/src/main/kotlin/com/atlarge/opendc/model/services/workflows/StageWorkflowScheduler.kt delete mode 100644 opendc-workflows/src/main/kotlin/com/atlarge/opendc/model/services/workflows/StageWorkflowSchedulerLogic.kt delete mode 100644 opendc-workflows/src/main/kotlin/com/atlarge/opendc/model/services/workflows/WorkflowScheduler.kt delete mode 100644 opendc-workflows/src/main/kotlin/com/atlarge/opendc/model/services/workflows/WorkflowSchedulerLogic.kt delete mode 100644 opendc-workflows/src/main/kotlin/com/atlarge/opendc/model/services/workflows/WorkflowSchedulerMode.kt delete mode 100644 opendc-workflows/src/main/kotlin/com/atlarge/opendc/model/services/workflows/WorkflowService.kt delete mode 100644 opendc-workflows/src/main/kotlin/com/atlarge/opendc/model/services/workflows/stages/job/FifoJobSortingPolicy.kt delete mode 100644 opendc-workflows/src/main/kotlin/com/atlarge/opendc/model/services/workflows/stages/job/JobAdmissionPolicy.kt delete mode 100644 opendc-workflows/src/main/kotlin/com/atlarge/opendc/model/services/workflows/stages/job/JobSortingPolicy.kt delete mode 100644 opendc-workflows/src/main/kotlin/com/atlarge/opendc/model/services/workflows/stages/job/NullJobAdmissionPolicy.kt delete mode 100644 opendc-workflows/src/main/kotlin/com/atlarge/opendc/model/services/workflows/stages/job/RandomJobSortingPolicy.kt delete mode 100644 opendc-workflows/src/main/kotlin/com/atlarge/opendc/model/services/workflows/stages/resources/FirstFitResourceSelectionPolicy.kt delete mode 100644 opendc-workflows/src/main/kotlin/com/atlarge/opendc/model/services/workflows/stages/resources/FunctionalResourceDynamicFilterPolicy.kt delete mode 100644 opendc-workflows/src/main/kotlin/com/atlarge/opendc/model/services/workflows/stages/resources/ResourceDynamicFilterPolicy.kt delete mode 100644 opendc-workflows/src/main/kotlin/com/atlarge/opendc/model/services/workflows/stages/resources/ResourceSelectionPolicy.kt delete mode 100644 opendc-workflows/src/main/kotlin/com/atlarge/opendc/model/services/workflows/stages/task/FifoTaskSortingPolicy.kt delete mode 100644 opendc-workflows/src/main/kotlin/com/atlarge/opendc/model/services/workflows/stages/task/FunctionalTaskEligibilityPolicy.kt delete mode 100644 opendc-workflows/src/main/kotlin/com/atlarge/opendc/model/services/workflows/stages/task/RandomTaskSortingPolicy.kt delete mode 100644 opendc-workflows/src/main/kotlin/com/atlarge/opendc/model/services/workflows/stages/task/TaskEligibilityPolicy.kt delete mode 100644 opendc-workflows/src/main/kotlin/com/atlarge/opendc/model/services/workflows/stages/task/TaskSortingPolicy.kt delete mode 100644 opendc-workflows/src/main/kotlin/com/atlarge/opendc/model/workload/workflow/Job.kt delete mode 100644 opendc-workflows/src/main/kotlin/com/atlarge/opendc/model/workload/workflow/Task.kt diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 00000000..e69de29b diff --git a/README.md b/README.md index 54fa77cb..b4da4287 100644 --- a/README.md +++ b/README.md @@ -3,7 +3,7 @@ OpenDC
- odcsim + OpenDC

@@ -13,6 +13,10 @@ MIT License

+

+ Collaborative Datacenter Simulation and Exploration for Everybody +

+ ## Introduction **odcsim** is a framework for discrete event simulation in Kotlin and Java, used @@ -33,79 +37,6 @@ The documentation is located in the [docs/](docs/) directory and is divided as f * [API Reference](https://atlarge-research.github.io/opendc-simulator) * [Contributing Guide](CONTRIBUTING.md) -## Getting Started - -### Installation -Please add the required packages as dependency in your project. -Releases are available in the [Maven Central Repository](https://search.maven.org/). - -The package `odcsim-core` is required to construct a simulation model. -A `odcsim-engine-*` package is needed for running the simulation -model. - -**Gradle** -```groovy -compile 'com.atlarge.odcsim:odcsim-core:2.0.0' -compile 'com.atlarge.odcsim:odcsim-engine-omega:2.0.0' -``` - -**Maven** -```xml - - com.atlarge.odcsim - odcsim-core - 2.0.0 - - - - com.atlarge.odcsim - odcsim-engine-omega - 2.0.0 - -``` - -### Construction of Simulation Model -Let's construct a simple simulation model of a single car actor. -The car will alternately drive and park for a while. When it starts -driving (or parking), it will print the current simulation time. - - -```kotlin -import com.atlarge.odcsim.Behavior -import com.atlarge.odcsim.coroutines.suspending -import com.atlarge.odcsim.coroutines.dsl.timeout - -fun car(): Behavior = - suspending { ctx -> - while (true) { - println("Start parking at ${ctx.time}") - val parkingDuration = 5.0 - timeout(parkingDuration) - - println("Start driving at ${ctx.time}") - val tripDuration = 2.0 - timeout(tripDuration) - } - - stopped() - } -``` - -### Running Simulation -Running the constructed simulation model requires an implementation -of the `ActorSystem` interface provided by one of the `odcsim-engine-*` -packages. The [ServiceLoader](https://docs.oracle.com/javase/9/docs/api/java/util/ServiceLoader.html) -class found in the JDK can be used to locate the `ActorSystem` implementation on the classpath. -```kotlin -import com.atlarge.odcsim.ActorSystemFactory -import java.util.ServiceLoader - -val factory = ServiceLoader.load(ActorSystemFactory::class.java).first() -val system = factory(car(), name = "car") -system.run(until = 10.0) -system.terminate() -``` - ## Contributing ### Contributing Guide Read our [contributing guide](CONTRIBUTING.md) to learn about our @@ -113,4 +44,4 @@ development process, how to propose bug fixes and improvements, and how to build and test your changes to the project. ### License -**odcsim** is [MIT licensed](https://github.com/atlarge-research/opendc-simulator/blob/master/LICENSE.txt). +The OpenDC simulator is [MIT licensed](https://github.com/atlarge-research/opendc-simulator/blob/master/LICENSE.txt). diff --git a/build.gradle.kts b/build.gradle.kts index 01f95b26..1dd5132c 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -23,29 +23,18 @@ */ plugins { - kotlin("jvm") version "1.3.30" apply false - id("org.jetbrains.dokka") version "0.9.18" apply false - id("org.jlleitschuh.gradle.ktlint") version "7.4.0" apply false + `dokka-convention` } allprojects { group = "com.atlarge.opendc" version = "2.0.0" - extra["junitJupiterVersion"] = "5.4.2" extra["junitPlatformVersion"] = "1.4.2" extra["githubUrl"] = "https://github.com/atlarge-research/${rootProject.name}" } tasks.wrapper { - gradleVersion = "5.1" -} - -// Wait for children to evaluate so we can configure tasks that are dependant on children -project.evaluationDependsOnChildren() - -apply { - from("gradle/jacoco.gradle") - from("gradle/dokka.gradle") + gradleVersion = "6.0" } diff --git a/buildSrc/build.gradle.kts b/buildSrc/build.gradle.kts new file mode 100644 index 00000000..ee61f351 --- /dev/null +++ b/buildSrc/build.gradle.kts @@ -0,0 +1,46 @@ +/* + * MIT License + * + * Copyright (c) 2019 atlarge-research + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +plugins { + `kotlin-dsl` +} + +kotlinDslPluginOptions { + experimentalWarning.set(false) +} + + +/* Project configuration */ +repositories { + jcenter() + maven { + url = uri("https://plugins.gradle.org/m2/") + } +} + +dependencies { + implementation(kotlin("gradle-plugin", "1.3.60")) + implementation("org.jlleitschuh.gradle:ktlint-gradle:9.1.1") + implementation("org.jetbrains.dokka:dokka-gradle-plugin:0.10.0") +} diff --git a/buildSrc/settings.gradle.kts b/buildSrc/settings.gradle.kts new file mode 100644 index 00000000..cc3f3add --- /dev/null +++ b/buildSrc/settings.gradle.kts @@ -0,0 +1,23 @@ +/* + * MIT License + * + * Copyright (c) 2019 atlarge-research + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ diff --git a/buildSrc/src/main/kotlin/dokka-convention.gradle.kts b/buildSrc/src/main/kotlin/dokka-convention.gradle.kts new file mode 100644 index 00000000..95183df2 --- /dev/null +++ b/buildSrc/src/main/kotlin/dokka-convention.gradle.kts @@ -0,0 +1,33 @@ +/* + * MIT License + * + * Copyright (c) 2019 atlarge-research + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +import org.jetbrains.dokka.gradle.DokkaTask + +plugins { + id("org.jetbrains.dokka") +} + +tasks.getting(DokkaTask::class) { + outputFormat = "html" +} diff --git a/buildSrc/src/main/kotlin/jacoco-aggregation.gradle.kts b/buildSrc/src/main/kotlin/jacoco-aggregation.gradle.kts new file mode 100644 index 00000000..8c07fdea --- /dev/null +++ b/buildSrc/src/main/kotlin/jacoco-aggregation.gradle.kts @@ -0,0 +1,50 @@ +/* + * MIT License + * + * Copyright (c) 2019 atlarge-research + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + + +plugins { + jacoco +} + +repositories { + mavenLocal() + mavenCentral() +} + +task("jacocoTestReport") { + group = "Coverage reports" + description = "Generates an aggregate report from all subprojects" + + val jacocoReportTasks = subprojects.map { it.tasks.withType() } + dependsOn(jacocoReportTasks) + executionData(jacocoReportTasks) + + subprojects.forEach { testedProject -> + val sourceSets = testedProject.convention.findPlugin(JavaPluginConvention::class)?.sourceSets ?: return@forEach + + this@task.additionalSourceDirs.from(sourceSets["main"].allSource.srcDirs) + this@task.sourceDirectories.from(sourceSets["main"].allSource.srcDirs) + this@task.classDirectories.from(sourceSets["main"].output) + } +} diff --git a/buildSrc/src/main/kotlin/kotlin-library-convention.gradle.kts b/buildSrc/src/main/kotlin/kotlin-library-convention.gradle.kts new file mode 100644 index 00000000..b27b12f5 --- /dev/null +++ b/buildSrc/src/main/kotlin/kotlin-library-convention.gradle.kts @@ -0,0 +1,57 @@ +/* + * MIT License + * + * Copyright (c) 2019 atlarge-research + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +import org.jetbrains.kotlin.gradle.tasks.KotlinCompile + +plugins { + `java-library` + kotlin("jvm") + id("org.jlleitschuh.gradle.ktlint") + jacoco +} + +/* Project configuration */ +repositories { + jcenter() +} + +java { + sourceCompatibility = JavaVersion.VERSION_1_8 +} + +tasks.withType().configureEach { + kotlinOptions.jvmTarget = "1.8" +} + +tasks.test { + useJUnitPlatform() + reports.html.isEnabled = true +} + + +tasks.jacocoTestReport { + reports { + html.isEnabled = true + } +} diff --git a/buildSrc/src/main/kotlin/library.kt b/buildSrc/src/main/kotlin/library.kt new file mode 100644 index 00000000..44355614 --- /dev/null +++ b/buildSrc/src/main/kotlin/library.kt @@ -0,0 +1,44 @@ +/* + * MIT License + * + * Copyright (c) 2019 atlarge-research + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +/** + * This object contains the versions of the dependencies shared by the different + * subprojects. + */ +object Library { + /** + * The library for testing the projects. + */ + val JUNIT_JUPITER = "5.5.2" + + /** + * The library for hosting the tests. + */ + val JUNIT_PLATFORM = "1.5.2" + + /** + * Logging facade. + */ + val SLF4J = "1.7.29" +} diff --git a/gradle/dokka.gradle b/gradle/dokka.gradle deleted file mode 100644 index 576dbbeb..00000000 --- a/gradle/dokka.gradle +++ /dev/null @@ -1,47 +0,0 @@ -/* - * MIT License - * - * Copyright (c) 2018 atlarge-research - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -/* Documentation generation for project */ -apply plugin: 'org.jetbrains.dokka' - -repositories { - jcenter() -} - -dokka { - moduleName = 'odcsim' - outputFormat = 'html' - kotlinTasks { - subprojects.compileKotlin - } - includes = ['docs/packages.md'] - samples = [] - jdkVersion = 8 - - linkMapping { - dir = rootProject.projectDir.absolutePath - url = "$githubUrl/tree/master" - suffix = '#L' - } -} diff --git a/gradle/jacoco.gradle b/gradle/jacoco.gradle deleted file mode 100644 index 687ad093..00000000 --- a/gradle/jacoco.gradle +++ /dev/null @@ -1,55 +0,0 @@ -/* - * MIT License - * - * Copyright (c) 2018 atlarge-research - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -/* Jacoco task for root project to generate merged report */ -apply plugin: 'jacoco' - -repositories { - mavenLocal() - mavenCentral() -} - -task jacocoMerge(type: JacocoMerge, dependsOn: subprojects.test) { - executionData = files(subprojects.jacocoTestReport.executionData) - - doFirst { - executionData = files(executionData.findAll { it.exists() }) - } -} - -task jacocoTestReport(type: JacocoReport, dependsOn: jacocoMerge) { - group = 'Coverage reports' - description = 'Generate an aggregate report from all subprojects' - - executionData tasks.jacocoMerge.destinationFile - subprojects.each { - sourceSets it.sourceSets.main - } - - reports { - html.enabled = true - xml.enabled = true - xml.destination file("${buildDir}/reports/jacoco/report.xml") - } -} diff --git a/gradle/kotlin.gradle b/gradle/kotlin.gradle deleted file mode 100644 index e24a3c70..00000000 --- a/gradle/kotlin.gradle +++ /dev/null @@ -1,64 +0,0 @@ -/* - * MIT License - * - * Copyright (c) 2017 atlarge-research - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -/* Default configuration for Kotlin projects */ -apply plugin: 'kotlin' -apply plugin: 'org.jlleitschuh.gradle.ktlint' -apply plugin: 'jacoco' - -sourceCompatibility = 1.8 - -compileKotlin { - kotlinOptions { - jvmTarget = '1.8' - } -} - -compileTestKotlin { - kotlinOptions { - jvmTarget = '1.8' - } -} - -/* Configure test setup */ -test { - useJUnitPlatform {} - - testLogging { - events 'passed', 'skipped', 'failed' - } - - reports { - html.enabled = true - } - - finalizedBy jacocoTestReport -} - -/* Coverage */ -jacocoTestReport { - reports { - html.enabled = true - } -} diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index 6b3851a8..6ce793f2 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,5 +1,5 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-5.1-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-6.0-bin.zip zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists diff --git a/odcsim-core/build.gradle.kts b/odcsim-core/build.gradle.kts deleted file mode 100644 index 4260aaad..00000000 --- a/odcsim-core/build.gradle.kts +++ /dev/null @@ -1,47 +0,0 @@ -/* - * MIT License - * - * Copyright (c) 2017 atlarge-research - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -/* Build configuration */ -apply(from = "../gradle/kotlin.gradle") -plugins { - `java-library` -} - -/* Project configuration */ -repositories { - jcenter() -} - -val junitJupiterVersion: String by extra -val junitPlatformVersion: String by extra - -dependencies { - implementation(kotlin("stdlib")) - api("org.slf4j:slf4j-api:1.7.26") - - testImplementation("org.junit.jupiter:junit-jupiter-api:$junitJupiterVersion") - testRuntimeOnly("org.junit.jupiter:junit-jupiter-engine:$junitJupiterVersion") - testImplementation("org.junit.platform:junit-platform-launcher:$junitPlatformVersion") - testImplementation("com.nhaarman.mockitokotlin2:mockito-kotlin:2.0.0") -} diff --git a/odcsim-core/src/main/kotlin/com/atlarge/odcsim/ActorContext.kt b/odcsim-core/src/main/kotlin/com/atlarge/odcsim/ActorContext.kt deleted file mode 100644 index dc6ca7ec..00000000 --- a/odcsim-core/src/main/kotlin/com/atlarge/odcsim/ActorContext.kt +++ /dev/null @@ -1,175 +0,0 @@ -/* - * MIT License - * - * Copyright (c) 2018 atlarge-research - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -package com.atlarge.odcsim - -import org.slf4j.Logger - -/** - * Represents the context in which the execution of an actor's behavior takes place. - * - * @param T The shape of the messages the actor accepts. - */ -interface ActorContext { - /** - * The identity of the actor, bound to the lifecycle of this actor instance. - */ - val self: ActorRef - - /** - * A view of the children of this actor. - */ - val children: List> - - /** - * The point of time within the simulation. - */ - val time: Instant - - /** - * The [ActorSystem] the actor is part of. - */ - val system: ActorSystem<*> - - /** - * An actor specific logger instance. - */ - val log: Logger - - /** - * Obtain the child of this actor with the specified name. - * - * @param name The name of the child actor to obtain. - * @return The reference to the child actor or `null` if it does not exist. - */ - fun getChild(name: String): ActorRef<*>? - - /** - * Send the specified message to the actor referenced by this [ActorRef]. - * - * Please note that callees must guarantee that messages are sent strictly in increasing time. - * If so, this method guarantees that: - * - A message will never be received earlier than specified - * - A message might arrive later than specified if the two actors are not synchronized. - * - * @param ref The actor to send the message to. - * @param msg The message to send to the referenced actor. - * @param after The delay after which the message should be received by the actor. - */ - fun send(ref: ActorRef, msg: U, after: Duration = 0.0) - - /** - * Spawn a child actor from the given [Behavior] and with the specified name. - * - * The name may not be empty or start with "$". Moreover, the name of an actor must be unique and this method - * will throw an [IllegalArgumentException] in case a child actor of the given name already exists. - * - * @param behavior The behavior of the child actor to spawn. - * @param name The name of the child actor to spawn. - * @return A reference to the child that has/will be spawned. - */ - fun spawn(behavior: Behavior, name: String): ActorRef - - /** - * Spawn an anonymous child actor from the given [Behavior]. - * - * @param behavior The behavior of the child actor to spawn. - * @return A reference to the child that has/will be spawned. - */ - fun spawnAnonymous(behavior: Behavior): ActorRef - - /** - * Force the specified child actor to terminate after it finishes processing its current message. - * Nothing will happen if the child is already stopped. - * - * Only direct children of an actor may be stopped through the actor context. Trying to stop other actors via this - * method will result in an [IllegalArgumentException]. Instead, stopping other actors has to be expressed as - * an explicit stop message that the actor accept. - * - * @param child The reference to the child actor to stop. - */ - fun stop(child: ActorRef<*>) - - /** - * Watch the specified [ActorRef] for termination of the referenced actor. On termination of the watched actor, - * a [Terminated] signal is sent to this actor. - * - * @param target The target actor to watch. - */ - fun watch(target: ActorRef<*>) - - /** - * Revoke the registration established by [watch]. - * - * In case there exists no registration for the specified [target], no action will be performed. - * - * @param target The target actor to unwatch. - */ - fun unwatch(target: ActorRef<*>) - - /** - * Synchronize the local virtual time of this target with the other referenced actor's local virtual time. - * - * By default, actors are not guaranteed to be synchronized, meaning that for some implementations, virtual time may - * drift between different actors. Synchronization between two actors ensures that virtual time remains consistent - * between at least the two actors. - * - * Be aware that this method may cause a jump in virtual time in order to get consistent with [target]. - * Furthermore, please note that synchronization might incur performance degradation and should only be used - * when necessary. - * - * @param target The reference to the target actor to synchronize with. - */ - fun sync(target: ActorRef<*>) - - /** - * Desynchronize virtual time between two actors if possible. - * - * Please note that this method only provides a hint to the [ActorSystem] that it may drop synchronization between - * the actors, but [ActorSystem] is not compelled to actually do so (i.e. in the case where synchronization is - * always guaranteed). - * - * Furthermore, if [target] is already desychronized, the method should return without error. [ActorContext.isSync] - * may be used to determine if an actor is synchronized. - * - * @param target The reference to the target actor to desynchronize with. - */ - fun unsync(target: ActorRef<*>) - - /** - * Determine whether this actor and [target] are synchronized in virtual time. - * - * @param target The target to check for synchronization. - * @return `true` if [target] is synchronized with this actor, `false` otherwise. - */ - fun isSync(target: ActorRef<*>): Boolean -} - -/** - * Unsafe helper method for widening the type accepted by this [ActorContext]. - */ -fun ActorContext.unsafeCast(): ActorContext { - @Suppress("UNCHECKED_CAST") - return this as ActorContext -} diff --git a/odcsim-core/src/main/kotlin/com/atlarge/odcsim/ActorPath.kt b/odcsim-core/src/main/kotlin/com/atlarge/odcsim/ActorPath.kt deleted file mode 100644 index a6c716a2..00000000 --- a/odcsim-core/src/main/kotlin/com/atlarge/odcsim/ActorPath.kt +++ /dev/null @@ -1,142 +0,0 @@ -/* - * MIT License - * - * Copyright (c) 2018 atlarge-research - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -package com.atlarge.odcsim - -import java.io.Serializable - -/** - * An actor path represents the unique path to a specific actor instance within an [ActorSystem]. - */ -sealed class ActorPath : Comparable, Serializable { - /** - * The name of the actor that this path refers to. - */ - abstract val name: String - - /** - * The path for the parent actor. - */ - abstract val parent: ActorPath - - /** - * Walk up the tree to obtain and return the [ActorPath.Root]. - */ - abstract val root: Root - - /** - * Create a new child actor path. - */ - fun child(name: String): ActorPath = Child(this, name) - - /** - * Create a new child actor path. - */ - operator fun div(name: String): ActorPath = child(name) - - /** - * Recursively create a descendant’s path by appending all child names. - */ - fun descendant(children: Iterable): ActorPath = children.fold(this) { parent, name -> - if (name.isNotBlank()) child(name) else parent - } - - /** - * Root of the hierarchy of [ActorPath]s. There is exactly root per [ActorSystem]. - */ - data class Root(override val name: String = "/") : ActorPath() { - init { - require(name.length == 1 || name.indexOf('/', 1) == -1) { - "/ may only exist at the beginning of the root actors name" - } - require(name.indexOf('#') == -1) { "# may not exist in a path component" } - } - - override val parent: ActorPath = this - - override val root: Root = this - - /** - * Compare the [specified][other] path with this root node for order. If both paths are roots, compare their - * name, otherwise the root is ordered higher. - * - * @return a negative integer, zero, or a positive integer as this object is less than, equal to, or greater - * than the specified path. - */ - override fun compareTo(other: ActorPath): Int = if (other is Root) name.compareTo(other.name) else 1 - - /** - * Create a string representation of this root node which prints its own [name]. - * - * @return A string representation of this node. - */ - override fun toString(): String = name - } - - /** - * A child in the hierarchy of [ActorPath]s. - */ - data class Child(override val parent: ActorPath, override val name: String) : ActorPath() { - init { - require(name.indexOf('/') == -1) { "/ may not exist in a path component" } - require(name.indexOf('#') == -1) { "# may not exist in a path component" } - } - - override val root: Root by lazy { - when (parent) { - is Root -> parent - else -> parent.root - } - } - - /** - * Compare the [specified][other] path with this child node for order. - * - * @return a negative integer, zero, or a positive integer as this object is less than, equal to, or greater - * than the specified path. - */ - override fun compareTo(other: ActorPath): Int { - tailrec fun rec(left: ActorPath, right: ActorPath): Int = when { - left == right -> 0 - left is Root -> left.compareTo(right) - right is Root -> -(right.compareTo(left)) - else -> { - val x = left.name.compareTo(right.name) - if (x == 0) - rec(left.parent, right.parent) - else - x - } - } - return rec(this, other) - } - - /** - * Create a string representation of this child node which prints the name of [parent] and its own [name]. - * - * @return A string representation of this node. - */ - override fun toString(): String = "$parent/$name" - } -} diff --git a/odcsim-core/src/main/kotlin/com/atlarge/odcsim/ActorRef.kt b/odcsim-core/src/main/kotlin/com/atlarge/odcsim/ActorRef.kt deleted file mode 100644 index 45fc756e..00000000 --- a/odcsim-core/src/main/kotlin/com/atlarge/odcsim/ActorRef.kt +++ /dev/null @@ -1,50 +0,0 @@ -/* - * MIT License - * - * Copyright (c) 2018 atlarge-research - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -package com.atlarge.odcsim - -import java.io.Serializable - -/** - * A reference to an entity in simulation that accepts messages of type [T]. - */ -interface ActorRef : Comparable>, Serializable { - /** - * The path for this actor (from this actor up to the root actor). - */ - val path: ActorPath - - /** - * Compare this reference to another actor reference. - */ - override fun compareTo(other: ActorRef<*>): Int = path.compareTo(other.path) -} - -/** - * Unsafe helper method for widening the type accepted by this [ActorRef]. - */ -fun ActorRef.unsafeCast(): ActorRef { - @Suppress("UNCHECKED_CAST") - return this as ActorRef -} diff --git a/odcsim-core/src/main/kotlin/com/atlarge/odcsim/ActorSystem.kt b/odcsim-core/src/main/kotlin/com/atlarge/odcsim/ActorSystem.kt deleted file mode 100644 index d65beebd..00000000 --- a/odcsim-core/src/main/kotlin/com/atlarge/odcsim/ActorSystem.kt +++ /dev/null @@ -1,76 +0,0 @@ -/* - * MIT License - * - * Copyright (c) 2018 atlarge-research - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -package com.atlarge.odcsim - -/** - * An actor system is a hierarchical grouping of actors that represents a discrete event simulation. - * - * An implementation of this interface should be provided by an engine. See for example *odcsim-engine-omega*, - * which is the reference implementation of the *odcsim* API. - * - * @param T The shape of the messages the root actor in the system can receive. - */ -interface ActorSystem : ActorRef { - /** - * The current point in simulation time. - */ - val time: Instant - - /** - * The name of this engine instance, used to distinguish between multiple engines running within the same JVM. - */ - val name: String - - /** - * Run the actors until the specified point in simulation time. - * - * @param until The point until which the simulation should run. - */ - fun run(until: Duration = Duration.POSITIVE_INFINITY) - - /** - * Send the specified message to the root actor of this [ActorSystem]. - * - * @param msg The message to send to the referenced actor. - * @param after The delay after which the message should be received by the actor. - */ - fun send(msg: T, after: Duration = 0.1) - - /** - * Terminates this actor system in an asynchronous fashion. - * - * This will stop the root actor and in turn will recursively stop all its child actors. - */ - fun terminate() - - /** - * Create an actor in the "/system" namespace. This actor will be shut down during `system.terminate()` only after - * all user actors have terminated. - * - * @param behavior The behavior of the system actor to spawn. - * @param name The name of the system actor to spawn. - */ - suspend fun spawnSystem(behavior: Behavior, name: String): ActorRef -} diff --git a/odcsim-core/src/main/kotlin/com/atlarge/odcsim/ActorSystemFactory.kt b/odcsim-core/src/main/kotlin/com/atlarge/odcsim/ActorSystemFactory.kt deleted file mode 100644 index f59bc966..00000000 --- a/odcsim-core/src/main/kotlin/com/atlarge/odcsim/ActorSystemFactory.kt +++ /dev/null @@ -1,38 +0,0 @@ -/* - * MIT License - * - * Copyright (c) 2018 atlarge-research - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -package com.atlarge.odcsim - -/** - * A factory for [ActorSystem] instances that allows users to dynamically load engine implementations. - */ -interface ActorSystemFactory { - /** - * Create an [ActorSystem] with the given root [Behavior] and the given name. - * - * @param root The behavior of the root actor. - * @param name The name of the engine instance. - */ - operator fun invoke(root: Behavior, name: String): ActorSystem -} diff --git a/odcsim-core/src/main/kotlin/com/atlarge/odcsim/Behavior.kt b/odcsim-core/src/main/kotlin/com/atlarge/odcsim/Behavior.kt deleted file mode 100644 index 9ad7f83f..00000000 --- a/odcsim-core/src/main/kotlin/com/atlarge/odcsim/Behavior.kt +++ /dev/null @@ -1,193 +0,0 @@ -/* - * MIT License - * - * Copyright (c) 2018 atlarge-research - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -package com.atlarge.odcsim - -/** - * The representation of the behavior of an actor. - * - * Behavior can be formulated using factory methods on the companion object or by extending either [DeferredBehavior] or - * [ReceivingBehavior]. - * - * Users are advised not to close over [ActorContext] within [Behavior], as it will causes it to become immobile, - * meaning it cannot be moved to another context and executed there, and therefore it cannot be replicated or forked - * either. - * - * @param T The shape of the messages the behavior accepts. - */ -sealed class Behavior { - /** - * Narrow the type of this behavior. - * - * This is almost always a safe operation, but might cause [ClassCastException] in case a narrowed behavior sends - * messages of a different type to itself and is chained via [Behavior.orElse]. - */ - fun narrow(): Behavior = unsafeCast() - - /** - * Widen the type of this behavior by placing a funnel in front of it. - * - * @param transform The mapping from the widened type to the original type, returning `null` in-case the message - * should not be handled. - */ - fun widen(transform: (U) -> T?): Behavior { - return wrap(this) { interpreter -> - receive { ctx, msg -> - val res = transform(msg) - @Suppress("UNCHECKED_CAST") - if (res == null || interpreter.interpretMessage(ctx as ActorContext, res)) - unhandled() - else - interpreter.behavior.unsafeCast() - }.unsafeCast() - }.unsafeCast() - } - - /** - * Compose this [Behavior] with a fallback [Behavior] which is used in case this [Behavior] does not handle the - * incoming message or signal. - * - * @param that The fallback behavior. - */ - fun orElse(that: Behavior): Behavior = - wrap(this) { left -> - wrap(that) { right -> - object : ReceivingBehavior() { - override fun receive(ctx: ActorContext, msg: T): Behavior { - if (left.interpretMessage(ctx, msg)) { - return left.behavior - } else if (right.interpretMessage(ctx, msg)) { - return right.behavior - } - - return unhandled() - } - - override fun receiveSignal(ctx: ActorContext, signal: Signal): Behavior { - if (left.interpretSignal(ctx, signal)) { - return left.behavior - } else if (right.interpretSignal(ctx, signal)) { - return right.behavior - } - - return unhandled() - } - } - } - } - - /** - * Unsafe utility method for changing the type accepted by this [Behavior]. - * Be aware that changing the type might result in [ClassCastException], when sending a message to the resulting - * behavior. - */ - fun unsafeCast(): Behavior { - @Suppress("UNCHECKED_CAST") - return this as Behavior - } -} - -/** - * A [Behavior] that defers the construction of the actual [Behavior] until the actor is started in some [ActorContext]. - * If the actor is already started, it will immediately evaluate. - * - * @param T The shape of the messages the behavior accepts. - */ -abstract class DeferredBehavior : Behavior() { - /** - * Create a [Behavior] instance in the [specified][ctx] [ActorContext]. - * - * @param ctx The [ActorContext] in which the behavior runs. - * @return The actor's next behavior. - */ - abstract operator fun invoke(ctx: ActorContext): Behavior -} - -/** - * A [Behavior] that concretely defines how an actor will react to the messages and signals it receives. - * The message may either be of the type that the actor declares and which is part of the [ActorRef] signature, - * or it may be a system [Signal] that expresses a lifecycle event of either this actor or one of its child actors. - * - * @param T The shape of the messages the behavior accepts. - */ -abstract class ReceivingBehavior : Behavior() { - /** - * Process an incoming message of type [T] and return the actor's next behavior. - * - * The returned behavior can in addition to normal behaviors be one of the canned special objects: - * - returning [stopped] will terminate this Behavior - * - returning [same] designates to reuse the current Behavior - * - returning [unhandled] keeps the same Behavior and signals that the message was not yet handled - * - * @param ctx The [ActorContext] in which the actor is currently running. - * @param msg The message that was received. - */ - open fun receive(ctx: ActorContext, msg: T): Behavior = unhandled() - - /** - * Process an incoming [Signal] and return the actor's next behavior. - * - * The returned behavior can in addition to normal behaviors be one of the canned special objects: - * - returning [stopped] will terminate this Behavior - * - returning [same] designates to reuse the current Behavior - * - returning [unhandled] keeps the same Behavior and signals that the message was not yet handled - * - * @param ctx The [ActorContext] in which the actor is currently running. - * @param signal The [Signal] that was received. - */ - open fun receiveSignal(ctx: ActorContext, signal: Signal): Behavior = unhandled() -} - -/** - * A flag to indicate whether a [Behavior] instance is still alive. - */ -val Behavior.isAlive get() = this !is StoppedBehavior - -/** - * A flag to indicate whether the last message/signal went unhandled. - */ -val Behavior.isUnhandled get() = this is UnhandledBehavior - -// The special behaviors are kept in this file as to be able to seal the Behavior class to prevent users from extending -// it. -/** - * A special [Behavior] instance that signals that the actor has stopped. - */ -internal object StoppedBehavior : Behavior() { - override fun toString() = "Stopped" -} - -/** - * A special [Behavior] object to signal that the actor wants to reuse its previous behavior. - */ -internal object SameBehavior : Behavior() { - override fun toString() = "Same" -} - -/** - * A special [Behavior] object that indicates that the last message or signal was not handled. - */ -internal object UnhandledBehavior : Behavior() { - override fun toString() = "Unhandled" -} diff --git a/odcsim-core/src/main/kotlin/com/atlarge/odcsim/Behaviors.kt b/odcsim-core/src/main/kotlin/com/atlarge/odcsim/Behaviors.kt deleted file mode 100644 index eac254ec..00000000 --- a/odcsim-core/src/main/kotlin/com/atlarge/odcsim/Behaviors.kt +++ /dev/null @@ -1,223 +0,0 @@ -/* - * MIT License - * - * Copyright (c) 2019 atlarge-research - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ -@file:JvmName("Behaviors") -package com.atlarge.odcsim - -import com.atlarge.odcsim.internal.BehaviorInterpreter -import com.atlarge.odcsim.internal.EmptyBehavior -import com.atlarge.odcsim.internal.IgnoreBehavior -import com.atlarge.odcsim.internal.TimerSchedulerImpl -import com.atlarge.odcsim.internal.sendSignal - -/** - * This [Behavior] is used to signal that this actor shall terminate voluntarily. If this actor has created child actors - * then these will be stopped as part of the shutdown procedure. - */ -fun stopped(): Behavior = StoppedBehavior.unsafeCast() - -/** - * This [Behavior] is used to signal that this actor wants to reuse its previous behavior. - */ -fun same(): Behavior = SameBehavior.unsafeCast() - -/** - * This [Behavior] is used to signal to the system that the last message or signal went unhandled. This will - * reuse the previous behavior. - */ -fun unhandled(): Behavior = UnhandledBehavior.unsafeCast() - -/** - * A factory for [Behavior]. Creation of the behavior instance is deferred until the actor is started. - */ -fun setup(block: (ActorContext) -> Behavior): Behavior { - return object : DeferredBehavior() { - override fun invoke(ctx: ActorContext): Behavior = block(ctx) - } -} - -/** - * A [Behavior] that ignores any incoming message or signal and keeps the same behavior. - */ -fun ignore(): Behavior = IgnoreBehavior.narrow() - -/** - * A [Behavior] that treats every incoming message or signal as unhandled. - */ -fun empty(): Behavior = EmptyBehavior.narrow() - -/** - * Construct a [Behavior] that reacts to incoming messages, provides access to the [ActorContext] and returns the - * actor's next behavior. - */ -fun receive(handler: (ActorContext, T) -> Behavior): Behavior { - return object : ReceivingBehavior() { - override fun receive(ctx: ActorContext, msg: T): Behavior = handler(ctx, msg) - } -} - -/** - * Construct a [Behavior] that reacts to incoming messages of type [U], provides access to the [ActorContext] and - * returns the actor's next behavior. Other messages will be unhandled. - */ -inline fun receiveOf(crossinline handler: (ActorContext, U) -> Behavior): Behavior { - return object : ReceivingBehavior() { - override fun receive(ctx: ActorContext, msg: T): Behavior { - return if (msg is U) - handler(ctx, msg) - else - unhandled() - } - } -} - -/** - * Construct a [Behavior] that reacts to incoming messages and returns the actor's next behavior. - */ -fun receiveMessage(handler: (T) -> Behavior): Behavior { - return object : ReceivingBehavior() { - override fun receive(ctx: ActorContext, msg: T): Behavior = handler(msg) - } -} - -/** - * Construct a [Behavior] that reacts to incoming signals, provides access to the [ActorContext] and returns the - * actor's next behavior. - */ -fun receiveSignal(handler: (ActorContext, Signal) -> Behavior): Behavior { - return object : ReceivingBehavior() { - override fun receiveSignal(ctx: ActorContext, signal: Signal): Behavior = handler(ctx, signal) - } -} - -/** - * Construct a [Behavior] that wraps another behavior instance and uses a [BehaviorInterpreter] to pass incoming - * messages and signals to the wrapper behavior. - */ -fun wrap(behavior: Behavior, wrap: (BehaviorInterpreter) -> Behavior): Behavior { - return setup { ctx -> wrap(BehaviorInterpreter(behavior, ctx)) } -} - -/** - * Obtain a [TimerScheduler] for building a [Behavior] instance. - */ -fun withTimers(handler: (TimerScheduler) -> Behavior): Behavior { - return setup { ctx -> - val scheduler = TimerSchedulerImpl(ctx) - receiveSignal { _, signal -> - if (signal is TimerSchedulerImpl.TimerSignal) { - val res = scheduler.interceptTimerSignal(signal) - if (res != null) { - ctx.send(ctx.self, res) - return@receiveSignal same() - } - } - unhandled() - }.join(handler(scheduler)) - } -} - -/** - * Construct a [Behavior] that waits for the specified duration before constructing the next behavior. - * - * @param after The delay before constructing the next behavior. - * @param handler The handler to construct the behavior with. - */ -fun withTimeout(after: Duration, handler: (ActorContext) -> Behavior): Behavior = - setup { ctx -> - val target = Any() - ctx.sendSignal(ctx.self, Timeout(target), after) - receiveSignal { _, signal -> - if (signal is Timeout && signal.target == target) { - handler(ctx) - } else { - unhandled() - } - } - } - -/** - * Join together both [Behavior] with another [Behavior], essentially running them side-by-side, only directly - * propagating stopped behavior. - * - * @param that The behavior to join with. - */ -fun Behavior.join(that: Behavior): Behavior = - wrap(this) { left -> - wrap(that) { right -> - object : ReceivingBehavior() { - override fun receive(ctx: ActorContext, msg: T): Behavior { - if (left.interpretMessage(ctx, msg)) { - return left.propagate(this) // Propagate stopped behavior - } else if (right.interpretMessage(ctx, msg)) { - return right.propagate(this) - } - - return unhandled() - } - - override fun receiveSignal(ctx: ActorContext, signal: Signal): Behavior { - if (left.interpretSignal(ctx, signal)) { - return left.propagate(this) - } else if (right.interpretSignal(ctx, signal)) { - return right.propagate(this) - } - - return unhandled() - } - } - } - } - -/** - * Widen the type of messages the [Behavior] by marking all other messages as unhandled. - */ -inline fun Behavior.widen(): Behavior = widen { - if (it is T) - it - else - null -} - -/** - * Keep the specified [Behavior] alive if it returns the stopped behavior. - */ -fun Behavior.keepAlive(): Behavior = - wrap(this) { interpreter -> - object : ReceivingBehavior() { - override fun receive(ctx: ActorContext, msg: T): Behavior { - if (interpreter.interpretMessage(ctx, msg)) { - return this - } - return empty() - } - - override fun receiveSignal(ctx: ActorContext, signal: Signal): Behavior { - if (interpreter.interpretSignal(ctx, signal)) { - return this - } - - return empty() - } - } - } diff --git a/odcsim-core/src/main/kotlin/com/atlarge/odcsim/Envelope.kt b/odcsim-core/src/main/kotlin/com/atlarge/odcsim/Envelope.kt deleted file mode 100644 index 3b73d52d..00000000 --- a/odcsim-core/src/main/kotlin/com/atlarge/odcsim/Envelope.kt +++ /dev/null @@ -1,57 +0,0 @@ -/* - * MIT License - * - * Copyright (c) 2019 atlarge-research - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -package com.atlarge.odcsim - -import java.io.Serializable - -/** - * A timestamped wrapper for messages that will be delivered to an actor. - */ -interface Envelope : Comparable>, Serializable { - /** - * The time at which this message should be delivered. - */ - val time: Instant - - /** - * The message contained in this envelope, of type [T] - */ - val message: T - - /** - * Extract the delivery time from the envelope. - */ - operator fun component1(): Instant = time - - /** - * Extract the message from this envelope. - */ - operator fun component2(): T = message - - /** - * Compare this envelope to the [other] envelope, ordered increasingly in time. - */ - override fun compareTo(other: Envelope<*>): Int = time.compareTo(other.time) -} diff --git a/odcsim-core/src/main/kotlin/com/atlarge/odcsim/Signals.kt b/odcsim-core/src/main/kotlin/com/atlarge/odcsim/Signals.kt deleted file mode 100644 index 9b707348..00000000 --- a/odcsim-core/src/main/kotlin/com/atlarge/odcsim/Signals.kt +++ /dev/null @@ -1,60 +0,0 @@ -/* - * MIT License - * - * Copyright (c) 2018 atlarge-research - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -package com.atlarge.odcsim - -/** - * System signals are notifications that are generated by the system and delivered to the actor behavior in a reliable - * fashion. - */ -interface Signal - -/** - * Lifecycle signal that is fired upon creation of the actor. This will be the first message that the actor receives. - */ -object PreStart : Signal - -/** - * Lifecycle signal that is fired after this actor and all its child actors (transitively) have terminated. - * The [Terminated] signal is only sent to registered watchers after this signal has been processed. - */ -object PostStop : Signal - -/** - * A lifecycle signal to indicate that an actor that was watched has terminated. - * - * @property ref The reference to the actor that has terminated. - * @property failure The failure that caused the termination, or `null` on graceful termination. - */ -data class Terminated(val ref: ActorRef<*>, val failure: Throwable? = null) : Signal - -/** - * A [Signal] to indicate an actor has timed out. - * - * This class contains a [target] property in order to allow nested behavior to function properly when multiple layers - * are waiting on this signal. - * - * @property target The target object that has timed out. - */ -data class Timeout(val target: Any) : Signal diff --git a/odcsim-core/src/main/kotlin/com/atlarge/odcsim/StashBuffer.kt b/odcsim-core/src/main/kotlin/com/atlarge/odcsim/StashBuffer.kt deleted file mode 100644 index 5d73d808..00000000 --- a/odcsim-core/src/main/kotlin/com/atlarge/odcsim/StashBuffer.kt +++ /dev/null @@ -1,88 +0,0 @@ -/* - * MIT License - * - * Copyright (c) 2019 atlarge-research - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -package com.atlarge.odcsim - -import com.atlarge.odcsim.internal.StashBufferImpl - -/** - * A non thread safe mutable message buffer that can be used to buffer messages inside actors and then unstash them. - * - * @param T The shape of the messages in this buffer. - */ -interface StashBuffer { - /** - * The first element of the buffer. - * - * @throws NoSuchElementException if the buffer is empty. - */ - val head: T - - /** - * A flag to indicate whether the buffer is empty. - */ - val isEmpty: Boolean - - /** - * A flag to indicate whether the buffer is full. - */ - val isFull: Boolean - - /** - * The number of elements in the stash buffer. - */ - val size: Int - - /** - * Iterate over all elements of the buffer and apply a function to each element, without removing them. - * - * @param block The function to invoke for each element. - */ - fun forEach(block: (T) -> Unit) - - /** - * Add one element to the end of the message buffer. - * - * @param msg The message to stash. - * @throws IllegalStateException if the element cannot be added at this time due to capacity restrictions - */ - fun stash(msg: T) - - /** - * Process all stashed messages with the behavior and the returned [Behavior] from each processed message. - * - * @param ctx The actor context to process these messages in. - * @param behavior The behavior to process the messages with. - */ - fun unstashAll(ctx: ActorContext, behavior: Behavior): Behavior - - companion object { - /** - * Construct a [StashBuffer] with the specified [capacity]. - * - * @param capacity The capacity of the buffer. - */ - operator fun invoke(capacity: Int): StashBuffer = StashBufferImpl(capacity) - } -} diff --git a/odcsim-core/src/main/kotlin/com/atlarge/odcsim/Time.kt b/odcsim-core/src/main/kotlin/com/atlarge/odcsim/Time.kt deleted file mode 100644 index f19f6fe2..00000000 --- a/odcsim-core/src/main/kotlin/com/atlarge/odcsim/Time.kt +++ /dev/null @@ -1,75 +0,0 @@ -/* - * MIT License - * - * Copyright (c) 2018 atlarge-research - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -package com.atlarge.odcsim - -/** - * An instantaneous point on the time-line, used to record message time-stamps in a simulation. - */ -typealias Instant = Double - -/** - * A time interval which represents the amount of elapsed time between two messages. - */ -typealias Duration = Double - -/** - * Convert this [Int] into an [Instant]. - */ -fun Int.toInstant(): Instant = toDouble() - -/** - * Convert this [Int] into a [Duration]. - */ -fun Int.toDuration(): Duration = toDouble() - -/** - * Convert this [Long] into an [Instant]. - */ -fun Long.toInstant(): Instant = toDouble() - -/** - * Convert this [Long] into a [Duration]. - */ -fun Long.toDuration(): Duration = toDouble() - -/** - * Convert this [Float] into an [Instant]. - */ -fun Float.toInstant(): Instant = toDouble() - -/** - * Convert this [Float] into a [Duration]. - */ -fun Float.toDuration(): Duration = toDouble() - -/** - * Convert this [Double] into an [Instant]. - */ -fun Double.toInstant(): Instant = toDouble() - -/** - * Convert this [Double] into a [Duration]. - */ -fun Double.toDuration(): Duration = toDouble() diff --git a/odcsim-core/src/main/kotlin/com/atlarge/odcsim/TimerScheduler.kt b/odcsim-core/src/main/kotlin/com/atlarge/odcsim/TimerScheduler.kt deleted file mode 100644 index c5c54b64..00000000 --- a/odcsim-core/src/main/kotlin/com/atlarge/odcsim/TimerScheduler.kt +++ /dev/null @@ -1,92 +0,0 @@ -/* - * MIT License - * - * Copyright (c) 2019 atlarge-research - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -package com.atlarge.odcsim - -/** - * An interface to provide support for scheduled self messages in an actor. It is used with [withTimers]. - * Timers are bound to the lifecycle of the actor that owns it, and thus are cancelled automatically when it is - * restarted or stopped. - * - * Please be aware that [TimerScheduler] is not thread-safe and must only be used within the actor that owns it. - * - * @param T The shape of the messages the owning actor of this scheduling accepts. - */ -interface TimerScheduler { - /** - * Cancel a timer with the given key. - * - * @param key The key of the timer. - */ - fun cancel(key: Any) - - /** - * Cancel all timers. - */ - fun cancelAll() - - /** - * Check if a timer with a given [key] is active. - * - * @param key The key to check if it is active. - * @return `true` if a timer with the specified key is active, `false` otherwise. - */ - fun isTimerActive(key: Any): Boolean - - /** - * Start a periodic timer that will send [msg] to the `self` actor at a fixed [interval]. - * - * @param key The key of the timer. - * @param msg The message to send to the actor. - * @param interval The interval of simulation time after which it should be sent. - */ - fun startPeriodicTimer(key: Any, msg: T, interval: Duration) - - /** - * Start a timer that will send [msg] once to the `self` actor after the given [delay]. - * - * @param key The key of the timer. - * @param msg The message to send to the actor. - * @param delay The delay in simulation time after which it should be sent. - */ - fun startSingleTimer(key: Any, msg: T, delay: Duration) - - /** - * Run [block] periodically at a fixed [interval] - * - * @param key The key of the timer. - * @param interval The delay of simulation time after which the block should run. - * @param block The block to run. - */ - fun every(key: Any, interval: Duration, block: () -> Unit) - - /** - * Run [block] after the specified [delay]. - * - * @param key The key of the timer. - * @param delay The delay in simulation time after which the block should run. - * @param block The block to run. - */ - fun after(key: Any, delay: Duration, block: () -> Unit) -} diff --git a/odcsim-core/src/main/kotlin/com/atlarge/odcsim/coroutines/Behavior.kt b/odcsim-core/src/main/kotlin/com/atlarge/odcsim/coroutines/Behavior.kt deleted file mode 100644 index eb26add1..00000000 --- a/odcsim-core/src/main/kotlin/com/atlarge/odcsim/coroutines/Behavior.kt +++ /dev/null @@ -1,116 +0,0 @@ -/* - * MIT License - * - * Copyright (c) 2018 atlarge-research - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -package com.atlarge.odcsim.coroutines - -import com.atlarge.odcsim.ActorContext -import com.atlarge.odcsim.Behavior -import com.atlarge.odcsim.DeferredBehavior -import com.atlarge.odcsim.Signal -import com.atlarge.odcsim.internal.SuspendingActorContextImpl -import com.atlarge.odcsim.internal.SuspendingBehaviorImpl -import kotlin.coroutines.Continuation -import kotlin.coroutines.CoroutineContext -import kotlin.coroutines.intrinsics.suspendCoroutineUninterceptedOrReturn -import kotlin.coroutines.suspendCoroutine - -/** - * A [Behavior] that allows method calls to suspend execution via Kotlin coroutines. - * - * @param T The shape of the messages the actor accepts. - */ -abstract class SuspendingBehavior : DeferredBehavior() { - /** - * Run the suspending logic of this behavior. - * - * @param ctx The [SuspendingActorContext] in which the behavior is executed. - * @return The next behavior for the actor. - */ - abstract suspend operator fun invoke(ctx: SuspendingActorContext): Behavior - - // Immediately transfer to implementation - override fun invoke(ctx: ActorContext): Behavior = SuspendingBehaviorImpl(ctx, this).start() -} - -/** - * An [ActorContext] that provides additional functionality for receiving messages and signals from - * the actor's mailbox. - * - * @param T The shape of the messages the actor accepts. - */ -interface SuspendingActorContext : ActorContext, CoroutineContext.Element { - /** - * Suspend execution of the active coroutine to wait for a message of type [T] to be received in the actor's - * mailbox. During suspension, incoming signals will be marked unhandled. - * - * @return The message of type [T] that has been received. - */ - suspend fun receive(): T - - /** - * Suspend execution of the active coroutine to wait for a [Signal] to be received in the actor's mailbox. - * During suspension, incoming messages will be marked unhandled. - * - * @return The [Signal] that has been received. - */ - suspend fun receiveSignal(): Signal - - /** - * A key to provide access to the untyped [SuspendingActorContext] via [CoroutineContext] for suspending methods - * running inside a [SuspendingBehavior]. - */ - companion object Key : CoroutineContext.Key> -} - -/** - * Obtains the current continuation instance inside suspend functions and suspends currently running coroutine. [block] - * should return a [Behavior] that will resume the continuation and return the next behavior which is supplied via the - * second argument of the block. - */ -suspend fun suspendWithBehavior(block: (Continuation, () -> Behavior) -> Behavior): U = - suspendCoroutine { cont -> - @Suppress("UNCHECKED_CAST") - val ctx = cont.context[SuspendingActorContext] as? SuspendingActorContextImpl - ?: throw UnsupportedOperationException("Coroutine does not run inside SuspendingBehavior") - ctx.become(block(cont) { ctx.behavior }) - } - -/** - * Obtain the current [SuspendingActorContext] instance for the active continuation. - */ -suspend fun actorContext(): SuspendingActorContext = - suspendCoroutineUninterceptedOrReturn { cont -> - @Suppress("UNCHECKED_CAST") - cont.context[SuspendingActorContext] as? SuspendingActorContext - ?: throw UnsupportedOperationException("Coroutine does not run inside SuspendingBehavior") - } - -/** - * Construct a [Behavior] that uses Kotlin coroutines functionality to handle incoming messages and signals. - */ -fun suspending(block: suspend (SuspendingActorContext) -> Behavior): Behavior { - return object : SuspendingBehavior() { - override suspend fun invoke(ctx: SuspendingActorContext) = block(ctx) - } -} diff --git a/odcsim-core/src/main/kotlin/com/atlarge/odcsim/coroutines/dsl/Receive.kt b/odcsim-core/src/main/kotlin/com/atlarge/odcsim/coroutines/dsl/Receive.kt deleted file mode 100644 index e995c0e3..00000000 --- a/odcsim-core/src/main/kotlin/com/atlarge/odcsim/coroutines/dsl/Receive.kt +++ /dev/null @@ -1,66 +0,0 @@ -/* - * MIT License - * - * Copyright (c) 2019 atlarge-research - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -package com.atlarge.odcsim.coroutines.dsl - -import com.atlarge.odcsim.ActorRef -import com.atlarge.odcsim.Duration -import com.atlarge.odcsim.coroutines.SuspendingActorContext -import com.atlarge.odcsim.coroutines.suspendWithBehavior -import com.atlarge.odcsim.receiveMessage -import com.atlarge.odcsim.unhandled -import kotlin.coroutines.resume - -/** - * Receive only messages of type [U] and mark all other messages as unhandled. - * - * @return The received message. - */ -suspend inline fun SuspendingActorContext.receiveOf(): U = - suspendWithBehavior { cont, next -> - receiveMessage { msg -> - if (msg is U) { - cont.resume(msg) - next() - } else { - unhandled() - } - } - } - -/** - * Send the specified message to the given reference and wait for a reply. - * - * @param ref The actor to send the message to. - * @param after The delay after which the message should be received by the actor. - * @param transform The block to transform `self` to a message. - */ -suspend inline fun SuspendingActorContext.ask( - ref: ActorRef, - after: Duration = 0.0, - transform: (ActorRef) -> U -): V { - send(ref, transform(self), after) - return receiveOf() -} diff --git a/odcsim-core/src/main/kotlin/com/atlarge/odcsim/coroutines/dsl/Timeout.kt b/odcsim-core/src/main/kotlin/com/atlarge/odcsim/coroutines/dsl/Timeout.kt deleted file mode 100644 index 16b6f534..00000000 --- a/odcsim-core/src/main/kotlin/com/atlarge/odcsim/coroutines/dsl/Timeout.kt +++ /dev/null @@ -1,42 +0,0 @@ -/* - * MIT License - * - * Copyright (c) 2018 atlarge-research - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -package com.atlarge.odcsim.coroutines.dsl - -import com.atlarge.odcsim.Duration -import com.atlarge.odcsim.coroutines.suspendWithBehavior -import com.atlarge.odcsim.withTimeout -import kotlin.coroutines.resume - -/** - * Block execution for the specified duration. - * - * @param after The duration after which execution should continue. - */ -suspend fun timeout(after: Duration) = suspendWithBehavior { cont, next -> - withTimeout(after) { - cont.resume(Unit) - next() - } -} diff --git a/odcsim-core/src/main/kotlin/com/atlarge/odcsim/internal/ActorContext.kt b/odcsim-core/src/main/kotlin/com/atlarge/odcsim/internal/ActorContext.kt deleted file mode 100644 index f1aba25e..00000000 --- a/odcsim-core/src/main/kotlin/com/atlarge/odcsim/internal/ActorContext.kt +++ /dev/null @@ -1,43 +0,0 @@ -/* - * MIT License - * - * Copyright (c) 2019 atlarge-research - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -package com.atlarge.odcsim.internal - -import com.atlarge.odcsim.ActorContext -import com.atlarge.odcsim.ActorRef -import com.atlarge.odcsim.Duration -import com.atlarge.odcsim.Signal - -/** - * Send the specified [Signal] to the given actor reference after the specified duration. - * - * @param ref The actor to send the signal to. - * @param signal The signal to send to the referenced actor. - * @param after The delay after which the signal should be received by the actor. - */ -fun ActorContext<*>.sendSignal(ref: ActorRef<*>, signal: Signal, after: Duration = 0.0) { - // Signals are currently processed as regular messages - @Suppress("UNCHECKED_CAST") - send(ref as ActorRef, signal, after) -} diff --git a/odcsim-core/src/main/kotlin/com/atlarge/odcsim/internal/Behavior.kt b/odcsim-core/src/main/kotlin/com/atlarge/odcsim/internal/Behavior.kt deleted file mode 100644 index b07cabc0..00000000 --- a/odcsim-core/src/main/kotlin/com/atlarge/odcsim/internal/Behavior.kt +++ /dev/null @@ -1,48 +0,0 @@ -/* - * MIT License - * - * Copyright (c) 2018 atlarge-research - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -package com.atlarge.odcsim.internal - -import com.atlarge.odcsim.ActorContext -import com.atlarge.odcsim.Behavior -import com.atlarge.odcsim.ReceivingBehavior -import com.atlarge.odcsim.Signal - -/** - * A [Behavior] object that ignores all messages sent to the actor. - */ -internal object IgnoreBehavior : ReceivingBehavior() { - override fun receive(ctx: ActorContext, msg: Any): Behavior = this - - override fun receiveSignal(ctx: ActorContext, signal: Signal): Behavior = this - - override fun toString() = "Ignore" -} - -/** - * A [Behavior] object that does not handle any message it receives. - */ -internal object EmptyBehavior : ReceivingBehavior() { - override fun toString() = "Empty" -} diff --git a/odcsim-core/src/main/kotlin/com/atlarge/odcsim/internal/BehaviorInterpreter.kt b/odcsim-core/src/main/kotlin/com/atlarge/odcsim/internal/BehaviorInterpreter.kt deleted file mode 100644 index 194c2a62..00000000 --- a/odcsim-core/src/main/kotlin/com/atlarge/odcsim/internal/BehaviorInterpreter.kt +++ /dev/null @@ -1,201 +0,0 @@ -/* - * MIT License - * - * Copyright (c) 2018 atlarge-research - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -package com.atlarge.odcsim.internal - -import com.atlarge.odcsim.ActorContext -import com.atlarge.odcsim.Behavior -import com.atlarge.odcsim.DeferredBehavior -import com.atlarge.odcsim.ReceivingBehavior -import com.atlarge.odcsim.SameBehavior -import com.atlarge.odcsim.Signal -import com.atlarge.odcsim.StoppedBehavior -import com.atlarge.odcsim.UnhandledBehavior -import com.atlarge.odcsim.isAlive -import com.atlarge.odcsim.isUnhandled - -/** - * Helper class that interprets messages/signals, canonicalizes special objects and manages the life-cycle of - * [Behavior] instances. - * - * @param initialBehavior The initial behavior to use. - */ -class BehaviorInterpreter(initialBehavior: Behavior) { - /** - * The current [Behavior] instance. - */ - var behavior: Behavior = initialBehavior - private set - - /** - * A flag to indicate the interpreter is still alive. - */ - val isAlive: Boolean get() = behavior.isAlive - - /** - * Construct a [BehaviorInterpreter] with the specified initial behavior and immediately start it in the specified - * context. - * - * @param initialBehavior The initial behavior of the actor. - * @param ctx The [ActorContext] to run the behavior in. - */ - constructor(initialBehavior: Behavior, ctx: ActorContext) : this(initialBehavior) { - start(ctx) - } - - /** - * Start the initial behavior. - * - * @param ctx The [ActorContext] to start the behavior in. - */ - fun start(ctx: ActorContext) { - behavior = validateAsInitial(start(ctx, behavior)) - } - - /** - * Stop the current active behavior and move into the stopped state. - * - * @param ctx The [ActorContext] this takes place in. - */ - fun stop(ctx: ActorContext) { - behavior = start(ctx, StoppedBehavior.narrow()) - } - - /** - * Replace the current behavior with the specified new behavior. - * - * @param ctx The [ActorContext] to run the behavior in. - * @param next The behavior to replace the current behavior with. - */ - fun become(ctx: ActorContext, next: Behavior) { - this.behavior = canonicalize(ctx, behavior, next) - } - - /** - * Propagate special states of the wrapper [Behavior] to the specified [Behavior]. This means - * that if the behavior of this interpreter is stopped or unhandled, this will be propagated. - * - * @param behavior The [Behavior] to map. - * @return Either the specified [Behavior] or the propagated special objects. - */ - fun propagate(behavior: Behavior): Behavior = - if (this.behavior.isUnhandled || !this.behavior.isAlive) - this.behavior - else - behavior - - /** - * Interpret the given message of type [T] using the current active behavior. - * - * @return `true` if the message was handled by the active behavior, `false` otherwise. - */ - fun interpretMessage(ctx: ActorContext, msg: T): Boolean = interpret(ctx, msg, false) - - /** - * Interpret the given [Signal] using the current active behavior. - * - * @return `true` if the signal was handled by the active behavior, `false` otherwise. - */ - fun interpretSignal(ctx: ActorContext, signal: Signal): Boolean = interpret(ctx, signal, true) - - /** - * Interpret the given message or signal using the current active behavior. - * - * @return `true` if the message or signal was handled by the active behavior, `false` otherwise. - */ - private fun interpret(ctx: ActorContext, msg: Any, isSignal: Boolean): Boolean = - if (isAlive) { - val next = when (val current = behavior) { - is DeferredBehavior -> - throw IllegalStateException("Deferred [$current] should not be passed to interpreter") - is ReceivingBehavior -> - if (isSignal) - current.receiveSignal(ctx, msg as Signal) - else - @Suppress("UNCHECKED_CAST") - current.receive(ctx, msg as T) - is SameBehavior, is UnhandledBehavior -> - throw IllegalStateException("Cannot execute with [$current] as behavior") - is StoppedBehavior -> current - } - - val unhandled = next.isUnhandled - behavior = canonicalize(ctx, behavior, next) - !unhandled - } else { - false - } - - /** - * Validate whether the given [Behavior] can be used as initial behavior. Throw an [IllegalArgumentException] if - * the [Behavior] is not valid. - * - * @param behavior The behavior to validate. - */ - private fun validateAsInitial(behavior: Behavior): Behavior = - when (behavior) { - is SameBehavior, is UnhandledBehavior -> - throw IllegalArgumentException("Cannot use [$behavior] as initial behavior") - else -> behavior - } - - /** - * Helper methods to properly manage the special, canned behavior objects. It highly recommended to use the - * [BehaviorInterpreter] instead to properly manage the life-cycles of the behavior objects. - */ - companion object { - /** - * Start the initial behavior of an actor in the specified [ActorContext]. - * - * This will activate the initial behavior and canonicalize the resulting behavior. - * - * @param ctx The [ActorContext] to start the behavior in. - * @param behavior The initial behavior to start. - * @return The behavior that has been started. - */ - tailrec fun start(ctx: ActorContext, behavior: Behavior): Behavior = - when (behavior) { - is DeferredBehavior -> start(ctx, behavior(ctx)) - else -> behavior - } - - /** - * Given a possibly special behavior (same or unhandled) and a "current" behavior (which defines the meaning of - * encountering a `same` behavior) this method computes the next behavior, suitable for passing a message or - * signal. - * - * @param ctx The context in which the actor runs. - * @param current The actor's current behavior. - * @param next The actor's next behavior. - * @return The actor's canonicalized next behavior. - */ - tailrec fun canonicalize(ctx: ActorContext, current: Behavior, next: Behavior): Behavior = - when (next) { - is SameBehavior, current -> current - is UnhandledBehavior -> current - is DeferredBehavior -> canonicalize(ctx, current, next(ctx)) - else -> next - } - } -} diff --git a/odcsim-core/src/main/kotlin/com/atlarge/odcsim/internal/Coroutines.kt b/odcsim-core/src/main/kotlin/com/atlarge/odcsim/internal/Coroutines.kt deleted file mode 100644 index 82b29715..00000000 --- a/odcsim-core/src/main/kotlin/com/atlarge/odcsim/internal/Coroutines.kt +++ /dev/null @@ -1,182 +0,0 @@ -/* - * MIT License - * - * Copyright (c) 2018 atlarge-research - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -package com.atlarge.odcsim.internal - -import com.atlarge.odcsim.ActorContext -import com.atlarge.odcsim.ActorRef -import com.atlarge.odcsim.ActorSystem -import com.atlarge.odcsim.Behavior -import com.atlarge.odcsim.Duration -import com.atlarge.odcsim.Instant -import com.atlarge.odcsim.ReceivingBehavior -import com.atlarge.odcsim.Signal -import com.atlarge.odcsim.coroutines.SuspendingActorContext -import com.atlarge.odcsim.coroutines.SuspendingBehavior -import com.atlarge.odcsim.coroutines.suspendWithBehavior -import com.atlarge.odcsim.empty -import com.atlarge.odcsim.receiveMessage -import com.atlarge.odcsim.receiveSignal -import org.slf4j.Logger -import kotlin.coroutines.Continuation -import kotlin.coroutines.CoroutineContext -import kotlin.coroutines.resume -import kotlin.coroutines.startCoroutine - -/** - * This interface exposes internal functionality provided by [SuspendingBehaviorImpl] on [SuspendingActorContext] to - * control the active behavior of the coroutine. - */ -interface SuspendingActorContextImpl : SuspendingActorContext { - /** - * The current active behavior - */ - val behavior: Behavior - - /** - * Replace the current active behavior with the specified new behavior. - * - * @param next The behavior to replace the current behavior with. - */ - fun become(next: Behavior) -} - -/** - * Implementation of [SuspendingBehavior] class that maps the suspending method calls to the [Behavior] - * interface. - * This implementation uses the fact that each actor is thread-safe (as it processes its mailbox sequentially). - */ -internal class SuspendingBehaviorImpl( - private var actorContext: ActorContext, - initialBehavior: SuspendingBehavior -) : ReceivingBehavior(), SuspendingActorContextImpl { - - /** - * The next behavior to use. - */ - private var next: Behavior = this - - /** - * The [BehaviorInterpreter] to wrap the suspending behavior. - */ - private val interpreter = BehaviorInterpreter(initialBehavior) - - override fun receive(ctx: ActorContext, msg: T): Behavior { - this.actorContext = ctx - return interpreter.also { it.interpretMessage(ctx, msg) }.propagate(next) - } - - override fun receiveSignal(ctx: ActorContext, signal: Signal): Behavior { - this.actorContext = ctx - return interpreter.also { it.interpretSignal(ctx, signal) }.propagate(next) - } - - override val self: ActorRef get() = actorContext.self - - override val time: Instant get() = actorContext.time - - override val children: List> - get() = actorContext.children - - override val system: ActorSystem<*> - get() = actorContext.system - - override val log: Logger - get() = actorContext.log - - override fun getChild(name: String): ActorRef<*>? = actorContext.getChild(name) - - override fun send(ref: ActorRef, msg: U, after: Duration) = actorContext.send(ref, msg, after) - - override fun spawn(behavior: Behavior, name: String) = actorContext.spawn(behavior, name) - - override fun spawnAnonymous(behavior: Behavior) = actorContext.spawnAnonymous(behavior) - - override fun stop(child: ActorRef<*>) = actorContext.stop(child) - - override fun watch(target: ActorRef<*>) = actorContext.watch(target) - - override fun unwatch(target: ActorRef<*>) = actorContext.unwatch(target) - - override fun sync(target: ActorRef<*>) = actorContext.sync(target) - - override fun unsync(target: ActorRef<*>) = actorContext.unsync(target) - - override fun isSync(target: ActorRef<*>): Boolean = actorContext.isSync(target) - - override suspend fun receive(): T = suspendWithBehavior { cont, next -> - receiveMessage { msg -> - cont.resume(msg) - next() - } - } - - override suspend fun receiveSignal(): Signal = suspendWithBehavior { cont, next -> - receiveSignal { _, signal -> - cont.resume(signal) - next() - } - } - - override val behavior: Behavior get() = interpreter.behavior - - override fun become(next: Behavior) { - interpreter.become(actorContext, next) - } - - override val key: CoroutineContext.Key<*> = SuspendingActorContext.Key - - /** - * Start the suspending behavior. - */ - internal fun start(): Behavior { - val behavior = interpreter.behavior as SuspendingBehavior - val block = suspend { behavior(this) } - interpreter.become(actorContext, empty()) - block.startCoroutine(SuspendingBehaviorImplContinuation()) - return next - } - - /** - * Stop the suspending behavior. - */ - private fun stop() { - this.interpreter.stop(actorContext) - } - - /** - * The continuation of suspending behavior. - */ - private inner class SuspendingBehaviorImplContinuation : Continuation> { - override val context = this@SuspendingBehaviorImpl - - override fun resumeWith(result: Result>) { - if (result.isSuccess) { - next = result.getOrNull()!! - } else if (result.isFailure) { - throw result.exceptionOrNull()!! - } - } - } -} diff --git a/odcsim-core/src/main/kotlin/com/atlarge/odcsim/internal/StashBufferImpl.kt b/odcsim-core/src/main/kotlin/com/atlarge/odcsim/internal/StashBufferImpl.kt deleted file mode 100644 index 24c3a9d5..00000000 --- a/odcsim-core/src/main/kotlin/com/atlarge/odcsim/internal/StashBufferImpl.kt +++ /dev/null @@ -1,74 +0,0 @@ -/* - * MIT License - * - * Copyright (c) 2019 atlarge-research - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -package com.atlarge.odcsim.internal - -import com.atlarge.odcsim.ActorContext -import com.atlarge.odcsim.Behavior -import com.atlarge.odcsim.StashBuffer -import java.util.ArrayDeque - -/** - * Internal implementation of the [StashBuffer] interface. - */ -internal class StashBufferImpl(private val capacity: Int) : StashBuffer { - /** - * The internal queue used to store the messages. - */ - private val queue = ArrayDeque(capacity) - - override val head: T - get() = queue.first - - override val isEmpty: Boolean - get() = queue.isEmpty() - - override val isFull: Boolean - get() = size > capacity - - override val size: Int - get() = queue.size - - override fun forEach(block: (T) -> Unit) { - queue.toList().forEach(block) - } - - override fun stash(msg: T) { - queue.add(msg) - } - - override fun unstashAll(ctx: ActorContext, behavior: Behavior): Behavior { - val messages = queue.toList() - queue.clear() - - val interpreter = BehaviorInterpreter(behavior) - interpreter.start(ctx) - - for (message in messages) { - interpreter.interpretMessage(ctx, message) - } - - return interpreter.behavior - } -} diff --git a/odcsim-core/src/main/kotlin/com/atlarge/odcsim/internal/TimerSchedulerImpl.kt b/odcsim-core/src/main/kotlin/com/atlarge/odcsim/internal/TimerSchedulerImpl.kt deleted file mode 100644 index 22bec507..00000000 --- a/odcsim-core/src/main/kotlin/com/atlarge/odcsim/internal/TimerSchedulerImpl.kt +++ /dev/null @@ -1,122 +0,0 @@ -/* - * MIT License - * - * Copyright (c) 2019 atlarge-research - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -package com.atlarge.odcsim.internal - -import com.atlarge.odcsim.ActorContext -import com.atlarge.odcsim.Duration -import com.atlarge.odcsim.Signal -import com.atlarge.odcsim.TimerScheduler - -/** - * Implementation of [TimerScheduler] that uses the actor's [ActorContext] to provide timer functionality. - * - * @property ctx The actor context to use. - */ -internal class TimerSchedulerImpl(private val ctx: ActorContext) : TimerScheduler { - private val timers = mutableMapOf>() - - override fun cancel(key: Any) { - val timer = timers[key] ?: return - ctx.log.debug("Cancel timer [{}] with generation [{}]", timer.key, timer.generation) - timers -= timer.key - } - - override fun cancelAll() { - ctx.log.debug("Cancel all timers") - timers.clear() - } - - override fun isTimerActive(key: Any): Boolean = timers.containsKey(key) - - override fun startPeriodicTimer(key: Any, msg: T, interval: Duration) { - startTimer(key, msg, interval, true) - } - - override fun startSingleTimer(key: Any, msg: T, delay: Duration) { - startTimer(key, msg, delay, false) - } - - override fun every(key: Any, interval: Duration, block: () -> Unit) { - @Suppress("UNCHECKED_CAST") - startTimer(key, Block(block) as T, interval, true) - } - - override fun after(key: Any, delay: Duration, block: () -> Unit) { - @Suppress("UNCHECKED_CAST") - startTimer(key, Block(block) as T, delay, false) - } - - private fun startTimer(key: Any, msg: T, duration: Duration, repeat: Boolean) { - val timer = timers.getOrPut(key) { Timer(key) } - timer.duration = duration - timer.generation += 1 - timer.msg = msg - timer.repeat = repeat - ctx.sendSignal(ctx.self, TimerSignal(key, timer.generation), duration) - ctx.log.debug("Start timer [{}] with generation [{}]", key, timer.generation) - } - - fun interceptTimerSignal(signal: TimerSignal): T? { - val timer = timers[signal.key] - - if (timer == null) { - // Message was from canceled timer that was already enqueued - ctx.log.debug("Received timer [{}] that has been removed, discarding", signal.key) - return null - } else if (signal.generation != timer.generation) { - // Message was from an old timer that was enqueued before canceled - ctx.log.debug("Received timer [{}] from old generation [{}], expected generation [{}], discarding", - signal.key, signal.generation, timer.generation) - } - - if (!timer.repeat) { - timers -= timer.key - } else { - ctx.sendSignal(ctx.self, signal, timer.duration) - } - - val msg = timer.msg - - if (msg is Block) { - msg() - return null - } - - return msg - } - - data class Timer(val key: Any) { - var duration: Duration = 0.0 - var repeat: Boolean = false - var generation: Int = 0 - lateinit var msg: T - } - - data class TimerSignal(val key: Any, val generation: Int) : Signal - - data class Block(val block: () -> Unit) { - operator fun invoke() = block() - } -} diff --git a/odcsim-core/src/main/kotlin/com/atlarge/odcsim/internal/logging/LocationAwareLoggerImpl.kt b/odcsim-core/src/main/kotlin/com/atlarge/odcsim/internal/logging/LocationAwareLoggerImpl.kt deleted file mode 100644 index bf50b5e8..00000000 --- a/odcsim-core/src/main/kotlin/com/atlarge/odcsim/internal/logging/LocationAwareLoggerImpl.kt +++ /dev/null @@ -1,567 +0,0 @@ -/* - * MIT License - * - * Copyright (c) 2019 atlarge-research - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -package com.atlarge.odcsim.internal.logging - -import com.atlarge.odcsim.ActorContext -import org.slf4j.Logger -import org.slf4j.Marker -import org.slf4j.helpers.MessageFormatter -import org.slf4j.spi.LocationAwareLogger - -/** - * An actor-specific [Logger] implementation that is aware of the calling location. - * - * @param ctx The owning [ActorContext] of this logger. - * @param delegate The [LocationAwareLogger] to delegate the messages to. - */ -internal class LocationAwareLoggerImpl( - ctx: ActorContext<*>, - private val delegate: LocationAwareLogger -) : LoggerImpl(ctx), Logger by delegate { - /** - * The fully qualified name of this class. - */ - private val fqcn = LocationAwareLoggerImpl::class.java.name - - override fun trace(format: String?, arg: Any?) { - if (!delegate.isTraceEnabled) { - return - } - - withMdc { - val formattedMessage = MessageFormatter.format(format, arg).message - delegate.log(null, fqcn, LocationAwareLogger.TRACE_INT, formattedMessage, arrayOf(arg), null) - } - } - - override fun trace(format: String?, arg1: Any?, arg2: Any?) { - if (!delegate.isTraceEnabled) { - return - } - - withMdc { - val formattedMessage = MessageFormatter.format(format, arg1, arg2).message - delegate.log(null, fqcn, LocationAwareLogger.TRACE_INT, formattedMessage, arrayOf(arg1, arg2), null) - } - } - - override fun trace(format: String?, argArray: Array) { - if (!delegate.isTraceEnabled) { - return - } - - withMdc { - val formattedMessage = MessageFormatter.arrayFormat(format, argArray).message - delegate.log(null, fqcn, LocationAwareLogger.TRACE_INT, formattedMessage, argArray, null) - } - } - - override fun trace(msg: String?, t: Throwable?) { - if (!delegate.isTraceEnabled) { - return - } - - withMdc { - delegate.log(null, fqcn, LocationAwareLogger.TRACE_INT, msg, null, t) - } - } - - override fun trace(marker: Marker?, msg: String?) { - if (!delegate.isTraceEnabled) { - return - } - - withMdc { - delegate.log(marker, fqcn, LocationAwareLogger.TRACE_INT, msg, null, null) - } - } - - override fun trace(marker: Marker?, format: String?, arg: Any?) { - if (!delegate.isTraceEnabled) { - return - } - - withMdc { - val formattedMessage = MessageFormatter.format(format, arg).message - delegate.log(marker, fqcn, LocationAwareLogger.TRACE_INT, formattedMessage, arrayOf(arg), null) - } - } - - override fun trace(marker: Marker?, format: String?, arg1: Any?, arg2: Any?) { - if (!delegate.isTraceEnabled) { - return - } - - withMdc { - val formattedMessage = MessageFormatter.format(format, arg1, arg2).message - delegate.log(marker, fqcn, LocationAwareLogger.TRACE_INT, formattedMessage, arrayOf(arg1, arg2), null) - } - } - - override fun trace(marker: Marker?, format: String?, argArray: Array) { - if (!delegate.isTraceEnabled) { - return - } - - withMdc { - val formattedMessage = MessageFormatter.arrayFormat(format, argArray).message - delegate.log(marker, fqcn, LocationAwareLogger.TRACE_INT, formattedMessage, argArray, null) - } - } - - override fun trace(marker: Marker?, msg: String?, t: Throwable?) { - if (!delegate.isTraceEnabled) { - return - } - - withMdc { - delegate.log(marker, fqcn, LocationAwareLogger.TRACE_INT, msg, null, t) - } - } - - override fun debug(msg: String?) { - if (!delegate.isDebugEnabled) { - return - } - - withMdc { - delegate.log(null, fqcn, LocationAwareLogger.DEBUG_INT, msg, null, null) - } - } - - override fun debug(format: String?, arg: Any?) { - if (!delegate.isDebugEnabled) { - return - } - - withMdc { - val formattedMessage = MessageFormatter.format(format, arg).message - delegate.log(null, fqcn, LocationAwareLogger.DEBUG_INT, formattedMessage, arrayOf(arg), null) - } - } - - override fun debug(format: String?, arg1: Any?, arg2: Any?) { - if (!delegate.isDebugEnabled) { - return - } - - withMdc { - val formattedMessage = MessageFormatter.format(format, arg1, arg2).message - delegate.log(null, fqcn, LocationAwareLogger.DEBUG_INT, formattedMessage, arrayOf(arg1, arg2), null) - } - } - - override fun debug(format: String?, argArray: Array) { - if (!delegate.isDebugEnabled) { - return - } - - withMdc { - val ft = MessageFormatter.arrayFormat(format, argArray) - delegate.log(null, fqcn, LocationAwareLogger.DEBUG_INT, ft.message, ft.argArray, ft.throwable) - } - } - - override fun debug(msg: String?, t: Throwable?) { - if (!delegate.isDebugEnabled) { - return - } - - withMdc { - delegate.log(null, fqcn, LocationAwareLogger.DEBUG_INT, msg, null, t) - } - } - - override fun debug(marker: Marker?, msg: String?) { - if (!delegate.isDebugEnabled) { - return - } - - withMdc { - delegate.log(marker, fqcn, LocationAwareLogger.DEBUG_INT, msg, null, null) - } - } - - override fun debug(marker: Marker?, format: String?, arg: Any?) { - if (!delegate.isDebugEnabled) { - return - } - - withMdc { - val ft = MessageFormatter.format(format, arg) - delegate.log(marker, fqcn, LocationAwareLogger.DEBUG_INT, ft.message, ft.argArray, ft.throwable) - } - } - - override fun debug(marker: Marker?, format: String?, arg1: Any?, arg2: Any?) { - if (!delegate.isDebugEnabled) { - return - } - - withMdc { - val formattedMessage = MessageFormatter.format(format, arg1, arg2).message - delegate.log(marker, fqcn, LocationAwareLogger.DEBUG_INT, formattedMessage, arrayOf(arg1, arg2), null) - } - } - - override fun debug(marker: Marker?, format: String?, argArray: Array) { - if (!delegate.isDebugEnabled) { - return - } - - withMdc { - val ft = MessageFormatter.arrayFormat(format, argArray) - delegate.log(marker, fqcn, LocationAwareLogger.DEBUG_INT, ft.message, argArray, ft.throwable) - } - } - - override fun debug(marker: Marker?, msg: String?, t: Throwable?) { - if (!delegate.isDebugEnabled) { - return - } - - withMdc { - delegate.log(marker, fqcn, LocationAwareLogger.DEBUG_INT, msg, null, t) - } - } - - override fun info(msg: String?) { - if (!delegate.isInfoEnabled) { - return - } - - withMdc { - delegate.log(null, fqcn, LocationAwareLogger.INFO_INT, msg, null, null) - } - } - - override fun info(format: String?, arg: Any?) { - if (!delegate.isInfoEnabled) { - return - } - - withMdc { - val formattedMessage = MessageFormatter.format(format, arg).message - delegate.log(null, fqcn, LocationAwareLogger.INFO_INT, formattedMessage, arrayOf(arg), null) - } - } - - override fun info(format: String?, arg1: Any?, arg2: Any?) { - if (!delegate.isInfoEnabled) { - return - } - - withMdc { - val formattedMessage = MessageFormatter.format(format, arg1, arg2).message - delegate.log(null, fqcn, LocationAwareLogger.INFO_INT, formattedMessage, arrayOf(arg1, arg2), null) - } - } - - override fun info(format: String?, argArray: Array) { - if (!delegate.isInfoEnabled) { - return - } - - withMdc { - val formattedMessage = MessageFormatter.arrayFormat(format, argArray).message - delegate.log(null, fqcn, LocationAwareLogger.INFO_INT, formattedMessage, argArray, null) - } - } - - override fun info(msg: String?, t: Throwable?) { - if (!delegate.isInfoEnabled) { - return - } - - withMdc { - delegate.log(null, fqcn, LocationAwareLogger.INFO_INT, msg, null, t) - } - } - - override fun info(marker: Marker?, msg: String?) { - if (!delegate.isInfoEnabled) { - return - } - - withMdc { - delegate.log(marker, fqcn, LocationAwareLogger.INFO_INT, msg, null, null) - } - } - - override fun info(marker: Marker?, format: String?, arg: Any?) { - if (!delegate.isInfoEnabled) { - return - } - - withMdc { - val formattedMessage = MessageFormatter.format(format, arg).message - delegate.log(marker, fqcn, LocationAwareLogger.INFO_INT, formattedMessage, arrayOf(arg), null) - } - } - - override fun info(marker: Marker?, format: String?, arg1: Any?, arg2: Any?) { - if (!delegate.isInfoEnabled) { - return - } - - withMdc { - val formattedMessage = MessageFormatter.format(format, arg1, arg2).message - delegate.log(marker, fqcn, LocationAwareLogger.INFO_INT, formattedMessage, arrayOf(arg1, arg2), null) - } - } - - override fun info(marker: Marker?, format: String?, argArray: Array) { - if (!delegate.isInfoEnabled) { - return - } - - withMdc { - val formattedMessage = MessageFormatter.arrayFormat(format, argArray).message - delegate.log(marker, fqcn, LocationAwareLogger.INFO_INT, formattedMessage, argArray, null) - } - } - - override fun info(marker: Marker?, msg: String?, t: Throwable?) { - if (!delegate.isInfoEnabled) { - return - } - - withMdc { - delegate.log(marker, fqcn, LocationAwareLogger.INFO_INT, msg, null, t) - } - } - - override fun warn(msg: String?) { - if (!delegate.isWarnEnabled) { - return - } - - withMdc { - delegate.log(null, fqcn, LocationAwareLogger.WARN_INT, msg, null, null) - } - } - - override fun warn(format: String?, arg: Any?) { - if (!delegate.isWarnEnabled) { - return - } - - withMdc { - val formattedMessage = MessageFormatter.format(format, arg).message - delegate.log(null, fqcn, LocationAwareLogger.WARN_INT, formattedMessage, arrayOf(arg), null) - } - } - - override fun warn(format: String?, arg1: Any?, arg2: Any?) { - if (!delegate.isWarnEnabled) { - return - } - - withMdc { - val formattedMessage = MessageFormatter.format(format, arg1, arg2).message - delegate.log(null, fqcn, LocationAwareLogger.WARN_INT, formattedMessage, arrayOf(arg1, arg2), null) - } - } - - override fun warn(format: String?, argArray: Array) { - if (!delegate.isWarnEnabled) { - return - } - - withMdc { - val formattedMessage = MessageFormatter.arrayFormat(format, argArray).message - delegate.log(null, fqcn, LocationAwareLogger.WARN_INT, formattedMessage, argArray, null) - } - } - - override fun warn(msg: String?, t: Throwable?) { - if (!delegate.isWarnEnabled) { - return - } - - withMdc { - delegate.log(null, fqcn, LocationAwareLogger.WARN_INT, msg, null, t) - } - } - - override fun warn(marker: Marker?, msg: String?) { - if (!delegate.isWarnEnabled) { - return - } - - withMdc { - delegate.log(marker, fqcn, LocationAwareLogger.WARN_INT, msg, null, null) - } - } - - override fun warn(marker: Marker?, format: String?, arg: Any?) { - if (!delegate.isWarnEnabled) { - return - } - - withMdc { - val formattedMessage = MessageFormatter.format(format, arg).message - delegate.log(marker, fqcn, LocationAwareLogger.WARN_INT, formattedMessage, arrayOf(arg), null) - } - } - - override fun warn(marker: Marker?, format: String?, arg1: Any?, arg2: Any?) { - if (!delegate.isWarnEnabled) { - return - } - - withMdc { - val formattedMessage = MessageFormatter.format(format, arg1, arg2).message - delegate.log(marker, fqcn, LocationAwareLogger.WARN_INT, formattedMessage, arrayOf(arg1, arg2), null) - } - } - - override fun warn(marker: Marker?, format: String?, argArray: Array) { - if (!delegate.isWarnEnabled) { - return - } - - withMdc { - val formattedMessage = MessageFormatter.arrayFormat(format, argArray).message - delegate.log(marker, fqcn, LocationAwareLogger.WARN_INT, formattedMessage, argArray, null) - } - } - - override fun warn(marker: Marker?, msg: String?, t: Throwable?) { - if (!delegate.isWarnEnabled) { - return - } - - withMdc { - delegate.log(marker, fqcn, LocationAwareLogger.WARN_INT, msg, null, t) - } - } - - override fun error(msg: String?) { - if (!delegate.isErrorEnabled) { - return - } - - withMdc { - delegate.log(null, fqcn, LocationAwareLogger.ERROR_INT, msg, null, null) - } - } - - override fun error(format: String?, arg: Any?) { - if (!delegate.isErrorEnabled) { - return - } - - withMdc { - val formattedMessage = MessageFormatter.format(format, arg).message - delegate.log(null, fqcn, LocationAwareLogger.ERROR_INT, formattedMessage, arrayOf(arg), null) - } - } - - override fun error(format: String?, arg1: Any?, arg2: Any?) { - if (!delegate.isErrorEnabled) { - return - } - - withMdc { - val formattedMessage = MessageFormatter.format(format, arg1, arg2).message - delegate.log(null, fqcn, LocationAwareLogger.ERROR_INT, formattedMessage, arrayOf(arg1, arg2), null) - } - } - - override fun error(format: String?, argArray: Array) { - if (!delegate.isErrorEnabled) { - return - } - - withMdc { - val formattedMessage = MessageFormatter.arrayFormat(format, argArray).message - delegate.log(null, fqcn, LocationAwareLogger.ERROR_INT, formattedMessage, argArray, null) - } - } - - override fun error(msg: String?, t: Throwable?) { - if (!delegate.isErrorEnabled) { - return - } - - withMdc { - delegate.log(null, fqcn, LocationAwareLogger.ERROR_INT, msg, null, t) - } - } - - override fun error(marker: Marker?, msg: String?) { - if (!delegate.isErrorEnabled) { - return - } - - withMdc { - delegate.log(marker, fqcn, LocationAwareLogger.ERROR_INT, msg, null, null) - } - } - - override fun error(marker: Marker?, format: String?, arg: Any?) { - if (!delegate.isErrorEnabled) { - return - } - - withMdc { - val formattedMessage = MessageFormatter.format(format, arg).message - delegate.log(marker, fqcn, LocationAwareLogger.ERROR_INT, formattedMessage, arrayOf(arg), null) - } - } - - override fun error(marker: Marker?, format: String?, arg1: Any?, arg2: Any?) { - if (!delegate.isErrorEnabled) { - return - } - - withMdc { - val formattedMessage = MessageFormatter.format(format, arg1, arg2).message - delegate.log(marker, fqcn, LocationAwareLogger.ERROR_INT, formattedMessage, arrayOf(arg1, arg2), null) - } - } - - override fun error(marker: Marker?, format: String?, argArray: Array) { - if (!delegate.isErrorEnabled) { - return - } - - withMdc { - val formattedMessage = MessageFormatter.arrayFormat(format, argArray).message - delegate.log(marker, fqcn, LocationAwareLogger.ERROR_INT, formattedMessage, argArray, null) - } - } - - override fun error(marker: Marker?, msg: String?, t: Throwable?) { - if (!delegate.isErrorEnabled) { - return - } - - withMdc { - delegate.log(marker, fqcn, LocationAwareLogger.ERROR_INT, msg, null, t) - } - } -} diff --git a/odcsim-core/src/main/kotlin/com/atlarge/odcsim/internal/logging/LocationIgnorantLoggerImpl.kt b/odcsim-core/src/main/kotlin/com/atlarge/odcsim/internal/logging/LocationIgnorantLoggerImpl.kt deleted file mode 100644 index 999e30e6..00000000 --- a/odcsim-core/src/main/kotlin/com/atlarge/odcsim/internal/logging/LocationIgnorantLoggerImpl.kt +++ /dev/null @@ -1,440 +0,0 @@ -/* - * MIT License - * - * Copyright (c) 2019 atlarge-research - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -package com.atlarge.odcsim.internal.logging - -import com.atlarge.odcsim.ActorContext -import org.slf4j.Logger -import org.slf4j.Marker - -/** - * A [Logger] implementation that is not aware of the calling location. - * - * @param ctx The owning [ActorContext] of this logger. - * @param delegate The [Logger] to delegate the messages to. - */ -internal class LocationIgnorantLoggerImpl( - ctx: ActorContext<*>, - private val delegate: Logger -) : LoggerImpl(ctx), Logger by delegate { - override fun warn(marker: Marker?, format: String?, arg1: Any?, arg2: Any?) { - if (!isWarnEnabled) { - return - } - - withMdc { delegate.warn(marker, format, arg1, arg2) } - } - - override fun warn(format: String?, arg1: Any?, arg2: Any?) { - if (!isWarnEnabled) { - return - } - - withMdc { delegate.warn(format, arg1, arg2) } - } - - override fun warn(msg: String?) { - if (!isWarnEnabled) { - return - } - - withMdc { delegate.warn(msg) } - } - - override fun warn(marker: Marker?, format: String?, arg: Any?) { - if (!isWarnEnabled) { - return - } - - withMdc { delegate.warn(marker, format, arg) } - } - - override fun warn(marker: Marker?, format: String?, vararg arguments: Any?) { - if (!isWarnEnabled) { - return - } - - withMdc { delegate.warn(marker, format, arguments) } - } - - override fun warn(format: String?, arg: Any?) { - if (!isWarnEnabled) { - return - } - - withMdc { delegate.warn(format, arg) } - } - - override fun warn(marker: Marker?, msg: String?) { - if (!isWarnEnabled) { - return - } - - withMdc { delegate.warn(marker, msg) } - } - - override fun warn(msg: String?, t: Throwable?) { - if (!isWarnEnabled) { - return - } - - withMdc { delegate.warn(msg, t) } - } - - override fun warn(format: String?, vararg arguments: Any?) { - if (!isWarnEnabled) { - return - } - - withMdc { delegate.warn(format, *arguments) } - } - - override fun warn(marker: Marker?, msg: String?, t: Throwable?) { - if (!isWarnEnabled) { - return - } - - withMdc { delegate.warn(marker, msg, t) } - } - - override fun info(marker: Marker?, format: String?, vararg arguments: Any?) { - if (!isInfoEnabled) { - return - } - - withMdc { delegate.info(marker, format, *arguments) } - } - - override fun info(format: String?, arg: Any?) { - if (!isInfoEnabled) { - return - } - - withMdc { delegate.info(format, arg) } - } - - override fun info(marker: Marker?, msg: String?, t: Throwable?) { - if (!isInfoEnabled) { - return - } - - withMdc { delegate.info(marker, msg, t) } - } - - override fun info(msg: String?) { - if (!isInfoEnabled) { - return - } - - withMdc { delegate.info(msg) } - } - - override fun info(format: String?, vararg arguments: Any?) { - if (!isInfoEnabled) { - return - } - - withMdc { delegate.info(format, *arguments) } - } - - override fun info(format: String?, arg1: Any?, arg2: Any?) { - if (!isInfoEnabled) { - return - } - - withMdc { delegate.info(format, arg1, arg2) } - } - - override fun info(marker: Marker?, msg: String?) { - if (!isInfoEnabled) { - return - } - - withMdc { delegate.info(marker, msg) } - } - - override fun info(marker: Marker?, format: String?, arg: Any?) { - if (!isInfoEnabled) { - return - } - - withMdc { delegate.info(marker, format, arg) } - } - - override fun info(marker: Marker?, format: String?, arg1: Any?, arg2: Any?) { - if (!isInfoEnabled) { - return - } - - withMdc { delegate.info(marker, format, arg1, arg2) } - } - - override fun info(msg: String?, t: Throwable?) { - if (!isInfoEnabled) { - return - } - - withMdc { delegate.info(msg, t) } - } - - override fun error(msg: String?) { - if (!isErrorEnabled) { - return - } - - withMdc { delegate.error(msg) } - } - - override fun error(marker: Marker?, msg: String?) { - if (!isErrorEnabled) { - return - } - - withMdc { delegate.error(marker, msg) } - } - - override fun error(format: String?, vararg arguments: Any?) { - if (!isErrorEnabled) { - return - } - - withMdc { delegate.error(format, *arguments) } - } - - override fun error(format: String?, arg: Any?) { - if (!isErrorEnabled) { - return - } - - withMdc { delegate.error(format, arg) } - } - - override fun error(msg: String?, t: Throwable?) { - if (!isErrorEnabled) { - return - } - - withMdc { delegate.error(msg, t) } - } - - override fun error(marker: Marker?, format: String?, arg1: Any?, arg2: Any?) { - if (!isErrorEnabled) { - return - } - - withMdc { delegate.error(marker, format, arg1, arg2) } - } - - override fun error(marker: Marker?, format: String?, vararg arguments: Any?) { - if (!isErrorEnabled) { - return - } - - withMdc { delegate.error(marker, format, *arguments) } - } - - override fun error(marker: Marker?, msg: String?, t: Throwable?) { - if (!isErrorEnabled) { - return - } - - withMdc { delegate.error(marker, msg, t) } - } - - override fun error(format: String?, arg1: Any?, arg2: Any?) { - if (!isErrorEnabled) { - return - } - - withMdc { delegate.error(format, arg1, arg2) } - } - - override fun error(marker: Marker?, format: String?, arg: Any?) { - if (!isErrorEnabled) { - return - } - - withMdc { delegate.error(marker, format, arg) } - } - - override fun debug(format: String?, vararg arguments: Any?) { - if (!isDebugEnabled) { - return - } - - withMdc { delegate.debug(format, *arguments) } - } - - override fun debug(format: String?, arg1: Any?, arg2: Any?) { - if (!isDebugEnabled) { - return - } - - withMdc { delegate.debug(format, arg1, arg2) } - } - - override fun debug(msg: String?, t: Throwable?) { - if (!isDebugEnabled) { - return - } - - withMdc { delegate.debug(msg, t) } - } - - override fun debug(format: String?, arg: Any?) { - if (!isDebugEnabled) { - return - } - - withMdc { delegate.debug(format, arg) } - } - - override fun debug(marker: Marker?, msg: String?) { - if (!isDebugEnabled) { - return - } - - withMdc { delegate.debug(marker, msg) } - } - - override fun debug(msg: String?) { - if (!isDebugEnabled) { - return - } - - withMdc { delegate.debug(msg) } - } - - override fun debug(marker: Marker?, msg: String?, t: Throwable?) { - if (!isDebugEnabled) { - return - } - - withMdc { delegate.debug(marker, msg, t) } - } - - override fun debug(marker: Marker?, format: String?, arg1: Any?, arg2: Any?) { - if (!isDebugEnabled) { - return - } - - withMdc { delegate.debug(marker, format, arg1, arg2) } - } - - override fun debug(marker: Marker?, format: String?, arg: Any?) { - if (!isDebugEnabled) { - return - } - - withMdc { delegate.debug(marker, format, arg) } - } - - override fun debug(marker: Marker?, format: String?, vararg arguments: Any?) { - if (!isDebugEnabled) { - return - } - - withMdc { delegate.debug(marker, format, *arguments) } - } - - override fun trace(format: String?, arg: Any?) { - if (!isTraceEnabled) { - return - } - - withMdc { delegate.trace(format, arg) } - } - - override fun trace(marker: Marker?, msg: String?) { - if (!isTraceEnabled) { - return - } - - withMdc { delegate.trace(marker, msg) } - } - - override fun trace(msg: String?) { - if (!isTraceEnabled) { - return - } - - withMdc { delegate.trace(msg) } - } - - override fun trace(msg: String?, t: Throwable?) { - if (!isTraceEnabled) { - return - } - - withMdc { delegate.trace(msg, t) } - } - - override fun trace(format: String?, arg1: Any?, arg2: Any?) { - if (!isTraceEnabled) { - return - } - - withMdc { delegate.trace(format, arg1, arg2) } - } - - override fun trace(marker: Marker?, format: String?, arg1: Any?, arg2: Any?) { - if (!isTraceEnabled) { - return - } - - withMdc { delegate.trace(marker, format, arg1, arg2) } - } - - override fun trace(marker: Marker?, format: String?, arg: Any?) { - if (!isTraceEnabled) { - return - } - - withMdc { delegate.trace(marker, format, arg) } - } - - override fun trace(marker: Marker?, format: String?, vararg argArray: Any?) { - if (!isTraceEnabled) { - return - } - - withMdc { delegate.trace(marker, format, *argArray) } - } - - override fun trace(marker: Marker?, msg: String?, t: Throwable?) { - if (!isTraceEnabled) { - return - } - - withMdc { delegate.trace(marker, msg, t) } - } - - override fun trace(format: String?, vararg arguments: Any?) { - if (!isTraceEnabled) { - return - } - - withMdc { delegate.trace(format, *arguments) } - } -} diff --git a/odcsim-core/src/main/kotlin/com/atlarge/odcsim/internal/logging/LoggerImpl.kt b/odcsim-core/src/main/kotlin/com/atlarge/odcsim/internal/logging/LoggerImpl.kt deleted file mode 100644 index f971f08d..00000000 --- a/odcsim-core/src/main/kotlin/com/atlarge/odcsim/internal/logging/LoggerImpl.kt +++ /dev/null @@ -1,77 +0,0 @@ -/* - * MIT License - * - * Copyright (c) 2019 atlarge-research - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -package com.atlarge.odcsim.internal.logging - -import com.atlarge.odcsim.ActorContext -import org.slf4j.Logger -import org.slf4j.LoggerFactory -import org.slf4j.MDC -import org.slf4j.spi.LocationAwareLogger - -/** - * An actor-specific [Logger] implementation. - * - * @param ctx The owning [ActorContext] of this logger. - */ -abstract class LoggerImpl internal constructor(protected val ctx: ActorContext<*>) : Logger { - /** - * Configure [MDC] with actor-specific information. - */ - protected inline fun withMdc(block: () -> Unit) { - MDC.put(MDC_ACTOR_SYSTEM, ctx.system.name) - MDC.put(MDC_ACTOR_TIME, String.format("%.2f", ctx.time)) - MDC.put(MDC_ACTOR_REF, ctx.self.path.toString()) - try { - block() - } finally { - MDC.remove(MDC_ACTOR_SYSTEM) - MDC.remove(MDC_ACTOR_TIME) - MDC.remove(MDC_ACTOR_REF) - } - } - - /** - * Mapped Diagnostic Context (MDC) attribute names. - */ - companion object { - val MDC_ACTOR_SYSTEM = "actor.system" - val MDC_ACTOR_TIME = "actor.time" - val MDC_ACTOR_REF = "actor.ref" - - /** - * Create a [Logger] for the specified [ActorContext]. - * - * @param ctx The actor context to create the logger for. - */ - operator fun invoke(ctx: ActorContext<*>): Logger { - val logger = LoggerFactory.getLogger(ctx.javaClass) - return if (logger is LocationAwareLogger) { - LocationAwareLoggerImpl(ctx, logger) - } else { - LocationIgnorantLoggerImpl(ctx, logger) - } - } - } -} diff --git a/odcsim-core/src/test/kotlin/com/atlarge/odcsim/ActorPathTest.kt b/odcsim-core/src/test/kotlin/com/atlarge/odcsim/ActorPathTest.kt deleted file mode 100644 index 023d3efd..00000000 --- a/odcsim-core/src/test/kotlin/com/atlarge/odcsim/ActorPathTest.kt +++ /dev/null @@ -1,86 +0,0 @@ -/* - * MIT License - * - * Copyright (c) 2018 atlarge-research - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -package com.atlarge.odcsim - -import org.junit.jupiter.api.Assertions.assertEquals -import org.junit.jupiter.api.DisplayName -import org.junit.jupiter.api.Test -import org.junit.jupiter.api.assertThrows - -/** - * A test suite for the [ActorPath] class. - */ -@DisplayName("ActorPath") -class ActorPathTest { - /** - * Test whether an [ActorPath.Root] may only start with a slash. - */ - @Test - fun `root node may only start with a slash`() { - ActorPath.Root() // Assert slash at start - assertThrows { ActorPath.Root("abc/") } - } - - /** - * Test whether an [ActorPath.Child] disallows names with a slash. - */ - @Test - fun `child node should not allow name with a slash`() { - assertThrows { ActorPath.Child(ActorPath.Root(), "/") } - } - - /** - * Test whether a root node can have a custom name. - */ - @Test - fun `root node can have a custom name`() { - val name = "user" - assertEquals(name, ActorPath.Root(name).name) - } - - /** - * Test whether a child node can be created on a root. - */ - @Test - fun `child node can be created on a root`() { - val root = ActorPath.Root(name = "/user") - val child = root.child("child") - - assertEquals(root, child.parent) - assertEquals("child", child.name) - } - - /** - * Test whether a child node can be created on a child. - */ - @Test - fun `child node can be created on a child`() { - val root = ActorPath.Root(name = "/user").child("child") - val child = root.child("child") - - assertEquals(root, child.parent) - assertEquals("child", child.name) - } -} diff --git a/odcsim-core/src/test/kotlin/com/atlarge/odcsim/BehaviorTest.kt b/odcsim-core/src/test/kotlin/com/atlarge/odcsim/BehaviorTest.kt deleted file mode 100644 index 1eb4f3b9..00000000 --- a/odcsim-core/src/test/kotlin/com/atlarge/odcsim/BehaviorTest.kt +++ /dev/null @@ -1,77 +0,0 @@ -/* - * MIT License - * - * Copyright (c) 2018 atlarge-research - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -package com.atlarge.odcsim - -import com.atlarge.odcsim.internal.BehaviorInterpreter -import com.nhaarman.mockitokotlin2.mock -import org.junit.jupiter.api.DisplayName -import org.junit.jupiter.api.Test -import org.junit.jupiter.api.assertThrows - -/** - * Test suite for [Behavior] and [BehaviorInterpreter]. - */ -@DisplayName("Behavior") -class BehaviorTest { - /** - * Test whether we cannot start an actor with the [unhandled] behavior. - */ - @Test - fun `should not start with unhandled behavior`() { - val ctx = mock>() - val interpreter = BehaviorInterpreter(unhandled()) - assertThrows { interpreter.start(ctx) } - } - - /** - * Test whether we cannot start an actor with deferred unhandled behavior. - */ - @Test - fun `should not start with deferred unhandled behavior`() { - val ctx = mock>() - val interpreter = BehaviorInterpreter(setup { unhandled() }) - assertThrows { interpreter.start(ctx) } - } - - /** - * Test whether deferred behavior that returns [same] fails. - */ - @Test - fun `should not allow setup to return same`() { - val ctx = mock>() - val interpreter = BehaviorInterpreter(setup { same() }) - assertThrows { interpreter.start(ctx) } - } - - /** - * Test whether deferred behavior that returns [unhandled] fails. - */ - @Test - fun `should not allow setup to return unhandled`() { - val ctx = mock>() - val interpreter = BehaviorInterpreter(setup { unhandled() }) - assertThrows { interpreter.start(ctx) } - } -} diff --git a/odcsim-core/src/test/kotlin/com/atlarge/odcsim/CoroutinesTest.kt b/odcsim-core/src/test/kotlin/com/atlarge/odcsim/CoroutinesTest.kt deleted file mode 100644 index af7619e6..00000000 --- a/odcsim-core/src/test/kotlin/com/atlarge/odcsim/CoroutinesTest.kt +++ /dev/null @@ -1,63 +0,0 @@ -/* - * MIT License - * - * Copyright (c) 2019 atlarge-research - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -package com.atlarge.odcsim - -import com.atlarge.odcsim.coroutines.SuspendingBehavior -import com.atlarge.odcsim.coroutines.suspending -import com.atlarge.odcsim.internal.BehaviorInterpreter -import com.atlarge.odcsim.internal.EmptyBehavior -import com.nhaarman.mockitokotlin2.mock -import org.junit.jupiter.api.Assertions.assertTrue -import org.junit.jupiter.api.DisplayName -import org.junit.jupiter.api.Test -import kotlin.coroutines.suspendCoroutine - -/** - * Test suite for [SuspendingBehavior] using Kotlin Coroutines. - */ -@DisplayName("Coroutines") -internal class CoroutinesTest { - - @Test - fun `should immediately return new behavior`() { - val ctx = mock>() - val behavior = suspending { empty() } - val interpreter = BehaviorInterpreter(behavior) - interpreter.start(ctx) - assertTrue(interpreter.behavior as Behavior<*> is EmptyBehavior) - } - - @Test - fun `should be able to invoke regular suspend methods`() { - val ctx = mock>() - val behavior = suspending { - suspendCoroutine {} - stopped() - } - val interpreter = BehaviorInterpreter(behavior) - interpreter.start(ctx) - interpreter.interpretMessage(ctx, Unit) - } -} diff --git a/odcsim-engine-omega/build.gradle.kts b/odcsim-engine-omega/build.gradle.kts deleted file mode 100644 index 57e68b2c..00000000 --- a/odcsim-engine-omega/build.gradle.kts +++ /dev/null @@ -1,50 +0,0 @@ -/* - * MIT License - * - * Copyright (c) 2018 atlarge-research - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -/* Build configuration */ -apply(from = "../gradle/kotlin.gradle") -plugins { - `java-library` -} - -/* Project configuration */ -repositories { - jcenter() -} - -val junitJupiterVersion: String by extra -val junitPlatformVersion: String by extra - -dependencies { - api(project(":odcsim-core")) - - implementation(kotlin("stdlib")) - implementation("org.jetbrains:annotations:17.0.0") - - testCompile(project(":odcsim-engine-tests")) - testImplementation("org.junit.jupiter:junit-jupiter-api:$junitJupiterVersion") - testRuntimeOnly("org.junit.jupiter:junit-jupiter-engine:$junitJupiterVersion") - testImplementation("org.junit.platform:junit-platform-launcher:$junitPlatformVersion") - testRuntimeOnly("org.slf4j:slf4j-simple:1.7.25") -} diff --git a/odcsim-engine-omega/src/main/kotlin/com/atlarge/odcsim/engine/omega/OmegaActorSystem.kt b/odcsim-engine-omega/src/main/kotlin/com/atlarge/odcsim/engine/omega/OmegaActorSystem.kt deleted file mode 100644 index dd92f90a..00000000 --- a/odcsim-engine-omega/src/main/kotlin/com/atlarge/odcsim/engine/omega/OmegaActorSystem.kt +++ /dev/null @@ -1,360 +0,0 @@ -/* - * MIT License - * - * Copyright (c) 2018 atlarge-research - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -package com.atlarge.odcsim.engine.omega - -import com.atlarge.odcsim.ActorContext -import com.atlarge.odcsim.ActorPath -import com.atlarge.odcsim.ActorRef -import com.atlarge.odcsim.ActorSystem -import com.atlarge.odcsim.Behavior -import com.atlarge.odcsim.Duration -import com.atlarge.odcsim.Envelope -import com.atlarge.odcsim.Instant -import com.atlarge.odcsim.PostStop -import com.atlarge.odcsim.PreStart -import com.atlarge.odcsim.Signal -import com.atlarge.odcsim.Terminated -import com.atlarge.odcsim.empty -import com.atlarge.odcsim.internal.BehaviorInterpreter -import com.atlarge.odcsim.internal.logging.LoggerImpl -import org.jetbrains.annotations.Async -import org.slf4j.Logger -import java.util.Collections -import java.util.PriorityQueue -import java.util.UUID -import java.util.WeakHashMap -import kotlin.math.max - -/** - * The reference implementation of the [ActorSystem] instance for the OpenDC simulation core. - * - * This engine implementation is a single-threaded implementation, running actors synchronously and - * provides a single priority queue for all events (messages, ticks, etc) that occur. - * - * @param guardianBehavior The behavior of the guardian (root) actor. - * @param name The name of the engine instance. - */ -class OmegaActorSystem(guardianBehavior: Behavior, override val name: String) : ActorSystem, ActorRef { - /** - * The state of the actor system. - */ - private var state: ActorSystemState = ActorSystemState.CREATED - - /** - * The event queue to process - */ - private val queue: PriorityQueue = PriorityQueue( - Comparator - .comparingDouble(EnvelopeImpl::time) - .thenComparingLong(EnvelopeImpl::id) - ) - - /** - * The registry of actors in the system. - */ - private val registry: MutableMap> = HashMap() - - /** - * The root actor path of the system. - */ - private val root: ActorPath = ActorPath.Root() - - /** - * The system actor path. - */ - private val system: ActorPath = root / "system" - - /** - * The current point in simulation time. - */ - override var time: Instant = .0 - - /** - * The path to the root actor. - */ - override val path: ActorPath = root / "user" - - init { - registry[system] = Actor(ActorRefImpl(this, system), empty()) - registry[path] = Actor(this, guardianBehavior) - schedule(path, PreStart, .0) - } - - override fun run(until: Duration) { - require(until >= .0) { "The given instant must be a non-negative number" } - - // Start the system/guardian actor on initial run - if (state == ActorSystemState.CREATED) { - state = ActorSystemState.STARTED - registry[system]!!.isolate { it.start() } - registry[path]!!.isolate { it.start() } - } else if (state == ActorSystemState.TERMINATED) { - throw IllegalStateException("The ActorSystem has been terminated.") - } - - while (time < until) { - // Check whether the system was interrupted - if (Thread.interrupted()) { - throw InterruptedException() - } - - val envelope = queue.peek() ?: break - val delivery = envelope.time.takeUnless { it > until } ?: break - - // A message should never be delivered out of order in this single-threaded implementation. Assert for - // sanity - assert(delivery >= time) { "Message delivered out of order [expected=$delivery, actual=$time]" } - - time = delivery - queue.poll() - - processEnvelope(envelope) - } - - // Jump forward in time as the caller expects the system to have run until the specified instant - // Taking the maximum value prevents the caller to jump backwards in time - time = max(time, until) - } - - override fun send(msg: T, after: Duration) = schedule(path, msg, after) - - override fun terminate() { - registry[path]?.stop(null) - registry[system]?.stop(null) - } - - override suspend fun spawnSystem(behavior: Behavior, name: String): ActorRef { - return registry[system]!!.spawn(behavior, name) - } - - override fun compareTo(other: ActorRef<*>): Int = path.compareTo(other.path) - - /** - * The identifier for the next message to be scheduled. - */ - private var nextId: Long = 0 - - /** - * Schedule a message to be processed by the engine. - * - * @param path The path to the destination of the message. - * @param message The message to schedule. - * @param delay The time to wait before processing the message. - */ - private fun schedule(@Async.Schedule path: ActorPath, message: Any, delay: Duration) { - require(delay >= .0) { "The given delay must be a non-negative number" } - scheduleEnvelope(EnvelopeImpl(nextId++, path, time + delay, message)) - } - - /** - * Schedule the specified envelope to be processed by the engine. - */ - private fun scheduleEnvelope(@Async.Schedule envelope: EnvelopeImpl) { - queue.add(envelope) - } - - /** - * Process the delivery of a message. - */ - private fun processEnvelope(@Async.Execute envelope: EnvelopeImpl) { - val actor = registry[envelope.destination] ?: return - - // Notice that messages for unknown/terminated actors are ignored for now - actor.isolate { it.interpretMessage(envelope.message) } - } - - /** - * An actor as represented in the Omega engine. - * - * @param self The [ActorRef] to this actor. - * @param initialBehavior The initial behavior of this actor. - */ - private inner class Actor(override val self: ActorRef, initialBehavior: Behavior) : ActorContext { - val childActors: MutableMap> = mutableMapOf() - val interpreter = BehaviorInterpreter(initialBehavior) - val watchers: MutableSet = Collections.newSetFromMap(WeakHashMap()) - - override val time: Instant - get() = this@OmegaActorSystem.time - - override val children: List> - get() = childActors.values.map { it.self } - - override val system: ActorSystem<*> - get() = this@OmegaActorSystem - - override val log: Logger by lazy(LazyThreadSafetyMode.NONE) { LoggerImpl(this) } - - override fun getChild(name: String): ActorRef<*>? = childActors[name]?.self - - override fun send(ref: ActorRef, msg: U, after: Duration) = schedule(ref.path, msg, after) - - override fun spawn(behavior: Behavior, name: String): ActorRef { - require(name.isNotEmpty()) { "Actor name may not be empty" } - require(!name.startsWith("$")) { "Actor name may not start with $-sign" } - return internalSpawn(behavior, name) - } - - override fun spawnAnonymous(behavior: Behavior): ActorRef { - val name = "$" + UUID.randomUUID() - return internalSpawn(behavior, name) - } - - private fun internalSpawn(behavior: Behavior, name: String): ActorRef { - require(name !in childActors) { "Actor name $name not unique" } - val ref = ActorRefImpl(this@OmegaActorSystem, self.path.child(name)) - val actor = Actor(ref, behavior) - registry[ref.path] = actor - childActors[name] = actor - schedule(ref.path, PreStart, .0) - actor.start() - return ref - } - - override fun stop(child: ActorRef<*>) { - when { - // Must be a direct child of this actor - child.path.parent == self.path -> { - val ref = childActors[child.path.name] ?: return - ref.stop(null) - } - self == child -> throw IllegalArgumentException( - "Only direct children of an actor may be stopped through the actor context, " + - "but you tried to stop [$self] by passing its ActorRef to the `stop` method. " + - "Stopping self has to be expressed as explicitly returning a Stop Behavior." - ) - else -> throw IllegalArgumentException( - "Only direct children of an actor may be stopped through the actor context, " + - "but [$child] is not a child of [$self]. Stopping other actors has to be expressed as " + - "an explicit stop message that the actor accepts." - ) - } - } - - override fun watch(target: ActorRef<*>) { - registry[target.path]?.watchers?.add(path) - } - - override fun unwatch(target: ActorRef<*>) { - registry[target.path]?.watchers?.remove(path) - } - - // Synchronization of actors in a single-threaded simulation is trivial: all actors are consistent in virtual - // time. - override fun sync(target: ActorRef<*>) {} - - override fun unsync(target: ActorRef<*>) {} - - override fun isSync(target: ActorRef<*>): Boolean = true - - /** - * Start this actor. - */ - fun start() { - interpreter.start(this) - } - - /** - * Stop this actor. - */ - fun stop(failure: Throwable?) { - interpreter.stop(this) - childActors.values.forEach { it.stop(failure) } - registry.remove(self.path) - interpreter.interpretSignal(this, PostStop) - val termination = Terminated(self, failure) - watchers.forEach { schedule(it, termination, 0.0) } - } - - /** - * Interpret the given message send to an actor. - */ - fun interpretMessage(msg: Any) { - if (msg is Signal) { - interpreter.interpretSignal(this, msg) - } else { - @Suppress("UNCHECKED_CAST") - interpreter.interpretMessage(this, msg as T) - } - - if (!interpreter.isAlive) { - stop(null) - } - } - - override fun equals(other: Any?): Boolean = - other is OmegaActorSystem<*>.Actor<*> && self.path == other.self.path - - override fun hashCode(): Int = self.path.hashCode() - } - - /** - * Isolate uncaught exceptions originating from actor interpreter invocations. - */ - private inline fun Actor.isolate(block: (Actor) -> U): U? { - return try { - block(this) - } catch (t: Throwable) { - // Forcefully stop the actor if it crashed - stop(t) - log.error("Unhandled exception in actor $path", t) - null - } - } - - /** - * Enumeration to track the state of the actor system. - */ - private enum class ActorSystemState { - CREATED, STARTED, TERMINATED - } - - /** - * Internal [ActorRef] implementation for this actor system. - */ - private data class ActorRefImpl( - private val owner: OmegaActorSystem<*>, - override val path: ActorPath - ) : ActorRef { - override fun toString(): String = "Actor[$path]" - - override fun compareTo(other: ActorRef<*>): Int = path.compareTo(other.path) - } - - /** - * A wrapper around a message that has been scheduled for processing. - * - * @property id The identifier of the message to keep the priority queue stable. - * @property destination The destination of the message. - * @property time The point in time to deliver the message. - * @property message The message to wrap. - */ - private class EnvelopeImpl( - val id: Long, - val destination: ActorPath, - override val time: Instant, - override val message: Any - ) : Envelope -} diff --git a/odcsim-engine-omega/src/main/kotlin/com/atlarge/odcsim/engine/omega/OmegaActorSystemFactory.kt b/odcsim-engine-omega/src/main/kotlin/com/atlarge/odcsim/engine/omega/OmegaActorSystemFactory.kt deleted file mode 100644 index 84bf1efb..00000000 --- a/odcsim-engine-omega/src/main/kotlin/com/atlarge/odcsim/engine/omega/OmegaActorSystemFactory.kt +++ /dev/null @@ -1,38 +0,0 @@ -/* - * MIT License - * - * Copyright (c) 2018 atlarge-research - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -package com.atlarge.odcsim.engine.omega - -import com.atlarge.odcsim.ActorSystem -import com.atlarge.odcsim.ActorSystemFactory -import com.atlarge.odcsim.Behavior -import java.util.ServiceLoader - -/** - * An [ActorSystemFactory] for the Omega engine, used by the [ServiceLoader] API to create [OmegaActorSystem] instances. - */ -class OmegaActorSystemFactory : ActorSystemFactory { - override operator fun invoke(root: Behavior, name: String): ActorSystem = - OmegaActorSystem(root, name) -} diff --git a/odcsim-engine-omega/src/main/resources/META-INF/services/com.atlarge.odcsim.ActorSystemFactory b/odcsim-engine-omega/src/main/resources/META-INF/services/com.atlarge.odcsim.ActorSystemFactory deleted file mode 100644 index d0ca8859..00000000 --- a/odcsim-engine-omega/src/main/resources/META-INF/services/com.atlarge.odcsim.ActorSystemFactory +++ /dev/null @@ -1 +0,0 @@ -com.atlarge.odcsim.engine.omega.OmegaActorSystemFactory diff --git a/odcsim-engine-omega/src/test/kotlin/com/atlarge/odcsim/engine/omega/OmegaActorSystemFactoryTest.kt b/odcsim-engine-omega/src/test/kotlin/com/atlarge/odcsim/engine/omega/OmegaActorSystemFactoryTest.kt deleted file mode 100644 index 4e195e6e..00000000 --- a/odcsim-engine-omega/src/test/kotlin/com/atlarge/odcsim/engine/omega/OmegaActorSystemFactoryTest.kt +++ /dev/null @@ -1,37 +0,0 @@ -/* - * MIT License - * - * Copyright (c) 2018 atlarge-research - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -package com.atlarge.odcsim.engine.omega - -import com.atlarge.odcsim.ActorSystemFactory -import com.atlarge.odcsim.engine.tests.ActorSystemFactoryContract -import org.junit.jupiter.api.DisplayName - -/** - * The [ActorSystemFactory] test suite for the Omega engine implementation. - */ -@DisplayName("OmegaActorSystemFactory") -class OmegaActorSystemFactoryTest : ActorSystemFactoryContract() { - override fun createFactory(): ActorSystemFactory = OmegaActorSystemFactory() -} diff --git a/odcsim-engine-omega/src/test/kotlin/com/atlarge/odcsim/engine/omega/OmegaActorSystemTest.kt b/odcsim-engine-omega/src/test/kotlin/com/atlarge/odcsim/engine/omega/OmegaActorSystemTest.kt deleted file mode 100644 index dc310d47..00000000 --- a/odcsim-engine-omega/src/test/kotlin/com/atlarge/odcsim/engine/omega/OmegaActorSystemTest.kt +++ /dev/null @@ -1,37 +0,0 @@ -/* - * MIT License - * - * Copyright (c) 2018 atlarge-research - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -package com.atlarge.odcsim.engine.omega - -import com.atlarge.odcsim.ActorSystem -import com.atlarge.odcsim.engine.tests.ActorSystemContract -import org.junit.jupiter.api.DisplayName - -/** - * The [ActorSystem] test suite for the Omega engine implementation. - */ -@DisplayName("OmegaActorSystem") -class OmegaActorSystemTest : ActorSystemContract() { - override val factory = OmegaActorSystemFactory() -} diff --git a/odcsim-engine-tests/build.gradle.kts b/odcsim-engine-tests/build.gradle.kts deleted file mode 100644 index 8b19019a..00000000 --- a/odcsim-engine-tests/build.gradle.kts +++ /dev/null @@ -1,43 +0,0 @@ -/* - * MIT License - * - * Copyright (c) 2019 atlarge-research - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -/* Build configuration */ -apply(from = "../gradle/kotlin.gradle") -plugins { - `java-library` -} - -/* Project configuration */ -repositories { - jcenter() -} - -val junitJupiterVersion: String by extra - -dependencies { - api(project(":odcsim-core")) - - implementation(kotlin("stdlib")) - implementation("org.junit.jupiter:junit-jupiter-api:$junitJupiterVersion") -} diff --git a/odcsim-engine-tests/src/main/kotlin/com/atlarge/odcsim/engine/tests/ActorSystemContract.kt b/odcsim-engine-tests/src/main/kotlin/com/atlarge/odcsim/engine/tests/ActorSystemContract.kt deleted file mode 100644 index 593f587b..00000000 --- a/odcsim-engine-tests/src/main/kotlin/com/atlarge/odcsim/engine/tests/ActorSystemContract.kt +++ /dev/null @@ -1,403 +0,0 @@ -/* - * MIT License - * - * Copyright (c) 2019 atlarge-research - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -package com.atlarge.odcsim.engine.tests - -import com.atlarge.odcsim.ActorPath -import com.atlarge.odcsim.ActorRef -import com.atlarge.odcsim.ActorSystemFactory -import com.atlarge.odcsim.Behavior -import com.atlarge.odcsim.Terminated -import com.atlarge.odcsim.coroutines.dsl.timeout -import com.atlarge.odcsim.coroutines.suspending -import com.atlarge.odcsim.empty -import com.atlarge.odcsim.ignore -import com.atlarge.odcsim.receiveMessage -import com.atlarge.odcsim.receiveSignal -import com.atlarge.odcsim.same -import com.atlarge.odcsim.setup -import com.atlarge.odcsim.stopped -import org.junit.jupiter.api.Assertions.assertEquals -import org.junit.jupiter.api.Assertions.assertTrue -import org.junit.jupiter.api.DisplayName -import org.junit.jupiter.api.Nested -import org.junit.jupiter.api.Test -import org.junit.jupiter.api.assertThrows - -/** - * A conformance test suite for implementors of the [ActorSystem] interface. - */ -abstract class ActorSystemContract { - /** - * An [ActorSystemFactory] provided by implementors to create the [ActorSystem] to be tested. - */ - abstract val factory: ActorSystemFactory - - /** - * Test whether the created [ActorSystem] has the correct name. - */ - @Test - fun `should have a name`() { - val name = "test" - val system = factory(empty(), name) - - assertEquals(name, system.name) - system.terminate() - } - - /** - * Test whether the created [ActorSystem] has a path. - */ - @Test - fun `should have a path`() { - val system = factory(empty(), "test") - - assertTrue(system.path is ActorPath.Root) - system.terminate() - } - - /** - * Test whether creating an [ActorSystem] sets the initial time at 0. - */ - @Test - fun `should start at t=0`() { - val system = factory(empty(), name = "test") - - assertEquals(.0, system.time, DELTA) - system.terminate() - } - - /** - * Test whether an [ActorSystem] does not accept invalid points in time. - */ - @Test - fun `should not accept negative instants for running`() { - val system = factory(empty(), name = "test") - assertThrows { system.run(-10.0) } - system.terminate() - } - - /** - * Test whether an [ActorSystem] will not jump backward in time when asking to run until a specified instant - * that has already occurred. - */ - @Test - fun `should not jump backward in time`() { - val until = 10.0 - val system = factory(empty(), name = "test") - - system.run(until = until) - system.run(until = until - 0.5) - assertEquals(until, system.time, DELTA) - system.terminate() - } - - /** - * Test whether an [ActorSystem] will jump forward in time when asking to run until a specified instant. - */ - @Test - fun `should jump forward in time`() { - val until = 10.0 - val system = factory(empty(), name = "test") - - system.run(until = until) - assertEquals(until, system.time, DELTA) - system.terminate() - } - - /** - * Test whether an [ActorSystem] will jump forward in time when asking to run until a specified instant. - */ - @Test - fun `should order messages at the instant by insertion time`() { - val behavior = receiveMessage { msg -> - assertEquals(1, msg) - receiveMessage { - assertEquals(2, it) - ignore() - } - } - val system = factory(behavior, name = "test") - system.send(1, after = 1.0) - system.send(2, after = 1.0) - system.run(until = 10.0) - system.terminate() - } - - /** - * Test whether an [ActorSystem] will not process messages in the queue after the deadline. - */ - @Test - fun `should not process messages after deadline`() { - var counter = 0 - val behavior = receiveMessage { _ -> - counter++ - same() - } - val system = factory(behavior, name = "test") - system.send(Unit, after = 3.0) - system.send(Unit, after = 1.0) - system.run(until = 2.0) - assertEquals(1, counter) - system.terminate() - } - - /** - * Test whether an [ActorSystem] will not initialize the root actor if the system has not been run yet. - */ - @Test - fun `should not initialize root actor if not run`() { - val system = factory(setup { TODO() }, name = "test") - system.terminate() - } - - @Nested - @DisplayName("ActorRef") - inner class ActorRefTest { - /** - * Test whether an [ActorSystem] disallows sending messages in the past. - */ - @Test - fun `should disallow messages in the past`() { - val system = factory(empty(), name = "test") - assertThrows { system.send(Unit, after = -1.0) } - system.terminate() - } - } - - @Nested - @DisplayName("Actor") - inner class Actor { - /** - * Test whether the pre-start time of the root actor is at 0. - */ - @Test - fun `should pre-start at t=0 if root`() { - val behavior = setup { ctx -> - assertEquals(.0, ctx.time, DELTA) - ignore() - } - - val system = factory(behavior, "test") - system.run() - system.terminate() - } - - /** - * Test whether a child actor can be created from an actor. - */ - @Test - fun `should allow spawning of child actors`() { - var spawned = false - val behavior = setup { spawned = true; empty() } - - val system = factory(setup { ctx -> - val ref = ctx.spawn(behavior, "child") - assertEquals("child", ref.path.name) - ignore() - }, name = "test") - - system.run(until = 10.0) - assertTrue(spawned) - system.terminate() - } - - /** - * Test whether a child actor can be stopped from an actor. - */ - @Test - fun `should allow stopping of child actors`() { - val system = factory(setup { ctx -> - val ref = ctx.spawn(receiveMessage { throw UnsupportedOperationException() }, "child") - ctx.stop(ref) - assertEquals("child", ref.path.name) - ignore() - }, name = "test") - - system.run(until = 10.0) - system.terminate() - } - - /** - * Test whether only the parent of a child can terminate it. - */ - @Test - fun `should only be able to terminate child actors`() { - val system = factory(setup { ctx1 -> - val child1 = ctx1.spawn(ignore(), "child-1") - ctx1.spawn(setup { ctx2 -> - ctx2.stop(child1) - ignore() - }, "child-2") - - ignore() - }, name = "test") - system.run() - system.terminate() - } - - /** - * Test whether stopping a child is idempotent. - */ - @Test - fun `should be able to stop a child twice`() { - val system = factory(setup { ctx -> - val child = ctx.spawn(ignore(), "child") - ctx.stop(child) - ctx.stop(child) - ignore() - }, name = "test") - system.run() - system.terminate() - } - - /** - * Test whether termination of a child also results in termination of its children. - */ - @Test - fun `should terminate children of child when terminating it`() { - val system = factory(setup> { ctx -> - val root = ctx.self - val child = ctx.spawn(setup { - val child = it.spawn(receiveMessage { - throw IllegalStateException("DELIBERATE") - }, "child") - ctx.send(root, child) - ignore() - }, "child") - - receiveMessage { msg -> - ctx.stop(child) - ctx.send(msg, Unit) // This actor should be stopped now and not receive the message anymore - stopped() - } - }, name = "test") - - system.run() - system.terminate() - } - - /** - * Test whether [same] works correctly. - */ - @Test - fun `should keep same behavior on same`() { - var counter = 0 - - val behavior = setup { ctx -> - counter++ - ctx.send(ctx.self, Unit) - receiveMessage { - counter++ - same() - } - } - - val system = factory(behavior, "test") - system.run() - assertEquals(2, counter) - system.terminate() - } - - /** - * Test whether the reference to the actor itself is valid. - */ - @Test - fun `should have reference to itself`() { - var flag = false - val behavior: Behavior = setup { ctx -> - ctx.send(ctx.self, Unit) - receiveMessage { flag = true; same() } - } - - val system = factory(behavior, "test") - system.run() - assertTrue(flag) - system.terminate() - } - - /** - * Test whether we can start an actor with the [stopped] behavior. - */ - @Test - fun `should start with stopped behavior`() { - val system = factory(stopped(), "test") - system.run() - system.terminate() - } - - /** - * Test whether an actor that is crashed cannot receive more messages. - */ - @Test - fun `should stop if it crashes`() { - var counter = 0 - val system = factory(receiveMessage { - counter++ - throw IllegalArgumentException("STAGED") - }, "test") - - system.send(Unit) - system.send(Unit) - - system.run() - assertEquals(1, counter) - system.terminate() - } - - /** - * Test whether an actor can watch for termination. - */ - @Test - fun `should watch for termination`() { - var received = false - val system = factory(setup { ctx -> - val child = ctx.spawn(suspending { - timeout(50.0) - stopped() - }, "child") - ctx.watch(child) - - receiveSignal { _, signal -> - when (signal) { - is Terminated -> { - received = true - stopped() - } - else -> - same() - } - } - }, "test") - - system.run() - system.terminate() - assertTrue(received) - } - } - - companion object { - private const val DELTA: Double = 0.0001 - } -} diff --git a/odcsim-engine-tests/src/main/kotlin/com/atlarge/odcsim/engine/tests/ActorSystemFactoryContract.kt b/odcsim-engine-tests/src/main/kotlin/com/atlarge/odcsim/engine/tests/ActorSystemFactoryContract.kt deleted file mode 100644 index 565f4f4c..00000000 --- a/odcsim-engine-tests/src/main/kotlin/com/atlarge/odcsim/engine/tests/ActorSystemFactoryContract.kt +++ /dev/null @@ -1,73 +0,0 @@ -/* - * MIT License - * - * Copyright (c) 2019 atlarge-research - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -package com.atlarge.odcsim.engine.tests - -import com.atlarge.odcsim.ActorSystemFactory -import com.atlarge.odcsim.empty -import com.atlarge.odcsim.setup -import com.atlarge.odcsim.stopped -import org.junit.jupiter.api.Assertions.assertEquals -import org.junit.jupiter.api.Assertions.assertTrue -import org.junit.jupiter.api.Test - -/** - * A conformance test suite for implementors of the [ActorSystemFactory] interface. - */ -abstract class ActorSystemFactoryContract { - /** - * Create an [ActorSystemFactory] instance to test. - */ - abstract fun createFactory(): ActorSystemFactory - - /** - * Test whether the factory will create an [ActorSystem] with correct name. - */ - @Test - fun `should create a system with correct name`() { - val factory = createFactory() - val name = "test" - val system = factory(empty(), name) - - assertEquals(name, system.name) - system.terminate() - } - - /** - * Test whether the factory will create an [ActorSystem] with valid root behavior. - */ - @Test - fun `should create a system with correct root behavior`() { - var flag = false - val factory = createFactory() - val system = factory(setup { - flag = true - stopped() - }, "test") - - system.run(until = 10.0) - system.terminate() - assertTrue(flag) - } -} diff --git a/odcsim-testkit/build.gradle.kts b/odcsim-testkit/build.gradle.kts deleted file mode 100644 index 8b19019a..00000000 --- a/odcsim-testkit/build.gradle.kts +++ /dev/null @@ -1,43 +0,0 @@ -/* - * MIT License - * - * Copyright (c) 2019 atlarge-research - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -/* Build configuration */ -apply(from = "../gradle/kotlin.gradle") -plugins { - `java-library` -} - -/* Project configuration */ -repositories { - jcenter() -} - -val junitJupiterVersion: String by extra - -dependencies { - api(project(":odcsim-core")) - - implementation(kotlin("stdlib")) - implementation("org.junit.jupiter:junit-jupiter-api:$junitJupiterVersion") -} diff --git a/odcsim-testkit/src/main/kotlin/com/atlarge/odcsim/testkit/BehaviorTestKit.kt b/odcsim-testkit/src/main/kotlin/com/atlarge/odcsim/testkit/BehaviorTestKit.kt deleted file mode 100644 index 65782d52..00000000 --- a/odcsim-testkit/src/main/kotlin/com/atlarge/odcsim/testkit/BehaviorTestKit.kt +++ /dev/null @@ -1,138 +0,0 @@ -/* - * MIT License - * - * Copyright (c) 2019 atlarge-research - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -package com.atlarge.odcsim.testkit - -import com.atlarge.odcsim.ActorContext -import com.atlarge.odcsim.ActorPath -import com.atlarge.odcsim.ActorRef -import com.atlarge.odcsim.Behavior -import com.atlarge.odcsim.Instant -import com.atlarge.odcsim.isAlive -import com.atlarge.odcsim.testkit.internal.BehaviorTestKitImpl -import java.util.UUID - -/** - * Utensils for synchronous testing of [Behavior] instances. - * - * @param T The shape of the messages the behavior accepts. - */ -interface BehaviorTestKit { - /** - * The current point in simulation time. - */ - val time: Instant - - /** - * The current behavior of the simulated actor. - */ - val behavior: Behavior - - /** - * The self reference to the simulated actor inside the test kit. - */ - val ref: ActorRef - - /** - * The context of the simulated actor. - */ - val context: ActorContext - - /** - * The inbox of the simulated actor. - */ - val inbox: TestInbox - - /** - * A flag indicating whether the [Behavior] instance is still alive. - */ - val isAlive: Boolean get() = behavior.isAlive - - /** - * Interpret the specified message at the current point in simulation time using the current behavior. - * - * @return `true` if the message was handled by the behavior, `false` if it was unhandled. - */ - fun run(msg: T): Boolean - - /** - * Interpret the oldest message in the inbox using the current behavior. - * - * @return `true` if the message was handled by the behavior, `false` if it was unhandled. - * @throws NoSuchElementException if the actor's inbox is empty. - */ - fun runOne(): Boolean - - /** - * Interpret the messages in the inbox until the specified point in simulation time is reached. - * - * @param time The time until which the messages in the inbox should be processed. - */ - fun runTo(time: Instant) - - /** - * Create an anonymous [TestInbox] for receiving messages. - */ - fun createInbox(): TestInbox - - /** - * Get the child inbox for the child with the given name, or fail if there is no child with the given name - * spawned - */ - fun childInbox(name: String): TestInbox - - /** - * Get the child inbox for the child referenced by [ref], or fail if it is not a child of this behavior. - */ - fun childInbox(ref: ActorRef): TestInbox - - /** - * Obtain the [BehaviorTestKit] for the child with the given name, or fail if there is no child with the given - * name spawned. - */ - fun childTestKit(name: String): BehaviorTestKit - - /** - * Obtain the [BehaviorTestKit] for the given child [ActorRef]. - */ - fun childTestKit(ref: ActorRef): BehaviorTestKit - - companion object { - /** - * Create a [BehaviorTestKit] instance for the specified [Behavior]. - * - * @param behavior The behavior for which a test kit should be created. - */ - operator fun invoke(behavior: Behavior): BehaviorTestKit = - BehaviorTestKitImpl(behavior, ActorPath.Root(name = "/" + UUID.randomUUID().toString())) - - /** - * Create a [BehaviorTestKit] instance for the specified [Behavior]. - * - * @param behavior The behavior for which a test kit should be created. - */ - @JvmStatic - fun create(behavior: Behavior): BehaviorTestKit = invoke(behavior) - } -} diff --git a/odcsim-testkit/src/main/kotlin/com/atlarge/odcsim/testkit/TestInbox.kt b/odcsim-testkit/src/main/kotlin/com/atlarge/odcsim/testkit/TestInbox.kt deleted file mode 100644 index de240143..00000000 --- a/odcsim-testkit/src/main/kotlin/com/atlarge/odcsim/testkit/TestInbox.kt +++ /dev/null @@ -1,90 +0,0 @@ -/* - * MIT License - * - * Copyright (c) 2019 atlarge-research - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -package com.atlarge.odcsim.testkit - -import com.atlarge.odcsim.ActorRef -import com.atlarge.odcsim.Envelope - -/** - * A helper class for testing messages sent to an [ActorRef]. - * - * @param T The shape of the messages the inbox accepts. - */ -interface TestInbox { - /** - * The actor reference of this inbox. - */ - val ref: ActorRef - - /** - * A flag to indicate whether the inbox contains any messages. - */ - val hasMessages: Boolean - - /** - * Receive the oldest message from the inbox and remove it. - * - * @return The message that has been received. - * @throws NoSuchElementException if the inbox is empty. - */ - fun receiveMessage(): T = receiveEnvelope().message - - /** - * Receive the oldest message from the inbox and remove it. - * - * @return The envelope containing the message that has been received. - * @throws NoSuchElementException if the inbox is empty. - */ - fun receiveEnvelope(): Envelope - - /** - * Receive all messages from the inbox and empty it. - * - * @return The list of messages in the inbox. - */ - fun receiveAll(): List> - - /** - * Clear all messages from the inbox. - */ - fun clear() - - /** - * Assert that the oldest message is equal to the [expected] message and remove - * it from the inbox. - * - * @param expected The expected message to be the oldest in the inbox. - */ - fun expectMessage(expected: T) - - /** - * Assert that the oldest message is equal to the [expected] message and remove - * it from the inbox. - * - * @param expected The expected message to be the oldest in the inbox. - * @param message The failure message to fail with. - */ - fun expectMessage(expected: T, message: String) -} diff --git a/odcsim-testkit/src/main/kotlin/com/atlarge/odcsim/testkit/internal/ActorContextStub.kt b/odcsim-testkit/src/main/kotlin/com/atlarge/odcsim/testkit/internal/ActorContextStub.kt deleted file mode 100644 index 79873141..00000000 --- a/odcsim-testkit/src/main/kotlin/com/atlarge/odcsim/testkit/internal/ActorContextStub.kt +++ /dev/null @@ -1,99 +0,0 @@ -/* - * MIT License - * - * Copyright (c) 2019 atlarge-research - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -package com.atlarge.odcsim.testkit.internal - -import com.atlarge.odcsim.ActorContext -import com.atlarge.odcsim.ActorRef -import com.atlarge.odcsim.ActorSystem -import com.atlarge.odcsim.Behavior -import com.atlarge.odcsim.Duration -import com.atlarge.odcsim.Instant -import com.atlarge.odcsim.internal.logging.LoggerImpl -import org.slf4j.Logger -import java.util.UUID - -/** - * A stubbed [ActorContext] implementation for synchronous behavior testing. - * - * @property owner The owner of this context. - */ -internal class ActorContextStub(private val owner: BehaviorTestKitImpl) : ActorContext { - /** - * The children of this context. - */ - val childActors = HashMap>() - - override val self: ActorRef - get() = owner.ref - - override val children: List> - get() = childActors.values.map { it.ref } - - override val time: Instant - get() = owner.time - - override val system: ActorSystem<*> by lazy { - ActorSystemStub(owner) - } - - override val log: Logger by lazy { - LoggerImpl(this) - } - - override fun getChild(name: String): ActorRef<*>? = childActors[name]?.ref - - override fun send(ref: ActorRef, msg: U, after: Duration) { - if (ref !is TestInboxImpl.ActorRefImpl) { - throw IllegalArgumentException("The referenced ActorRef is not part of the test kit") - } - - ref.send(msg, after) - } - - override fun spawn(behavior: Behavior, name: String): ActorRef { - val btk = BehaviorTestKitImpl(behavior, self.path.child(name)) - childActors[name] = btk - return btk.ref - } - - override fun spawnAnonymous(behavior: Behavior): ActorRef { - return spawn(behavior, "$" + UUID.randomUUID()) - } - - override fun stop(child: ActorRef<*>) { - require(child.path.parent == self.path) { "Only direct children of an actor may be stopped through the actor context." } - childActors -= child.path.name - } - - override fun watch(target: ActorRef<*>) {} - - override fun unwatch(target: ActorRef<*>) {} - - override fun sync(target: ActorRef<*>) {} - - override fun unsync(target: ActorRef<*>) {} - - override fun isSync(target: ActorRef<*>): Boolean = true -} diff --git a/odcsim-testkit/src/main/kotlin/com/atlarge/odcsim/testkit/internal/ActorSystemStub.kt b/odcsim-testkit/src/main/kotlin/com/atlarge/odcsim/testkit/internal/ActorSystemStub.kt deleted file mode 100644 index fee34a48..00000000 --- a/odcsim-testkit/src/main/kotlin/com/atlarge/odcsim/testkit/internal/ActorSystemStub.kt +++ /dev/null @@ -1,58 +0,0 @@ -/* - * MIT License - * - * Copyright (c) 2019 atlarge-research - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -package com.atlarge.odcsim.testkit.internal - -import com.atlarge.odcsim.ActorPath -import com.atlarge.odcsim.ActorRef -import com.atlarge.odcsim.ActorSystem -import com.atlarge.odcsim.Behavior -import com.atlarge.odcsim.Duration -import com.atlarge.odcsim.Instant - -/** - * A stubbed [ActorSystem] for synchronous testing of behavior. - * - * @property owner The owner of this actor system. - */ -internal class ActorSystemStub(private val owner: BehaviorTestKitImpl) : ActorSystem { - override val time: Instant - get() = owner.time - - override val name: String - get() = owner.ref.path.name - - override fun run(until: Duration) = throw IllegalStateException("Cannot run ActorSystem within actor") - - override fun send(msg: T, after: Duration) = owner.context.send(owner.context.self, msg, after) - - override fun terminate() {} - - override val path: ActorPath - get() = owner.ref.path - - override suspend fun spawnSystem(behavior: Behavior, name: String): ActorRef { - throw IllegalStateException("Cannot spawn system actor") - } -} diff --git a/odcsim-testkit/src/main/kotlin/com/atlarge/odcsim/testkit/internal/BehaviorTestKitImpl.kt b/odcsim-testkit/src/main/kotlin/com/atlarge/odcsim/testkit/internal/BehaviorTestKitImpl.kt deleted file mode 100644 index 2bd5b973..00000000 --- a/odcsim-testkit/src/main/kotlin/com/atlarge/odcsim/testkit/internal/BehaviorTestKitImpl.kt +++ /dev/null @@ -1,112 +0,0 @@ -/* - * MIT License - * - * Copyright (c) 2019 atlarge-research - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -package com.atlarge.odcsim.testkit.internal - -import com.atlarge.odcsim.ActorPath -import com.atlarge.odcsim.ActorRef -import com.atlarge.odcsim.Behavior -import com.atlarge.odcsim.Instant -import com.atlarge.odcsim.internal.BehaviorInterpreter -import com.atlarge.odcsim.testkit.BehaviorTestKit -import com.atlarge.odcsim.testkit.TestInbox -import java.util.UUID -import kotlin.math.max - -/** - * Default implementation of the [BehaviorTestKit] interface. - * - * @param initialBehavior The initial behavior to initialize the actor of the test kit with. - * @param path The path to the actor. - */ -internal class BehaviorTestKitImpl( - initialBehavior: Behavior, - path: ActorPath -) : BehaviorTestKit { - /** - * The [BehaviorInterpreter] used to interpret incoming messages. - */ - private val interpreter = BehaviorInterpreter(initialBehavior) - - /** - * A flag to indicate whether the behavior was initially started. - */ - private var isStarted: Boolean = false - - override var time: Instant = .0 - private set - - override val inbox: TestInbox = TestInboxImpl(this, path) - - override val behavior: Behavior get() = interpreter.behavior - - override val ref: ActorRef = inbox.ref - - override val context: ActorContextStub = ActorContextStub(this) - - override fun run(msg: T): Boolean { - if (!isStarted) { - isStarted = true - interpreter.start(context) - } - - return interpreter.interpretMessage(context, msg) - } - - override fun runOne(): Boolean { - val (delivery, msg) = inbox.receiveEnvelope() - time = max(time, delivery) - return run(msg) - } - - override fun runTo(time: Instant) { - while (inbox.hasMessages && this.time <= time) { - runOne() - } - this.time = time - } - - override fun createInbox(): TestInbox { - return TestInboxImpl(this, ref.path.child(UUID.randomUUID().toString())) - } - - override fun childInbox(name: String): TestInbox = childTestKit(name).inbox - - override fun childInbox(ref: ActorRef): TestInbox = childTestKit(ref).inbox - - override fun childTestKit(name: String): BehaviorTestKit { - @Suppress("UNCHECKED_CAST") - return context.childActors[ref.path.name] as BehaviorTestKitImpl? ?: throw IllegalArgumentException("$ref is not a child of $this") - } - - override fun childTestKit(ref: ActorRef): BehaviorTestKit { - val btk = childTestKit(ref.path.name) - - if (btk.ref != ref) { - throw IllegalArgumentException("$ref is not a child of $this") - } - - return btk - } -} diff --git a/odcsim-testkit/src/main/kotlin/com/atlarge/odcsim/testkit/internal/TestInboxImpl.kt b/odcsim-testkit/src/main/kotlin/com/atlarge/odcsim/testkit/internal/TestInboxImpl.kt deleted file mode 100644 index 35abd758..00000000 --- a/odcsim-testkit/src/main/kotlin/com/atlarge/odcsim/testkit/internal/TestInboxImpl.kt +++ /dev/null @@ -1,100 +0,0 @@ -/* - * MIT License - * - * Copyright (c) 2019 atlarge-research - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -package com.atlarge.odcsim.testkit.internal - -import com.atlarge.odcsim.ActorPath -import com.atlarge.odcsim.ActorRef -import com.atlarge.odcsim.Envelope -import com.atlarge.odcsim.Instant -import com.atlarge.odcsim.testkit.TestInbox -import org.junit.jupiter.api.Assertions.assertEquals -import java.util.PriorityQueue - -/** - * A helper class for testing messages sent to an [ActorRef]. - * - * @param owner The owner of the inbox. - * @param path The path to the test inbox. - * @param T The shape of the messages the inbox accepts. - */ -internal class TestInboxImpl(private val owner: BehaviorTestKitImpl<*>, path: ActorPath) : TestInbox { - /** - * The queue of received messages. - */ - private val inbox = PriorityQueue>() - - /** - * The identifier for the next message to be scheduled. - */ - private var nextId: Long = 0 - - override val ref: ActorRef = ActorRefImpl(path) - - override val hasMessages: Boolean - get() = inbox.isNotEmpty() - - override fun receiveEnvelope(): Envelope = inbox.remove() - - override fun receiveAll(): List> = inbox.toList().also { inbox.clear() } - - override fun clear() = inbox.clear() - - override fun expectMessage(expected: T) = assertEquals(expected, receiveMessage()) - - override fun expectMessage(expected: T, message: String) = assertEquals(expected, receiveMessage(), message) - - internal inner class ActorRefImpl(override val path: ActorPath) : ActorRef { - /** - * Send the specified message to the actor this reference is pointing to after the specified delay. - * - * @param msg The message to send. - * @param after The delay before the message is received. - */ - fun send(msg: T, after: Instant) { - inbox.add(EnvelopeImpl(nextId++, owner.time + after, msg)) - } - } - - /** - * A wrapper around a message that has been scheduled for processing. - * - * @property id The identifier of the message to keep the priority queue stable. - * @property time The point in time to deliver the message. - * @property message The message to wrap. - */ - private inner class EnvelopeImpl( - val id: Long, - override val time: Instant, - override val message: T - ) : Envelope { - override fun compareTo(other: Envelope<*>): Int { - val cmp = super.compareTo(other) - return if (cmp == 0 && other is EnvelopeImpl) - id.compareTo(other.id) - else - cmp - } - } -} diff --git a/odcsim/README.md b/odcsim/README.md new file mode 100644 index 00000000..00d6a2fe --- /dev/null +++ b/odcsim/README.md @@ -0,0 +1,100 @@ +

+ + OpenDC + +
+ odcsim +

+ +## Introduction +**odcsim** is a framework for discrete event simulation in Kotlin and Java, used +by the [OpenDC](https://opendc.org) project. +Simulations are defined in terms of a hierarchical grouping of actors +and the interactions between these actors +([Actor model](https://en.wikipedia.org/wiki/Actor_model)), using +an API very similar to [Akka Typed](https://doc.akka.io/docs/akka/current/typed/index.html). + +## Documentation +Check out the [Getting Started](#getting-started) section for a quick +overview. +The documentation is located in the [docs/](docs/) directory and is divided as follows: +* [Main Concepts](docs/concepts.md) +* [Building a Model](docs/build.md) +* [Running a Model](docs/run.md) +* [Pre-built Models](docs/models.md) +* [API Reference](https://atlarge-research.github.io/opendc-simulator) +* [Contributing Guide](CONTRIBUTING.md) + +## Getting Started + +### Installation +Please add the required packages as dependency in your project. +Releases are available in the [Maven Central Repository](https://search.maven.org/). + +The package `odcsim-core` is required to construct a simulation model. +A `odcsim-engine-*` package is needed for running the simulation +model. + +**Gradle** +```groovy +compile 'com.atlarge.odcsim:odcsim-core:2.0.0' +compile 'com.atlarge.odcsim:odcsim-engine-omega:2.0.0' +``` + +**Maven** +```xml + + com.atlarge.odcsim + odcsim-core + 2.0.0 + + + + com.atlarge.odcsim + odcsim-engine-omega + 2.0.0 + +``` + +### Construction of Simulation Model +Let's construct a simple simulation model of a single car actor. +The car will alternately drive and park for a while. When it starts +driving (or parking), it will print the current simulation time. + + +```kotlin +import com.atlarge.odcsim.Behavior +import com.atlarge.odcsim.coroutines.suspending +import com.atlarge.odcsim.coroutines.dsl.timeout + +fun car(): Behavior = + suspending { ctx -> + while (true) { + println("Start parking at ${ctx.time}") + val parkingDuration = 5.0 + timeout(parkingDuration) + + println("Start driving at ${ctx.time}") + val tripDuration = 2.0 + timeout(tripDuration) + } + + stopped() + } +``` + +### Running Simulation +Running the constructed simulation model requires an implementation +of the `ActorSystem` interface provided by one of the `odcsim-engine-*` +packages. The [ServiceLoader](https://docs.oracle.com/javase/9/docs/api/java/util/ServiceLoader.html) +class found in the JDK can be used to locate the `ActorSystem` implementation on the classpath. +```kotlin +import com.atlarge.odcsim.ActorSystemFactory +import java.util.ServiceLoader + +val factory = ServiceLoader.load(ActorSystemFactory::class.java).first() +val system = factory(car(), name = "car") +system.run(until = 10.0) +system.terminate() +``` + diff --git a/odcsim/build.gradle.kts b/odcsim/build.gradle.kts new file mode 100644 index 00000000..cc3f3add --- /dev/null +++ b/odcsim/build.gradle.kts @@ -0,0 +1,23 @@ +/* + * MIT License + * + * Copyright (c) 2019 atlarge-research + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ diff --git a/odcsim/odcsim-core/build.gradle.kts b/odcsim/odcsim-core/build.gradle.kts new file mode 100644 index 00000000..013d1598 --- /dev/null +++ b/odcsim/odcsim-core/build.gradle.kts @@ -0,0 +1,38 @@ +/* + * MIT License + * + * Copyright (c) 2017 atlarge-research + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +/* Build configuration */ +plugins { + `kotlin-library-convention` +} + +dependencies { + implementation(kotlin("stdlib")) + api("org.slf4j:slf4j-api:${Library.SLF4J}") + + testImplementation("org.junit.jupiter:junit-jupiter-api:${Library.JUNIT_JUPITER}") + testRuntimeOnly("org.junit.jupiter:junit-jupiter-engine:${Library.JUNIT_JUPITER}") + testImplementation("org.junit.platform:junit-platform-launcher:${Library.JUNIT_PLATFORM}") + testImplementation("com.nhaarman.mockitokotlin2:mockito-kotlin:2.0.0") +} diff --git a/odcsim/odcsim-core/src/main/kotlin/com/atlarge/odcsim/ActorContext.kt b/odcsim/odcsim-core/src/main/kotlin/com/atlarge/odcsim/ActorContext.kt new file mode 100644 index 00000000..dc6ca7ec --- /dev/null +++ b/odcsim/odcsim-core/src/main/kotlin/com/atlarge/odcsim/ActorContext.kt @@ -0,0 +1,175 @@ +/* + * MIT License + * + * Copyright (c) 2018 atlarge-research + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package com.atlarge.odcsim + +import org.slf4j.Logger + +/** + * Represents the context in which the execution of an actor's behavior takes place. + * + * @param T The shape of the messages the actor accepts. + */ +interface ActorContext { + /** + * The identity of the actor, bound to the lifecycle of this actor instance. + */ + val self: ActorRef + + /** + * A view of the children of this actor. + */ + val children: List> + + /** + * The point of time within the simulation. + */ + val time: Instant + + /** + * The [ActorSystem] the actor is part of. + */ + val system: ActorSystem<*> + + /** + * An actor specific logger instance. + */ + val log: Logger + + /** + * Obtain the child of this actor with the specified name. + * + * @param name The name of the child actor to obtain. + * @return The reference to the child actor or `null` if it does not exist. + */ + fun getChild(name: String): ActorRef<*>? + + /** + * Send the specified message to the actor referenced by this [ActorRef]. + * + * Please note that callees must guarantee that messages are sent strictly in increasing time. + * If so, this method guarantees that: + * - A message will never be received earlier than specified + * - A message might arrive later than specified if the two actors are not synchronized. + * + * @param ref The actor to send the message to. + * @param msg The message to send to the referenced actor. + * @param after The delay after which the message should be received by the actor. + */ + fun send(ref: ActorRef, msg: U, after: Duration = 0.0) + + /** + * Spawn a child actor from the given [Behavior] and with the specified name. + * + * The name may not be empty or start with "$". Moreover, the name of an actor must be unique and this method + * will throw an [IllegalArgumentException] in case a child actor of the given name already exists. + * + * @param behavior The behavior of the child actor to spawn. + * @param name The name of the child actor to spawn. + * @return A reference to the child that has/will be spawned. + */ + fun spawn(behavior: Behavior, name: String): ActorRef + + /** + * Spawn an anonymous child actor from the given [Behavior]. + * + * @param behavior The behavior of the child actor to spawn. + * @return A reference to the child that has/will be spawned. + */ + fun spawnAnonymous(behavior: Behavior): ActorRef + + /** + * Force the specified child actor to terminate after it finishes processing its current message. + * Nothing will happen if the child is already stopped. + * + * Only direct children of an actor may be stopped through the actor context. Trying to stop other actors via this + * method will result in an [IllegalArgumentException]. Instead, stopping other actors has to be expressed as + * an explicit stop message that the actor accept. + * + * @param child The reference to the child actor to stop. + */ + fun stop(child: ActorRef<*>) + + /** + * Watch the specified [ActorRef] for termination of the referenced actor. On termination of the watched actor, + * a [Terminated] signal is sent to this actor. + * + * @param target The target actor to watch. + */ + fun watch(target: ActorRef<*>) + + /** + * Revoke the registration established by [watch]. + * + * In case there exists no registration for the specified [target], no action will be performed. + * + * @param target The target actor to unwatch. + */ + fun unwatch(target: ActorRef<*>) + + /** + * Synchronize the local virtual time of this target with the other referenced actor's local virtual time. + * + * By default, actors are not guaranteed to be synchronized, meaning that for some implementations, virtual time may + * drift between different actors. Synchronization between two actors ensures that virtual time remains consistent + * between at least the two actors. + * + * Be aware that this method may cause a jump in virtual time in order to get consistent with [target]. + * Furthermore, please note that synchronization might incur performance degradation and should only be used + * when necessary. + * + * @param target The reference to the target actor to synchronize with. + */ + fun sync(target: ActorRef<*>) + + /** + * Desynchronize virtual time between two actors if possible. + * + * Please note that this method only provides a hint to the [ActorSystem] that it may drop synchronization between + * the actors, but [ActorSystem] is not compelled to actually do so (i.e. in the case where synchronization is + * always guaranteed). + * + * Furthermore, if [target] is already desychronized, the method should return without error. [ActorContext.isSync] + * may be used to determine if an actor is synchronized. + * + * @param target The reference to the target actor to desynchronize with. + */ + fun unsync(target: ActorRef<*>) + + /** + * Determine whether this actor and [target] are synchronized in virtual time. + * + * @param target The target to check for synchronization. + * @return `true` if [target] is synchronized with this actor, `false` otherwise. + */ + fun isSync(target: ActorRef<*>): Boolean +} + +/** + * Unsafe helper method for widening the type accepted by this [ActorContext]. + */ +fun ActorContext.unsafeCast(): ActorContext { + @Suppress("UNCHECKED_CAST") + return this as ActorContext +} diff --git a/odcsim/odcsim-core/src/main/kotlin/com/atlarge/odcsim/ActorPath.kt b/odcsim/odcsim-core/src/main/kotlin/com/atlarge/odcsim/ActorPath.kt new file mode 100644 index 00000000..a6c716a2 --- /dev/null +++ b/odcsim/odcsim-core/src/main/kotlin/com/atlarge/odcsim/ActorPath.kt @@ -0,0 +1,142 @@ +/* + * MIT License + * + * Copyright (c) 2018 atlarge-research + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package com.atlarge.odcsim + +import java.io.Serializable + +/** + * An actor path represents the unique path to a specific actor instance within an [ActorSystem]. + */ +sealed class ActorPath : Comparable, Serializable { + /** + * The name of the actor that this path refers to. + */ + abstract val name: String + + /** + * The path for the parent actor. + */ + abstract val parent: ActorPath + + /** + * Walk up the tree to obtain and return the [ActorPath.Root]. + */ + abstract val root: Root + + /** + * Create a new child actor path. + */ + fun child(name: String): ActorPath = Child(this, name) + + /** + * Create a new child actor path. + */ + operator fun div(name: String): ActorPath = child(name) + + /** + * Recursively create a descendant’s path by appending all child names. + */ + fun descendant(children: Iterable): ActorPath = children.fold(this) { parent, name -> + if (name.isNotBlank()) child(name) else parent + } + + /** + * Root of the hierarchy of [ActorPath]s. There is exactly root per [ActorSystem]. + */ + data class Root(override val name: String = "/") : ActorPath() { + init { + require(name.length == 1 || name.indexOf('/', 1) == -1) { + "/ may only exist at the beginning of the root actors name" + } + require(name.indexOf('#') == -1) { "# may not exist in a path component" } + } + + override val parent: ActorPath = this + + override val root: Root = this + + /** + * Compare the [specified][other] path with this root node for order. If both paths are roots, compare their + * name, otherwise the root is ordered higher. + * + * @return a negative integer, zero, or a positive integer as this object is less than, equal to, or greater + * than the specified path. + */ + override fun compareTo(other: ActorPath): Int = if (other is Root) name.compareTo(other.name) else 1 + + /** + * Create a string representation of this root node which prints its own [name]. + * + * @return A string representation of this node. + */ + override fun toString(): String = name + } + + /** + * A child in the hierarchy of [ActorPath]s. + */ + data class Child(override val parent: ActorPath, override val name: String) : ActorPath() { + init { + require(name.indexOf('/') == -1) { "/ may not exist in a path component" } + require(name.indexOf('#') == -1) { "# may not exist in a path component" } + } + + override val root: Root by lazy { + when (parent) { + is Root -> parent + else -> parent.root + } + } + + /** + * Compare the [specified][other] path with this child node for order. + * + * @return a negative integer, zero, or a positive integer as this object is less than, equal to, or greater + * than the specified path. + */ + override fun compareTo(other: ActorPath): Int { + tailrec fun rec(left: ActorPath, right: ActorPath): Int = when { + left == right -> 0 + left is Root -> left.compareTo(right) + right is Root -> -(right.compareTo(left)) + else -> { + val x = left.name.compareTo(right.name) + if (x == 0) + rec(left.parent, right.parent) + else + x + } + } + return rec(this, other) + } + + /** + * Create a string representation of this child node which prints the name of [parent] and its own [name]. + * + * @return A string representation of this node. + */ + override fun toString(): String = "$parent/$name" + } +} diff --git a/odcsim/odcsim-core/src/main/kotlin/com/atlarge/odcsim/ActorRef.kt b/odcsim/odcsim-core/src/main/kotlin/com/atlarge/odcsim/ActorRef.kt new file mode 100644 index 00000000..45fc756e --- /dev/null +++ b/odcsim/odcsim-core/src/main/kotlin/com/atlarge/odcsim/ActorRef.kt @@ -0,0 +1,50 @@ +/* + * MIT License + * + * Copyright (c) 2018 atlarge-research + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package com.atlarge.odcsim + +import java.io.Serializable + +/** + * A reference to an entity in simulation that accepts messages of type [T]. + */ +interface ActorRef : Comparable>, Serializable { + /** + * The path for this actor (from this actor up to the root actor). + */ + val path: ActorPath + + /** + * Compare this reference to another actor reference. + */ + override fun compareTo(other: ActorRef<*>): Int = path.compareTo(other.path) +} + +/** + * Unsafe helper method for widening the type accepted by this [ActorRef]. + */ +fun ActorRef.unsafeCast(): ActorRef { + @Suppress("UNCHECKED_CAST") + return this as ActorRef +} diff --git a/odcsim/odcsim-core/src/main/kotlin/com/atlarge/odcsim/ActorSystem.kt b/odcsim/odcsim-core/src/main/kotlin/com/atlarge/odcsim/ActorSystem.kt new file mode 100644 index 00000000..d65beebd --- /dev/null +++ b/odcsim/odcsim-core/src/main/kotlin/com/atlarge/odcsim/ActorSystem.kt @@ -0,0 +1,76 @@ +/* + * MIT License + * + * Copyright (c) 2018 atlarge-research + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package com.atlarge.odcsim + +/** + * An actor system is a hierarchical grouping of actors that represents a discrete event simulation. + * + * An implementation of this interface should be provided by an engine. See for example *odcsim-engine-omega*, + * which is the reference implementation of the *odcsim* API. + * + * @param T The shape of the messages the root actor in the system can receive. + */ +interface ActorSystem : ActorRef { + /** + * The current point in simulation time. + */ + val time: Instant + + /** + * The name of this engine instance, used to distinguish between multiple engines running within the same JVM. + */ + val name: String + + /** + * Run the actors until the specified point in simulation time. + * + * @param until The point until which the simulation should run. + */ + fun run(until: Duration = Duration.POSITIVE_INFINITY) + + /** + * Send the specified message to the root actor of this [ActorSystem]. + * + * @param msg The message to send to the referenced actor. + * @param after The delay after which the message should be received by the actor. + */ + fun send(msg: T, after: Duration = 0.1) + + /** + * Terminates this actor system in an asynchronous fashion. + * + * This will stop the root actor and in turn will recursively stop all its child actors. + */ + fun terminate() + + /** + * Create an actor in the "/system" namespace. This actor will be shut down during `system.terminate()` only after + * all user actors have terminated. + * + * @param behavior The behavior of the system actor to spawn. + * @param name The name of the system actor to spawn. + */ + suspend fun spawnSystem(behavior: Behavior, name: String): ActorRef +} diff --git a/odcsim/odcsim-core/src/main/kotlin/com/atlarge/odcsim/ActorSystemFactory.kt b/odcsim/odcsim-core/src/main/kotlin/com/atlarge/odcsim/ActorSystemFactory.kt new file mode 100644 index 00000000..f59bc966 --- /dev/null +++ b/odcsim/odcsim-core/src/main/kotlin/com/atlarge/odcsim/ActorSystemFactory.kt @@ -0,0 +1,38 @@ +/* + * MIT License + * + * Copyright (c) 2018 atlarge-research + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package com.atlarge.odcsim + +/** + * A factory for [ActorSystem] instances that allows users to dynamically load engine implementations. + */ +interface ActorSystemFactory { + /** + * Create an [ActorSystem] with the given root [Behavior] and the given name. + * + * @param root The behavior of the root actor. + * @param name The name of the engine instance. + */ + operator fun invoke(root: Behavior, name: String): ActorSystem +} diff --git a/odcsim/odcsim-core/src/main/kotlin/com/atlarge/odcsim/Behavior.kt b/odcsim/odcsim-core/src/main/kotlin/com/atlarge/odcsim/Behavior.kt new file mode 100644 index 00000000..9ad7f83f --- /dev/null +++ b/odcsim/odcsim-core/src/main/kotlin/com/atlarge/odcsim/Behavior.kt @@ -0,0 +1,193 @@ +/* + * MIT License + * + * Copyright (c) 2018 atlarge-research + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package com.atlarge.odcsim + +/** + * The representation of the behavior of an actor. + * + * Behavior can be formulated using factory methods on the companion object or by extending either [DeferredBehavior] or + * [ReceivingBehavior]. + * + * Users are advised not to close over [ActorContext] within [Behavior], as it will causes it to become immobile, + * meaning it cannot be moved to another context and executed there, and therefore it cannot be replicated or forked + * either. + * + * @param T The shape of the messages the behavior accepts. + */ +sealed class Behavior { + /** + * Narrow the type of this behavior. + * + * This is almost always a safe operation, but might cause [ClassCastException] in case a narrowed behavior sends + * messages of a different type to itself and is chained via [Behavior.orElse]. + */ + fun narrow(): Behavior = unsafeCast() + + /** + * Widen the type of this behavior by placing a funnel in front of it. + * + * @param transform The mapping from the widened type to the original type, returning `null` in-case the message + * should not be handled. + */ + fun widen(transform: (U) -> T?): Behavior { + return wrap(this) { interpreter -> + receive { ctx, msg -> + val res = transform(msg) + @Suppress("UNCHECKED_CAST") + if (res == null || interpreter.interpretMessage(ctx as ActorContext, res)) + unhandled() + else + interpreter.behavior.unsafeCast() + }.unsafeCast() + }.unsafeCast() + } + + /** + * Compose this [Behavior] with a fallback [Behavior] which is used in case this [Behavior] does not handle the + * incoming message or signal. + * + * @param that The fallback behavior. + */ + fun orElse(that: Behavior): Behavior = + wrap(this) { left -> + wrap(that) { right -> + object : ReceivingBehavior() { + override fun receive(ctx: ActorContext, msg: T): Behavior { + if (left.interpretMessage(ctx, msg)) { + return left.behavior + } else if (right.interpretMessage(ctx, msg)) { + return right.behavior + } + + return unhandled() + } + + override fun receiveSignal(ctx: ActorContext, signal: Signal): Behavior { + if (left.interpretSignal(ctx, signal)) { + return left.behavior + } else if (right.interpretSignal(ctx, signal)) { + return right.behavior + } + + return unhandled() + } + } + } + } + + /** + * Unsafe utility method for changing the type accepted by this [Behavior]. + * Be aware that changing the type might result in [ClassCastException], when sending a message to the resulting + * behavior. + */ + fun unsafeCast(): Behavior { + @Suppress("UNCHECKED_CAST") + return this as Behavior + } +} + +/** + * A [Behavior] that defers the construction of the actual [Behavior] until the actor is started in some [ActorContext]. + * If the actor is already started, it will immediately evaluate. + * + * @param T The shape of the messages the behavior accepts. + */ +abstract class DeferredBehavior : Behavior() { + /** + * Create a [Behavior] instance in the [specified][ctx] [ActorContext]. + * + * @param ctx The [ActorContext] in which the behavior runs. + * @return The actor's next behavior. + */ + abstract operator fun invoke(ctx: ActorContext): Behavior +} + +/** + * A [Behavior] that concretely defines how an actor will react to the messages and signals it receives. + * The message may either be of the type that the actor declares and which is part of the [ActorRef] signature, + * or it may be a system [Signal] that expresses a lifecycle event of either this actor or one of its child actors. + * + * @param T The shape of the messages the behavior accepts. + */ +abstract class ReceivingBehavior : Behavior() { + /** + * Process an incoming message of type [T] and return the actor's next behavior. + * + * The returned behavior can in addition to normal behaviors be one of the canned special objects: + * - returning [stopped] will terminate this Behavior + * - returning [same] designates to reuse the current Behavior + * - returning [unhandled] keeps the same Behavior and signals that the message was not yet handled + * + * @param ctx The [ActorContext] in which the actor is currently running. + * @param msg The message that was received. + */ + open fun receive(ctx: ActorContext, msg: T): Behavior = unhandled() + + /** + * Process an incoming [Signal] and return the actor's next behavior. + * + * The returned behavior can in addition to normal behaviors be one of the canned special objects: + * - returning [stopped] will terminate this Behavior + * - returning [same] designates to reuse the current Behavior + * - returning [unhandled] keeps the same Behavior and signals that the message was not yet handled + * + * @param ctx The [ActorContext] in which the actor is currently running. + * @param signal The [Signal] that was received. + */ + open fun receiveSignal(ctx: ActorContext, signal: Signal): Behavior = unhandled() +} + +/** + * A flag to indicate whether a [Behavior] instance is still alive. + */ +val Behavior.isAlive get() = this !is StoppedBehavior + +/** + * A flag to indicate whether the last message/signal went unhandled. + */ +val Behavior.isUnhandled get() = this is UnhandledBehavior + +// The special behaviors are kept in this file as to be able to seal the Behavior class to prevent users from extending +// it. +/** + * A special [Behavior] instance that signals that the actor has stopped. + */ +internal object StoppedBehavior : Behavior() { + override fun toString() = "Stopped" +} + +/** + * A special [Behavior] object to signal that the actor wants to reuse its previous behavior. + */ +internal object SameBehavior : Behavior() { + override fun toString() = "Same" +} + +/** + * A special [Behavior] object that indicates that the last message or signal was not handled. + */ +internal object UnhandledBehavior : Behavior() { + override fun toString() = "Unhandled" +} diff --git a/odcsim/odcsim-core/src/main/kotlin/com/atlarge/odcsim/Behaviors.kt b/odcsim/odcsim-core/src/main/kotlin/com/atlarge/odcsim/Behaviors.kt new file mode 100644 index 00000000..eac254ec --- /dev/null +++ b/odcsim/odcsim-core/src/main/kotlin/com/atlarge/odcsim/Behaviors.kt @@ -0,0 +1,223 @@ +/* + * MIT License + * + * Copyright (c) 2019 atlarge-research + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +@file:JvmName("Behaviors") +package com.atlarge.odcsim + +import com.atlarge.odcsim.internal.BehaviorInterpreter +import com.atlarge.odcsim.internal.EmptyBehavior +import com.atlarge.odcsim.internal.IgnoreBehavior +import com.atlarge.odcsim.internal.TimerSchedulerImpl +import com.atlarge.odcsim.internal.sendSignal + +/** + * This [Behavior] is used to signal that this actor shall terminate voluntarily. If this actor has created child actors + * then these will be stopped as part of the shutdown procedure. + */ +fun stopped(): Behavior = StoppedBehavior.unsafeCast() + +/** + * This [Behavior] is used to signal that this actor wants to reuse its previous behavior. + */ +fun same(): Behavior = SameBehavior.unsafeCast() + +/** + * This [Behavior] is used to signal to the system that the last message or signal went unhandled. This will + * reuse the previous behavior. + */ +fun unhandled(): Behavior = UnhandledBehavior.unsafeCast() + +/** + * A factory for [Behavior]. Creation of the behavior instance is deferred until the actor is started. + */ +fun setup(block: (ActorContext) -> Behavior): Behavior { + return object : DeferredBehavior() { + override fun invoke(ctx: ActorContext): Behavior = block(ctx) + } +} + +/** + * A [Behavior] that ignores any incoming message or signal and keeps the same behavior. + */ +fun ignore(): Behavior = IgnoreBehavior.narrow() + +/** + * A [Behavior] that treats every incoming message or signal as unhandled. + */ +fun empty(): Behavior = EmptyBehavior.narrow() + +/** + * Construct a [Behavior] that reacts to incoming messages, provides access to the [ActorContext] and returns the + * actor's next behavior. + */ +fun receive(handler: (ActorContext, T) -> Behavior): Behavior { + return object : ReceivingBehavior() { + override fun receive(ctx: ActorContext, msg: T): Behavior = handler(ctx, msg) + } +} + +/** + * Construct a [Behavior] that reacts to incoming messages of type [U], provides access to the [ActorContext] and + * returns the actor's next behavior. Other messages will be unhandled. + */ +inline fun receiveOf(crossinline handler: (ActorContext, U) -> Behavior): Behavior { + return object : ReceivingBehavior() { + override fun receive(ctx: ActorContext, msg: T): Behavior { + return if (msg is U) + handler(ctx, msg) + else + unhandled() + } + } +} + +/** + * Construct a [Behavior] that reacts to incoming messages and returns the actor's next behavior. + */ +fun receiveMessage(handler: (T) -> Behavior): Behavior { + return object : ReceivingBehavior() { + override fun receive(ctx: ActorContext, msg: T): Behavior = handler(msg) + } +} + +/** + * Construct a [Behavior] that reacts to incoming signals, provides access to the [ActorContext] and returns the + * actor's next behavior. + */ +fun receiveSignal(handler: (ActorContext, Signal) -> Behavior): Behavior { + return object : ReceivingBehavior() { + override fun receiveSignal(ctx: ActorContext, signal: Signal): Behavior = handler(ctx, signal) + } +} + +/** + * Construct a [Behavior] that wraps another behavior instance and uses a [BehaviorInterpreter] to pass incoming + * messages and signals to the wrapper behavior. + */ +fun wrap(behavior: Behavior, wrap: (BehaviorInterpreter) -> Behavior): Behavior { + return setup { ctx -> wrap(BehaviorInterpreter(behavior, ctx)) } +} + +/** + * Obtain a [TimerScheduler] for building a [Behavior] instance. + */ +fun withTimers(handler: (TimerScheduler) -> Behavior): Behavior { + return setup { ctx -> + val scheduler = TimerSchedulerImpl(ctx) + receiveSignal { _, signal -> + if (signal is TimerSchedulerImpl.TimerSignal) { + val res = scheduler.interceptTimerSignal(signal) + if (res != null) { + ctx.send(ctx.self, res) + return@receiveSignal same() + } + } + unhandled() + }.join(handler(scheduler)) + } +} + +/** + * Construct a [Behavior] that waits for the specified duration before constructing the next behavior. + * + * @param after The delay before constructing the next behavior. + * @param handler The handler to construct the behavior with. + */ +fun withTimeout(after: Duration, handler: (ActorContext) -> Behavior): Behavior = + setup { ctx -> + val target = Any() + ctx.sendSignal(ctx.self, Timeout(target), after) + receiveSignal { _, signal -> + if (signal is Timeout && signal.target == target) { + handler(ctx) + } else { + unhandled() + } + } + } + +/** + * Join together both [Behavior] with another [Behavior], essentially running them side-by-side, only directly + * propagating stopped behavior. + * + * @param that The behavior to join with. + */ +fun Behavior.join(that: Behavior): Behavior = + wrap(this) { left -> + wrap(that) { right -> + object : ReceivingBehavior() { + override fun receive(ctx: ActorContext, msg: T): Behavior { + if (left.interpretMessage(ctx, msg)) { + return left.propagate(this) // Propagate stopped behavior + } else if (right.interpretMessage(ctx, msg)) { + return right.propagate(this) + } + + return unhandled() + } + + override fun receiveSignal(ctx: ActorContext, signal: Signal): Behavior { + if (left.interpretSignal(ctx, signal)) { + return left.propagate(this) + } else if (right.interpretSignal(ctx, signal)) { + return right.propagate(this) + } + + return unhandled() + } + } + } + } + +/** + * Widen the type of messages the [Behavior] by marking all other messages as unhandled. + */ +inline fun Behavior.widen(): Behavior = widen { + if (it is T) + it + else + null +} + +/** + * Keep the specified [Behavior] alive if it returns the stopped behavior. + */ +fun Behavior.keepAlive(): Behavior = + wrap(this) { interpreter -> + object : ReceivingBehavior() { + override fun receive(ctx: ActorContext, msg: T): Behavior { + if (interpreter.interpretMessage(ctx, msg)) { + return this + } + return empty() + } + + override fun receiveSignal(ctx: ActorContext, signal: Signal): Behavior { + if (interpreter.interpretSignal(ctx, signal)) { + return this + } + + return empty() + } + } + } diff --git a/odcsim/odcsim-core/src/main/kotlin/com/atlarge/odcsim/Envelope.kt b/odcsim/odcsim-core/src/main/kotlin/com/atlarge/odcsim/Envelope.kt new file mode 100644 index 00000000..3b73d52d --- /dev/null +++ b/odcsim/odcsim-core/src/main/kotlin/com/atlarge/odcsim/Envelope.kt @@ -0,0 +1,57 @@ +/* + * MIT License + * + * Copyright (c) 2019 atlarge-research + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package com.atlarge.odcsim + +import java.io.Serializable + +/** + * A timestamped wrapper for messages that will be delivered to an actor. + */ +interface Envelope : Comparable>, Serializable { + /** + * The time at which this message should be delivered. + */ + val time: Instant + + /** + * The message contained in this envelope, of type [T] + */ + val message: T + + /** + * Extract the delivery time from the envelope. + */ + operator fun component1(): Instant = time + + /** + * Extract the message from this envelope. + */ + operator fun component2(): T = message + + /** + * Compare this envelope to the [other] envelope, ordered increasingly in time. + */ + override fun compareTo(other: Envelope<*>): Int = time.compareTo(other.time) +} diff --git a/odcsim/odcsim-core/src/main/kotlin/com/atlarge/odcsim/Signals.kt b/odcsim/odcsim-core/src/main/kotlin/com/atlarge/odcsim/Signals.kt new file mode 100644 index 00000000..9b707348 --- /dev/null +++ b/odcsim/odcsim-core/src/main/kotlin/com/atlarge/odcsim/Signals.kt @@ -0,0 +1,60 @@ +/* + * MIT License + * + * Copyright (c) 2018 atlarge-research + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package com.atlarge.odcsim + +/** + * System signals are notifications that are generated by the system and delivered to the actor behavior in a reliable + * fashion. + */ +interface Signal + +/** + * Lifecycle signal that is fired upon creation of the actor. This will be the first message that the actor receives. + */ +object PreStart : Signal + +/** + * Lifecycle signal that is fired after this actor and all its child actors (transitively) have terminated. + * The [Terminated] signal is only sent to registered watchers after this signal has been processed. + */ +object PostStop : Signal + +/** + * A lifecycle signal to indicate that an actor that was watched has terminated. + * + * @property ref The reference to the actor that has terminated. + * @property failure The failure that caused the termination, or `null` on graceful termination. + */ +data class Terminated(val ref: ActorRef<*>, val failure: Throwable? = null) : Signal + +/** + * A [Signal] to indicate an actor has timed out. + * + * This class contains a [target] property in order to allow nested behavior to function properly when multiple layers + * are waiting on this signal. + * + * @property target The target object that has timed out. + */ +data class Timeout(val target: Any) : Signal diff --git a/odcsim/odcsim-core/src/main/kotlin/com/atlarge/odcsim/StashBuffer.kt b/odcsim/odcsim-core/src/main/kotlin/com/atlarge/odcsim/StashBuffer.kt new file mode 100644 index 00000000..5d73d808 --- /dev/null +++ b/odcsim/odcsim-core/src/main/kotlin/com/atlarge/odcsim/StashBuffer.kt @@ -0,0 +1,88 @@ +/* + * MIT License + * + * Copyright (c) 2019 atlarge-research + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package com.atlarge.odcsim + +import com.atlarge.odcsim.internal.StashBufferImpl + +/** + * A non thread safe mutable message buffer that can be used to buffer messages inside actors and then unstash them. + * + * @param T The shape of the messages in this buffer. + */ +interface StashBuffer { + /** + * The first element of the buffer. + * + * @throws NoSuchElementException if the buffer is empty. + */ + val head: T + + /** + * A flag to indicate whether the buffer is empty. + */ + val isEmpty: Boolean + + /** + * A flag to indicate whether the buffer is full. + */ + val isFull: Boolean + + /** + * The number of elements in the stash buffer. + */ + val size: Int + + /** + * Iterate over all elements of the buffer and apply a function to each element, without removing them. + * + * @param block The function to invoke for each element. + */ + fun forEach(block: (T) -> Unit) + + /** + * Add one element to the end of the message buffer. + * + * @param msg The message to stash. + * @throws IllegalStateException if the element cannot be added at this time due to capacity restrictions + */ + fun stash(msg: T) + + /** + * Process all stashed messages with the behavior and the returned [Behavior] from each processed message. + * + * @param ctx The actor context to process these messages in. + * @param behavior The behavior to process the messages with. + */ + fun unstashAll(ctx: ActorContext, behavior: Behavior): Behavior + + companion object { + /** + * Construct a [StashBuffer] with the specified [capacity]. + * + * @param capacity The capacity of the buffer. + */ + operator fun invoke(capacity: Int): StashBuffer = StashBufferImpl(capacity) + } +} diff --git a/odcsim/odcsim-core/src/main/kotlin/com/atlarge/odcsim/Time.kt b/odcsim/odcsim-core/src/main/kotlin/com/atlarge/odcsim/Time.kt new file mode 100644 index 00000000..f19f6fe2 --- /dev/null +++ b/odcsim/odcsim-core/src/main/kotlin/com/atlarge/odcsim/Time.kt @@ -0,0 +1,75 @@ +/* + * MIT License + * + * Copyright (c) 2018 atlarge-research + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package com.atlarge.odcsim + +/** + * An instantaneous point on the time-line, used to record message time-stamps in a simulation. + */ +typealias Instant = Double + +/** + * A time interval which represents the amount of elapsed time between two messages. + */ +typealias Duration = Double + +/** + * Convert this [Int] into an [Instant]. + */ +fun Int.toInstant(): Instant = toDouble() + +/** + * Convert this [Int] into a [Duration]. + */ +fun Int.toDuration(): Duration = toDouble() + +/** + * Convert this [Long] into an [Instant]. + */ +fun Long.toInstant(): Instant = toDouble() + +/** + * Convert this [Long] into a [Duration]. + */ +fun Long.toDuration(): Duration = toDouble() + +/** + * Convert this [Float] into an [Instant]. + */ +fun Float.toInstant(): Instant = toDouble() + +/** + * Convert this [Float] into a [Duration]. + */ +fun Float.toDuration(): Duration = toDouble() + +/** + * Convert this [Double] into an [Instant]. + */ +fun Double.toInstant(): Instant = toDouble() + +/** + * Convert this [Double] into a [Duration]. + */ +fun Double.toDuration(): Duration = toDouble() diff --git a/odcsim/odcsim-core/src/main/kotlin/com/atlarge/odcsim/TimerScheduler.kt b/odcsim/odcsim-core/src/main/kotlin/com/atlarge/odcsim/TimerScheduler.kt new file mode 100644 index 00000000..c5c54b64 --- /dev/null +++ b/odcsim/odcsim-core/src/main/kotlin/com/atlarge/odcsim/TimerScheduler.kt @@ -0,0 +1,92 @@ +/* + * MIT License + * + * Copyright (c) 2019 atlarge-research + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package com.atlarge.odcsim + +/** + * An interface to provide support for scheduled self messages in an actor. It is used with [withTimers]. + * Timers are bound to the lifecycle of the actor that owns it, and thus are cancelled automatically when it is + * restarted or stopped. + * + * Please be aware that [TimerScheduler] is not thread-safe and must only be used within the actor that owns it. + * + * @param T The shape of the messages the owning actor of this scheduling accepts. + */ +interface TimerScheduler { + /** + * Cancel a timer with the given key. + * + * @param key The key of the timer. + */ + fun cancel(key: Any) + + /** + * Cancel all timers. + */ + fun cancelAll() + + /** + * Check if a timer with a given [key] is active. + * + * @param key The key to check if it is active. + * @return `true` if a timer with the specified key is active, `false` otherwise. + */ + fun isTimerActive(key: Any): Boolean + + /** + * Start a periodic timer that will send [msg] to the `self` actor at a fixed [interval]. + * + * @param key The key of the timer. + * @param msg The message to send to the actor. + * @param interval The interval of simulation time after which it should be sent. + */ + fun startPeriodicTimer(key: Any, msg: T, interval: Duration) + + /** + * Start a timer that will send [msg] once to the `self` actor after the given [delay]. + * + * @param key The key of the timer. + * @param msg The message to send to the actor. + * @param delay The delay in simulation time after which it should be sent. + */ + fun startSingleTimer(key: Any, msg: T, delay: Duration) + + /** + * Run [block] periodically at a fixed [interval] + * + * @param key The key of the timer. + * @param interval The delay of simulation time after which the block should run. + * @param block The block to run. + */ + fun every(key: Any, interval: Duration, block: () -> Unit) + + /** + * Run [block] after the specified [delay]. + * + * @param key The key of the timer. + * @param delay The delay in simulation time after which the block should run. + * @param block The block to run. + */ + fun after(key: Any, delay: Duration, block: () -> Unit) +} diff --git a/odcsim/odcsim-core/src/main/kotlin/com/atlarge/odcsim/coroutines/Behavior.kt b/odcsim/odcsim-core/src/main/kotlin/com/atlarge/odcsim/coroutines/Behavior.kt new file mode 100644 index 00000000..eb26add1 --- /dev/null +++ b/odcsim/odcsim-core/src/main/kotlin/com/atlarge/odcsim/coroutines/Behavior.kt @@ -0,0 +1,116 @@ +/* + * MIT License + * + * Copyright (c) 2018 atlarge-research + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package com.atlarge.odcsim.coroutines + +import com.atlarge.odcsim.ActorContext +import com.atlarge.odcsim.Behavior +import com.atlarge.odcsim.DeferredBehavior +import com.atlarge.odcsim.Signal +import com.atlarge.odcsim.internal.SuspendingActorContextImpl +import com.atlarge.odcsim.internal.SuspendingBehaviorImpl +import kotlin.coroutines.Continuation +import kotlin.coroutines.CoroutineContext +import kotlin.coroutines.intrinsics.suspendCoroutineUninterceptedOrReturn +import kotlin.coroutines.suspendCoroutine + +/** + * A [Behavior] that allows method calls to suspend execution via Kotlin coroutines. + * + * @param T The shape of the messages the actor accepts. + */ +abstract class SuspendingBehavior : DeferredBehavior() { + /** + * Run the suspending logic of this behavior. + * + * @param ctx The [SuspendingActorContext] in which the behavior is executed. + * @return The next behavior for the actor. + */ + abstract suspend operator fun invoke(ctx: SuspendingActorContext): Behavior + + // Immediately transfer to implementation + override fun invoke(ctx: ActorContext): Behavior = SuspendingBehaviorImpl(ctx, this).start() +} + +/** + * An [ActorContext] that provides additional functionality for receiving messages and signals from + * the actor's mailbox. + * + * @param T The shape of the messages the actor accepts. + */ +interface SuspendingActorContext : ActorContext, CoroutineContext.Element { + /** + * Suspend execution of the active coroutine to wait for a message of type [T] to be received in the actor's + * mailbox. During suspension, incoming signals will be marked unhandled. + * + * @return The message of type [T] that has been received. + */ + suspend fun receive(): T + + /** + * Suspend execution of the active coroutine to wait for a [Signal] to be received in the actor's mailbox. + * During suspension, incoming messages will be marked unhandled. + * + * @return The [Signal] that has been received. + */ + suspend fun receiveSignal(): Signal + + /** + * A key to provide access to the untyped [SuspendingActorContext] via [CoroutineContext] for suspending methods + * running inside a [SuspendingBehavior]. + */ + companion object Key : CoroutineContext.Key> +} + +/** + * Obtains the current continuation instance inside suspend functions and suspends currently running coroutine. [block] + * should return a [Behavior] that will resume the continuation and return the next behavior which is supplied via the + * second argument of the block. + */ +suspend fun suspendWithBehavior(block: (Continuation, () -> Behavior) -> Behavior): U = + suspendCoroutine { cont -> + @Suppress("UNCHECKED_CAST") + val ctx = cont.context[SuspendingActorContext] as? SuspendingActorContextImpl + ?: throw UnsupportedOperationException("Coroutine does not run inside SuspendingBehavior") + ctx.become(block(cont) { ctx.behavior }) + } + +/** + * Obtain the current [SuspendingActorContext] instance for the active continuation. + */ +suspend fun actorContext(): SuspendingActorContext = + suspendCoroutineUninterceptedOrReturn { cont -> + @Suppress("UNCHECKED_CAST") + cont.context[SuspendingActorContext] as? SuspendingActorContext + ?: throw UnsupportedOperationException("Coroutine does not run inside SuspendingBehavior") + } + +/** + * Construct a [Behavior] that uses Kotlin coroutines functionality to handle incoming messages and signals. + */ +fun suspending(block: suspend (SuspendingActorContext) -> Behavior): Behavior { + return object : SuspendingBehavior() { + override suspend fun invoke(ctx: SuspendingActorContext) = block(ctx) + } +} diff --git a/odcsim/odcsim-core/src/main/kotlin/com/atlarge/odcsim/coroutines/dsl/Receive.kt b/odcsim/odcsim-core/src/main/kotlin/com/atlarge/odcsim/coroutines/dsl/Receive.kt new file mode 100644 index 00000000..e995c0e3 --- /dev/null +++ b/odcsim/odcsim-core/src/main/kotlin/com/atlarge/odcsim/coroutines/dsl/Receive.kt @@ -0,0 +1,66 @@ +/* + * MIT License + * + * Copyright (c) 2019 atlarge-research + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package com.atlarge.odcsim.coroutines.dsl + +import com.atlarge.odcsim.ActorRef +import com.atlarge.odcsim.Duration +import com.atlarge.odcsim.coroutines.SuspendingActorContext +import com.atlarge.odcsim.coroutines.suspendWithBehavior +import com.atlarge.odcsim.receiveMessage +import com.atlarge.odcsim.unhandled +import kotlin.coroutines.resume + +/** + * Receive only messages of type [U] and mark all other messages as unhandled. + * + * @return The received message. + */ +suspend inline fun SuspendingActorContext.receiveOf(): U = + suspendWithBehavior { cont, next -> + receiveMessage { msg -> + if (msg is U) { + cont.resume(msg) + next() + } else { + unhandled() + } + } + } + +/** + * Send the specified message to the given reference and wait for a reply. + * + * @param ref The actor to send the message to. + * @param after The delay after which the message should be received by the actor. + * @param transform The block to transform `self` to a message. + */ +suspend inline fun SuspendingActorContext.ask( + ref: ActorRef, + after: Duration = 0.0, + transform: (ActorRef) -> U +): V { + send(ref, transform(self), after) + return receiveOf() +} diff --git a/odcsim/odcsim-core/src/main/kotlin/com/atlarge/odcsim/coroutines/dsl/Timeout.kt b/odcsim/odcsim-core/src/main/kotlin/com/atlarge/odcsim/coroutines/dsl/Timeout.kt new file mode 100644 index 00000000..16b6f534 --- /dev/null +++ b/odcsim/odcsim-core/src/main/kotlin/com/atlarge/odcsim/coroutines/dsl/Timeout.kt @@ -0,0 +1,42 @@ +/* + * MIT License + * + * Copyright (c) 2018 atlarge-research + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package com.atlarge.odcsim.coroutines.dsl + +import com.atlarge.odcsim.Duration +import com.atlarge.odcsim.coroutines.suspendWithBehavior +import com.atlarge.odcsim.withTimeout +import kotlin.coroutines.resume + +/** + * Block execution for the specified duration. + * + * @param after The duration after which execution should continue. + */ +suspend fun timeout(after: Duration) = suspendWithBehavior { cont, next -> + withTimeout(after) { + cont.resume(Unit) + next() + } +} diff --git a/odcsim/odcsim-core/src/main/kotlin/com/atlarge/odcsim/internal/ActorContext.kt b/odcsim/odcsim-core/src/main/kotlin/com/atlarge/odcsim/internal/ActorContext.kt new file mode 100644 index 00000000..f1aba25e --- /dev/null +++ b/odcsim/odcsim-core/src/main/kotlin/com/atlarge/odcsim/internal/ActorContext.kt @@ -0,0 +1,43 @@ +/* + * MIT License + * + * Copyright (c) 2019 atlarge-research + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package com.atlarge.odcsim.internal + +import com.atlarge.odcsim.ActorContext +import com.atlarge.odcsim.ActorRef +import com.atlarge.odcsim.Duration +import com.atlarge.odcsim.Signal + +/** + * Send the specified [Signal] to the given actor reference after the specified duration. + * + * @param ref The actor to send the signal to. + * @param signal The signal to send to the referenced actor. + * @param after The delay after which the signal should be received by the actor. + */ +fun ActorContext<*>.sendSignal(ref: ActorRef<*>, signal: Signal, after: Duration = 0.0) { + // Signals are currently processed as regular messages + @Suppress("UNCHECKED_CAST") + send(ref as ActorRef, signal, after) +} diff --git a/odcsim/odcsim-core/src/main/kotlin/com/atlarge/odcsim/internal/Behavior.kt b/odcsim/odcsim-core/src/main/kotlin/com/atlarge/odcsim/internal/Behavior.kt new file mode 100644 index 00000000..b07cabc0 --- /dev/null +++ b/odcsim/odcsim-core/src/main/kotlin/com/atlarge/odcsim/internal/Behavior.kt @@ -0,0 +1,48 @@ +/* + * MIT License + * + * Copyright (c) 2018 atlarge-research + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package com.atlarge.odcsim.internal + +import com.atlarge.odcsim.ActorContext +import com.atlarge.odcsim.Behavior +import com.atlarge.odcsim.ReceivingBehavior +import com.atlarge.odcsim.Signal + +/** + * A [Behavior] object that ignores all messages sent to the actor. + */ +internal object IgnoreBehavior : ReceivingBehavior() { + override fun receive(ctx: ActorContext, msg: Any): Behavior = this + + override fun receiveSignal(ctx: ActorContext, signal: Signal): Behavior = this + + override fun toString() = "Ignore" +} + +/** + * A [Behavior] object that does not handle any message it receives. + */ +internal object EmptyBehavior : ReceivingBehavior() { + override fun toString() = "Empty" +} diff --git a/odcsim/odcsim-core/src/main/kotlin/com/atlarge/odcsim/internal/BehaviorInterpreter.kt b/odcsim/odcsim-core/src/main/kotlin/com/atlarge/odcsim/internal/BehaviorInterpreter.kt new file mode 100644 index 00000000..194c2a62 --- /dev/null +++ b/odcsim/odcsim-core/src/main/kotlin/com/atlarge/odcsim/internal/BehaviorInterpreter.kt @@ -0,0 +1,201 @@ +/* + * MIT License + * + * Copyright (c) 2018 atlarge-research + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package com.atlarge.odcsim.internal + +import com.atlarge.odcsim.ActorContext +import com.atlarge.odcsim.Behavior +import com.atlarge.odcsim.DeferredBehavior +import com.atlarge.odcsim.ReceivingBehavior +import com.atlarge.odcsim.SameBehavior +import com.atlarge.odcsim.Signal +import com.atlarge.odcsim.StoppedBehavior +import com.atlarge.odcsim.UnhandledBehavior +import com.atlarge.odcsim.isAlive +import com.atlarge.odcsim.isUnhandled + +/** + * Helper class that interprets messages/signals, canonicalizes special objects and manages the life-cycle of + * [Behavior] instances. + * + * @param initialBehavior The initial behavior to use. + */ +class BehaviorInterpreter(initialBehavior: Behavior) { + /** + * The current [Behavior] instance. + */ + var behavior: Behavior = initialBehavior + private set + + /** + * A flag to indicate the interpreter is still alive. + */ + val isAlive: Boolean get() = behavior.isAlive + + /** + * Construct a [BehaviorInterpreter] with the specified initial behavior and immediately start it in the specified + * context. + * + * @param initialBehavior The initial behavior of the actor. + * @param ctx The [ActorContext] to run the behavior in. + */ + constructor(initialBehavior: Behavior, ctx: ActorContext) : this(initialBehavior) { + start(ctx) + } + + /** + * Start the initial behavior. + * + * @param ctx The [ActorContext] to start the behavior in. + */ + fun start(ctx: ActorContext) { + behavior = validateAsInitial(start(ctx, behavior)) + } + + /** + * Stop the current active behavior and move into the stopped state. + * + * @param ctx The [ActorContext] this takes place in. + */ + fun stop(ctx: ActorContext) { + behavior = start(ctx, StoppedBehavior.narrow()) + } + + /** + * Replace the current behavior with the specified new behavior. + * + * @param ctx The [ActorContext] to run the behavior in. + * @param next The behavior to replace the current behavior with. + */ + fun become(ctx: ActorContext, next: Behavior) { + this.behavior = canonicalize(ctx, behavior, next) + } + + /** + * Propagate special states of the wrapper [Behavior] to the specified [Behavior]. This means + * that if the behavior of this interpreter is stopped or unhandled, this will be propagated. + * + * @param behavior The [Behavior] to map. + * @return Either the specified [Behavior] or the propagated special objects. + */ + fun propagate(behavior: Behavior): Behavior = + if (this.behavior.isUnhandled || !this.behavior.isAlive) + this.behavior + else + behavior + + /** + * Interpret the given message of type [T] using the current active behavior. + * + * @return `true` if the message was handled by the active behavior, `false` otherwise. + */ + fun interpretMessage(ctx: ActorContext, msg: T): Boolean = interpret(ctx, msg, false) + + /** + * Interpret the given [Signal] using the current active behavior. + * + * @return `true` if the signal was handled by the active behavior, `false` otherwise. + */ + fun interpretSignal(ctx: ActorContext, signal: Signal): Boolean = interpret(ctx, signal, true) + + /** + * Interpret the given message or signal using the current active behavior. + * + * @return `true` if the message or signal was handled by the active behavior, `false` otherwise. + */ + private fun interpret(ctx: ActorContext, msg: Any, isSignal: Boolean): Boolean = + if (isAlive) { + val next = when (val current = behavior) { + is DeferredBehavior -> + throw IllegalStateException("Deferred [$current] should not be passed to interpreter") + is ReceivingBehavior -> + if (isSignal) + current.receiveSignal(ctx, msg as Signal) + else + @Suppress("UNCHECKED_CAST") + current.receive(ctx, msg as T) + is SameBehavior, is UnhandledBehavior -> + throw IllegalStateException("Cannot execute with [$current] as behavior") + is StoppedBehavior -> current + } + + val unhandled = next.isUnhandled + behavior = canonicalize(ctx, behavior, next) + !unhandled + } else { + false + } + + /** + * Validate whether the given [Behavior] can be used as initial behavior. Throw an [IllegalArgumentException] if + * the [Behavior] is not valid. + * + * @param behavior The behavior to validate. + */ + private fun validateAsInitial(behavior: Behavior): Behavior = + when (behavior) { + is SameBehavior, is UnhandledBehavior -> + throw IllegalArgumentException("Cannot use [$behavior] as initial behavior") + else -> behavior + } + + /** + * Helper methods to properly manage the special, canned behavior objects. It highly recommended to use the + * [BehaviorInterpreter] instead to properly manage the life-cycles of the behavior objects. + */ + companion object { + /** + * Start the initial behavior of an actor in the specified [ActorContext]. + * + * This will activate the initial behavior and canonicalize the resulting behavior. + * + * @param ctx The [ActorContext] to start the behavior in. + * @param behavior The initial behavior to start. + * @return The behavior that has been started. + */ + tailrec fun start(ctx: ActorContext, behavior: Behavior): Behavior = + when (behavior) { + is DeferredBehavior -> start(ctx, behavior(ctx)) + else -> behavior + } + + /** + * Given a possibly special behavior (same or unhandled) and a "current" behavior (which defines the meaning of + * encountering a `same` behavior) this method computes the next behavior, suitable for passing a message or + * signal. + * + * @param ctx The context in which the actor runs. + * @param current The actor's current behavior. + * @param next The actor's next behavior. + * @return The actor's canonicalized next behavior. + */ + tailrec fun canonicalize(ctx: ActorContext, current: Behavior, next: Behavior): Behavior = + when (next) { + is SameBehavior, current -> current + is UnhandledBehavior -> current + is DeferredBehavior -> canonicalize(ctx, current, next(ctx)) + else -> next + } + } +} diff --git a/odcsim/odcsim-core/src/main/kotlin/com/atlarge/odcsim/internal/Coroutines.kt b/odcsim/odcsim-core/src/main/kotlin/com/atlarge/odcsim/internal/Coroutines.kt new file mode 100644 index 00000000..82b29715 --- /dev/null +++ b/odcsim/odcsim-core/src/main/kotlin/com/atlarge/odcsim/internal/Coroutines.kt @@ -0,0 +1,182 @@ +/* + * MIT License + * + * Copyright (c) 2018 atlarge-research + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package com.atlarge.odcsim.internal + +import com.atlarge.odcsim.ActorContext +import com.atlarge.odcsim.ActorRef +import com.atlarge.odcsim.ActorSystem +import com.atlarge.odcsim.Behavior +import com.atlarge.odcsim.Duration +import com.atlarge.odcsim.Instant +import com.atlarge.odcsim.ReceivingBehavior +import com.atlarge.odcsim.Signal +import com.atlarge.odcsim.coroutines.SuspendingActorContext +import com.atlarge.odcsim.coroutines.SuspendingBehavior +import com.atlarge.odcsim.coroutines.suspendWithBehavior +import com.atlarge.odcsim.empty +import com.atlarge.odcsim.receiveMessage +import com.atlarge.odcsim.receiveSignal +import org.slf4j.Logger +import kotlin.coroutines.Continuation +import kotlin.coroutines.CoroutineContext +import kotlin.coroutines.resume +import kotlin.coroutines.startCoroutine + +/** + * This interface exposes internal functionality provided by [SuspendingBehaviorImpl] on [SuspendingActorContext] to + * control the active behavior of the coroutine. + */ +interface SuspendingActorContextImpl : SuspendingActorContext { + /** + * The current active behavior + */ + val behavior: Behavior + + /** + * Replace the current active behavior with the specified new behavior. + * + * @param next The behavior to replace the current behavior with. + */ + fun become(next: Behavior) +} + +/** + * Implementation of [SuspendingBehavior] class that maps the suspending method calls to the [Behavior] + * interface. + * This implementation uses the fact that each actor is thread-safe (as it processes its mailbox sequentially). + */ +internal class SuspendingBehaviorImpl( + private var actorContext: ActorContext, + initialBehavior: SuspendingBehavior +) : ReceivingBehavior(), SuspendingActorContextImpl { + + /** + * The next behavior to use. + */ + private var next: Behavior = this + + /** + * The [BehaviorInterpreter] to wrap the suspending behavior. + */ + private val interpreter = BehaviorInterpreter(initialBehavior) + + override fun receive(ctx: ActorContext, msg: T): Behavior { + this.actorContext = ctx + return interpreter.also { it.interpretMessage(ctx, msg) }.propagate(next) + } + + override fun receiveSignal(ctx: ActorContext, signal: Signal): Behavior { + this.actorContext = ctx + return interpreter.also { it.interpretSignal(ctx, signal) }.propagate(next) + } + + override val self: ActorRef get() = actorContext.self + + override val time: Instant get() = actorContext.time + + override val children: List> + get() = actorContext.children + + override val system: ActorSystem<*> + get() = actorContext.system + + override val log: Logger + get() = actorContext.log + + override fun getChild(name: String): ActorRef<*>? = actorContext.getChild(name) + + override fun send(ref: ActorRef, msg: U, after: Duration) = actorContext.send(ref, msg, after) + + override fun spawn(behavior: Behavior, name: String) = actorContext.spawn(behavior, name) + + override fun spawnAnonymous(behavior: Behavior) = actorContext.spawnAnonymous(behavior) + + override fun stop(child: ActorRef<*>) = actorContext.stop(child) + + override fun watch(target: ActorRef<*>) = actorContext.watch(target) + + override fun unwatch(target: ActorRef<*>) = actorContext.unwatch(target) + + override fun sync(target: ActorRef<*>) = actorContext.sync(target) + + override fun unsync(target: ActorRef<*>) = actorContext.unsync(target) + + override fun isSync(target: ActorRef<*>): Boolean = actorContext.isSync(target) + + override suspend fun receive(): T = suspendWithBehavior { cont, next -> + receiveMessage { msg -> + cont.resume(msg) + next() + } + } + + override suspend fun receiveSignal(): Signal = suspendWithBehavior { cont, next -> + receiveSignal { _, signal -> + cont.resume(signal) + next() + } + } + + override val behavior: Behavior get() = interpreter.behavior + + override fun become(next: Behavior) { + interpreter.become(actorContext, next) + } + + override val key: CoroutineContext.Key<*> = SuspendingActorContext.Key + + /** + * Start the suspending behavior. + */ + internal fun start(): Behavior { + val behavior = interpreter.behavior as SuspendingBehavior + val block = suspend { behavior(this) } + interpreter.become(actorContext, empty()) + block.startCoroutine(SuspendingBehaviorImplContinuation()) + return next + } + + /** + * Stop the suspending behavior. + */ + private fun stop() { + this.interpreter.stop(actorContext) + } + + /** + * The continuation of suspending behavior. + */ + private inner class SuspendingBehaviorImplContinuation : Continuation> { + override val context = this@SuspendingBehaviorImpl + + override fun resumeWith(result: Result>) { + if (result.isSuccess) { + next = result.getOrNull()!! + } else if (result.isFailure) { + throw result.exceptionOrNull()!! + } + } + } +} diff --git a/odcsim/odcsim-core/src/main/kotlin/com/atlarge/odcsim/internal/StashBufferImpl.kt b/odcsim/odcsim-core/src/main/kotlin/com/atlarge/odcsim/internal/StashBufferImpl.kt new file mode 100644 index 00000000..24c3a9d5 --- /dev/null +++ b/odcsim/odcsim-core/src/main/kotlin/com/atlarge/odcsim/internal/StashBufferImpl.kt @@ -0,0 +1,74 @@ +/* + * MIT License + * + * Copyright (c) 2019 atlarge-research + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package com.atlarge.odcsim.internal + +import com.atlarge.odcsim.ActorContext +import com.atlarge.odcsim.Behavior +import com.atlarge.odcsim.StashBuffer +import java.util.ArrayDeque + +/** + * Internal implementation of the [StashBuffer] interface. + */ +internal class StashBufferImpl(private val capacity: Int) : StashBuffer { + /** + * The internal queue used to store the messages. + */ + private val queue = ArrayDeque(capacity) + + override val head: T + get() = queue.first + + override val isEmpty: Boolean + get() = queue.isEmpty() + + override val isFull: Boolean + get() = size > capacity + + override val size: Int + get() = queue.size + + override fun forEach(block: (T) -> Unit) { + queue.toList().forEach(block) + } + + override fun stash(msg: T) { + queue.add(msg) + } + + override fun unstashAll(ctx: ActorContext, behavior: Behavior): Behavior { + val messages = queue.toList() + queue.clear() + + val interpreter = BehaviorInterpreter(behavior) + interpreter.start(ctx) + + for (message in messages) { + interpreter.interpretMessage(ctx, message) + } + + return interpreter.behavior + } +} diff --git a/odcsim/odcsim-core/src/main/kotlin/com/atlarge/odcsim/internal/TimerSchedulerImpl.kt b/odcsim/odcsim-core/src/main/kotlin/com/atlarge/odcsim/internal/TimerSchedulerImpl.kt new file mode 100644 index 00000000..22bec507 --- /dev/null +++ b/odcsim/odcsim-core/src/main/kotlin/com/atlarge/odcsim/internal/TimerSchedulerImpl.kt @@ -0,0 +1,122 @@ +/* + * MIT License + * + * Copyright (c) 2019 atlarge-research + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package com.atlarge.odcsim.internal + +import com.atlarge.odcsim.ActorContext +import com.atlarge.odcsim.Duration +import com.atlarge.odcsim.Signal +import com.atlarge.odcsim.TimerScheduler + +/** + * Implementation of [TimerScheduler] that uses the actor's [ActorContext] to provide timer functionality. + * + * @property ctx The actor context to use. + */ +internal class TimerSchedulerImpl(private val ctx: ActorContext) : TimerScheduler { + private val timers = mutableMapOf>() + + override fun cancel(key: Any) { + val timer = timers[key] ?: return + ctx.log.debug("Cancel timer [{}] with generation [{}]", timer.key, timer.generation) + timers -= timer.key + } + + override fun cancelAll() { + ctx.log.debug("Cancel all timers") + timers.clear() + } + + override fun isTimerActive(key: Any): Boolean = timers.containsKey(key) + + override fun startPeriodicTimer(key: Any, msg: T, interval: Duration) { + startTimer(key, msg, interval, true) + } + + override fun startSingleTimer(key: Any, msg: T, delay: Duration) { + startTimer(key, msg, delay, false) + } + + override fun every(key: Any, interval: Duration, block: () -> Unit) { + @Suppress("UNCHECKED_CAST") + startTimer(key, Block(block) as T, interval, true) + } + + override fun after(key: Any, delay: Duration, block: () -> Unit) { + @Suppress("UNCHECKED_CAST") + startTimer(key, Block(block) as T, delay, false) + } + + private fun startTimer(key: Any, msg: T, duration: Duration, repeat: Boolean) { + val timer = timers.getOrPut(key) { Timer(key) } + timer.duration = duration + timer.generation += 1 + timer.msg = msg + timer.repeat = repeat + ctx.sendSignal(ctx.self, TimerSignal(key, timer.generation), duration) + ctx.log.debug("Start timer [{}] with generation [{}]", key, timer.generation) + } + + fun interceptTimerSignal(signal: TimerSignal): T? { + val timer = timers[signal.key] + + if (timer == null) { + // Message was from canceled timer that was already enqueued + ctx.log.debug("Received timer [{}] that has been removed, discarding", signal.key) + return null + } else if (signal.generation != timer.generation) { + // Message was from an old timer that was enqueued before canceled + ctx.log.debug("Received timer [{}] from old generation [{}], expected generation [{}], discarding", + signal.key, signal.generation, timer.generation) + } + + if (!timer.repeat) { + timers -= timer.key + } else { + ctx.sendSignal(ctx.self, signal, timer.duration) + } + + val msg = timer.msg + + if (msg is Block) { + msg() + return null + } + + return msg + } + + data class Timer(val key: Any) { + var duration: Duration = 0.0 + var repeat: Boolean = false + var generation: Int = 0 + lateinit var msg: T + } + + data class TimerSignal(val key: Any, val generation: Int) : Signal + + data class Block(val block: () -> Unit) { + operator fun invoke() = block() + } +} diff --git a/odcsim/odcsim-core/src/main/kotlin/com/atlarge/odcsim/internal/logging/LocationAwareLoggerImpl.kt b/odcsim/odcsim-core/src/main/kotlin/com/atlarge/odcsim/internal/logging/LocationAwareLoggerImpl.kt new file mode 100644 index 00000000..bf50b5e8 --- /dev/null +++ b/odcsim/odcsim-core/src/main/kotlin/com/atlarge/odcsim/internal/logging/LocationAwareLoggerImpl.kt @@ -0,0 +1,567 @@ +/* + * MIT License + * + * Copyright (c) 2019 atlarge-research + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package com.atlarge.odcsim.internal.logging + +import com.atlarge.odcsim.ActorContext +import org.slf4j.Logger +import org.slf4j.Marker +import org.slf4j.helpers.MessageFormatter +import org.slf4j.spi.LocationAwareLogger + +/** + * An actor-specific [Logger] implementation that is aware of the calling location. + * + * @param ctx The owning [ActorContext] of this logger. + * @param delegate The [LocationAwareLogger] to delegate the messages to. + */ +internal class LocationAwareLoggerImpl( + ctx: ActorContext<*>, + private val delegate: LocationAwareLogger +) : LoggerImpl(ctx), Logger by delegate { + /** + * The fully qualified name of this class. + */ + private val fqcn = LocationAwareLoggerImpl::class.java.name + + override fun trace(format: String?, arg: Any?) { + if (!delegate.isTraceEnabled) { + return + } + + withMdc { + val formattedMessage = MessageFormatter.format(format, arg).message + delegate.log(null, fqcn, LocationAwareLogger.TRACE_INT, formattedMessage, arrayOf(arg), null) + } + } + + override fun trace(format: String?, arg1: Any?, arg2: Any?) { + if (!delegate.isTraceEnabled) { + return + } + + withMdc { + val formattedMessage = MessageFormatter.format(format, arg1, arg2).message + delegate.log(null, fqcn, LocationAwareLogger.TRACE_INT, formattedMessage, arrayOf(arg1, arg2), null) + } + } + + override fun trace(format: String?, argArray: Array) { + if (!delegate.isTraceEnabled) { + return + } + + withMdc { + val formattedMessage = MessageFormatter.arrayFormat(format, argArray).message + delegate.log(null, fqcn, LocationAwareLogger.TRACE_INT, formattedMessage, argArray, null) + } + } + + override fun trace(msg: String?, t: Throwable?) { + if (!delegate.isTraceEnabled) { + return + } + + withMdc { + delegate.log(null, fqcn, LocationAwareLogger.TRACE_INT, msg, null, t) + } + } + + override fun trace(marker: Marker?, msg: String?) { + if (!delegate.isTraceEnabled) { + return + } + + withMdc { + delegate.log(marker, fqcn, LocationAwareLogger.TRACE_INT, msg, null, null) + } + } + + override fun trace(marker: Marker?, format: String?, arg: Any?) { + if (!delegate.isTraceEnabled) { + return + } + + withMdc { + val formattedMessage = MessageFormatter.format(format, arg).message + delegate.log(marker, fqcn, LocationAwareLogger.TRACE_INT, formattedMessage, arrayOf(arg), null) + } + } + + override fun trace(marker: Marker?, format: String?, arg1: Any?, arg2: Any?) { + if (!delegate.isTraceEnabled) { + return + } + + withMdc { + val formattedMessage = MessageFormatter.format(format, arg1, arg2).message + delegate.log(marker, fqcn, LocationAwareLogger.TRACE_INT, formattedMessage, arrayOf(arg1, arg2), null) + } + } + + override fun trace(marker: Marker?, format: String?, argArray: Array) { + if (!delegate.isTraceEnabled) { + return + } + + withMdc { + val formattedMessage = MessageFormatter.arrayFormat(format, argArray).message + delegate.log(marker, fqcn, LocationAwareLogger.TRACE_INT, formattedMessage, argArray, null) + } + } + + override fun trace(marker: Marker?, msg: String?, t: Throwable?) { + if (!delegate.isTraceEnabled) { + return + } + + withMdc { + delegate.log(marker, fqcn, LocationAwareLogger.TRACE_INT, msg, null, t) + } + } + + override fun debug(msg: String?) { + if (!delegate.isDebugEnabled) { + return + } + + withMdc { + delegate.log(null, fqcn, LocationAwareLogger.DEBUG_INT, msg, null, null) + } + } + + override fun debug(format: String?, arg: Any?) { + if (!delegate.isDebugEnabled) { + return + } + + withMdc { + val formattedMessage = MessageFormatter.format(format, arg).message + delegate.log(null, fqcn, LocationAwareLogger.DEBUG_INT, formattedMessage, arrayOf(arg), null) + } + } + + override fun debug(format: String?, arg1: Any?, arg2: Any?) { + if (!delegate.isDebugEnabled) { + return + } + + withMdc { + val formattedMessage = MessageFormatter.format(format, arg1, arg2).message + delegate.log(null, fqcn, LocationAwareLogger.DEBUG_INT, formattedMessage, arrayOf(arg1, arg2), null) + } + } + + override fun debug(format: String?, argArray: Array) { + if (!delegate.isDebugEnabled) { + return + } + + withMdc { + val ft = MessageFormatter.arrayFormat(format, argArray) + delegate.log(null, fqcn, LocationAwareLogger.DEBUG_INT, ft.message, ft.argArray, ft.throwable) + } + } + + override fun debug(msg: String?, t: Throwable?) { + if (!delegate.isDebugEnabled) { + return + } + + withMdc { + delegate.log(null, fqcn, LocationAwareLogger.DEBUG_INT, msg, null, t) + } + } + + override fun debug(marker: Marker?, msg: String?) { + if (!delegate.isDebugEnabled) { + return + } + + withMdc { + delegate.log(marker, fqcn, LocationAwareLogger.DEBUG_INT, msg, null, null) + } + } + + override fun debug(marker: Marker?, format: String?, arg: Any?) { + if (!delegate.isDebugEnabled) { + return + } + + withMdc { + val ft = MessageFormatter.format(format, arg) + delegate.log(marker, fqcn, LocationAwareLogger.DEBUG_INT, ft.message, ft.argArray, ft.throwable) + } + } + + override fun debug(marker: Marker?, format: String?, arg1: Any?, arg2: Any?) { + if (!delegate.isDebugEnabled) { + return + } + + withMdc { + val formattedMessage = MessageFormatter.format(format, arg1, arg2).message + delegate.log(marker, fqcn, LocationAwareLogger.DEBUG_INT, formattedMessage, arrayOf(arg1, arg2), null) + } + } + + override fun debug(marker: Marker?, format: String?, argArray: Array) { + if (!delegate.isDebugEnabled) { + return + } + + withMdc { + val ft = MessageFormatter.arrayFormat(format, argArray) + delegate.log(marker, fqcn, LocationAwareLogger.DEBUG_INT, ft.message, argArray, ft.throwable) + } + } + + override fun debug(marker: Marker?, msg: String?, t: Throwable?) { + if (!delegate.isDebugEnabled) { + return + } + + withMdc { + delegate.log(marker, fqcn, LocationAwareLogger.DEBUG_INT, msg, null, t) + } + } + + override fun info(msg: String?) { + if (!delegate.isInfoEnabled) { + return + } + + withMdc { + delegate.log(null, fqcn, LocationAwareLogger.INFO_INT, msg, null, null) + } + } + + override fun info(format: String?, arg: Any?) { + if (!delegate.isInfoEnabled) { + return + } + + withMdc { + val formattedMessage = MessageFormatter.format(format, arg).message + delegate.log(null, fqcn, LocationAwareLogger.INFO_INT, formattedMessage, arrayOf(arg), null) + } + } + + override fun info(format: String?, arg1: Any?, arg2: Any?) { + if (!delegate.isInfoEnabled) { + return + } + + withMdc { + val formattedMessage = MessageFormatter.format(format, arg1, arg2).message + delegate.log(null, fqcn, LocationAwareLogger.INFO_INT, formattedMessage, arrayOf(arg1, arg2), null) + } + } + + override fun info(format: String?, argArray: Array) { + if (!delegate.isInfoEnabled) { + return + } + + withMdc { + val formattedMessage = MessageFormatter.arrayFormat(format, argArray).message + delegate.log(null, fqcn, LocationAwareLogger.INFO_INT, formattedMessage, argArray, null) + } + } + + override fun info(msg: String?, t: Throwable?) { + if (!delegate.isInfoEnabled) { + return + } + + withMdc { + delegate.log(null, fqcn, LocationAwareLogger.INFO_INT, msg, null, t) + } + } + + override fun info(marker: Marker?, msg: String?) { + if (!delegate.isInfoEnabled) { + return + } + + withMdc { + delegate.log(marker, fqcn, LocationAwareLogger.INFO_INT, msg, null, null) + } + } + + override fun info(marker: Marker?, format: String?, arg: Any?) { + if (!delegate.isInfoEnabled) { + return + } + + withMdc { + val formattedMessage = MessageFormatter.format(format, arg).message + delegate.log(marker, fqcn, LocationAwareLogger.INFO_INT, formattedMessage, arrayOf(arg), null) + } + } + + override fun info(marker: Marker?, format: String?, arg1: Any?, arg2: Any?) { + if (!delegate.isInfoEnabled) { + return + } + + withMdc { + val formattedMessage = MessageFormatter.format(format, arg1, arg2).message + delegate.log(marker, fqcn, LocationAwareLogger.INFO_INT, formattedMessage, arrayOf(arg1, arg2), null) + } + } + + override fun info(marker: Marker?, format: String?, argArray: Array) { + if (!delegate.isInfoEnabled) { + return + } + + withMdc { + val formattedMessage = MessageFormatter.arrayFormat(format, argArray).message + delegate.log(marker, fqcn, LocationAwareLogger.INFO_INT, formattedMessage, argArray, null) + } + } + + override fun info(marker: Marker?, msg: String?, t: Throwable?) { + if (!delegate.isInfoEnabled) { + return + } + + withMdc { + delegate.log(marker, fqcn, LocationAwareLogger.INFO_INT, msg, null, t) + } + } + + override fun warn(msg: String?) { + if (!delegate.isWarnEnabled) { + return + } + + withMdc { + delegate.log(null, fqcn, LocationAwareLogger.WARN_INT, msg, null, null) + } + } + + override fun warn(format: String?, arg: Any?) { + if (!delegate.isWarnEnabled) { + return + } + + withMdc { + val formattedMessage = MessageFormatter.format(format, arg).message + delegate.log(null, fqcn, LocationAwareLogger.WARN_INT, formattedMessage, arrayOf(arg), null) + } + } + + override fun warn(format: String?, arg1: Any?, arg2: Any?) { + if (!delegate.isWarnEnabled) { + return + } + + withMdc { + val formattedMessage = MessageFormatter.format(format, arg1, arg2).message + delegate.log(null, fqcn, LocationAwareLogger.WARN_INT, formattedMessage, arrayOf(arg1, arg2), null) + } + } + + override fun warn(format: String?, argArray: Array) { + if (!delegate.isWarnEnabled) { + return + } + + withMdc { + val formattedMessage = MessageFormatter.arrayFormat(format, argArray).message + delegate.log(null, fqcn, LocationAwareLogger.WARN_INT, formattedMessage, argArray, null) + } + } + + override fun warn(msg: String?, t: Throwable?) { + if (!delegate.isWarnEnabled) { + return + } + + withMdc { + delegate.log(null, fqcn, LocationAwareLogger.WARN_INT, msg, null, t) + } + } + + override fun warn(marker: Marker?, msg: String?) { + if (!delegate.isWarnEnabled) { + return + } + + withMdc { + delegate.log(marker, fqcn, LocationAwareLogger.WARN_INT, msg, null, null) + } + } + + override fun warn(marker: Marker?, format: String?, arg: Any?) { + if (!delegate.isWarnEnabled) { + return + } + + withMdc { + val formattedMessage = MessageFormatter.format(format, arg).message + delegate.log(marker, fqcn, LocationAwareLogger.WARN_INT, formattedMessage, arrayOf(arg), null) + } + } + + override fun warn(marker: Marker?, format: String?, arg1: Any?, arg2: Any?) { + if (!delegate.isWarnEnabled) { + return + } + + withMdc { + val formattedMessage = MessageFormatter.format(format, arg1, arg2).message + delegate.log(marker, fqcn, LocationAwareLogger.WARN_INT, formattedMessage, arrayOf(arg1, arg2), null) + } + } + + override fun warn(marker: Marker?, format: String?, argArray: Array) { + if (!delegate.isWarnEnabled) { + return + } + + withMdc { + val formattedMessage = MessageFormatter.arrayFormat(format, argArray).message + delegate.log(marker, fqcn, LocationAwareLogger.WARN_INT, formattedMessage, argArray, null) + } + } + + override fun warn(marker: Marker?, msg: String?, t: Throwable?) { + if (!delegate.isWarnEnabled) { + return + } + + withMdc { + delegate.log(marker, fqcn, LocationAwareLogger.WARN_INT, msg, null, t) + } + } + + override fun error(msg: String?) { + if (!delegate.isErrorEnabled) { + return + } + + withMdc { + delegate.log(null, fqcn, LocationAwareLogger.ERROR_INT, msg, null, null) + } + } + + override fun error(format: String?, arg: Any?) { + if (!delegate.isErrorEnabled) { + return + } + + withMdc { + val formattedMessage = MessageFormatter.format(format, arg).message + delegate.log(null, fqcn, LocationAwareLogger.ERROR_INT, formattedMessage, arrayOf(arg), null) + } + } + + override fun error(format: String?, arg1: Any?, arg2: Any?) { + if (!delegate.isErrorEnabled) { + return + } + + withMdc { + val formattedMessage = MessageFormatter.format(format, arg1, arg2).message + delegate.log(null, fqcn, LocationAwareLogger.ERROR_INT, formattedMessage, arrayOf(arg1, arg2), null) + } + } + + override fun error(format: String?, argArray: Array) { + if (!delegate.isErrorEnabled) { + return + } + + withMdc { + val formattedMessage = MessageFormatter.arrayFormat(format, argArray).message + delegate.log(null, fqcn, LocationAwareLogger.ERROR_INT, formattedMessage, argArray, null) + } + } + + override fun error(msg: String?, t: Throwable?) { + if (!delegate.isErrorEnabled) { + return + } + + withMdc { + delegate.log(null, fqcn, LocationAwareLogger.ERROR_INT, msg, null, t) + } + } + + override fun error(marker: Marker?, msg: String?) { + if (!delegate.isErrorEnabled) { + return + } + + withMdc { + delegate.log(marker, fqcn, LocationAwareLogger.ERROR_INT, msg, null, null) + } + } + + override fun error(marker: Marker?, format: String?, arg: Any?) { + if (!delegate.isErrorEnabled) { + return + } + + withMdc { + val formattedMessage = MessageFormatter.format(format, arg).message + delegate.log(marker, fqcn, LocationAwareLogger.ERROR_INT, formattedMessage, arrayOf(arg), null) + } + } + + override fun error(marker: Marker?, format: String?, arg1: Any?, arg2: Any?) { + if (!delegate.isErrorEnabled) { + return + } + + withMdc { + val formattedMessage = MessageFormatter.format(format, arg1, arg2).message + delegate.log(marker, fqcn, LocationAwareLogger.ERROR_INT, formattedMessage, arrayOf(arg1, arg2), null) + } + } + + override fun error(marker: Marker?, format: String?, argArray: Array) { + if (!delegate.isErrorEnabled) { + return + } + + withMdc { + val formattedMessage = MessageFormatter.arrayFormat(format, argArray).message + delegate.log(marker, fqcn, LocationAwareLogger.ERROR_INT, formattedMessage, argArray, null) + } + } + + override fun error(marker: Marker?, msg: String?, t: Throwable?) { + if (!delegate.isErrorEnabled) { + return + } + + withMdc { + delegate.log(marker, fqcn, LocationAwareLogger.ERROR_INT, msg, null, t) + } + } +} diff --git a/odcsim/odcsim-core/src/main/kotlin/com/atlarge/odcsim/internal/logging/LocationIgnorantLoggerImpl.kt b/odcsim/odcsim-core/src/main/kotlin/com/atlarge/odcsim/internal/logging/LocationIgnorantLoggerImpl.kt new file mode 100644 index 00000000..999e30e6 --- /dev/null +++ b/odcsim/odcsim-core/src/main/kotlin/com/atlarge/odcsim/internal/logging/LocationIgnorantLoggerImpl.kt @@ -0,0 +1,440 @@ +/* + * MIT License + * + * Copyright (c) 2019 atlarge-research + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package com.atlarge.odcsim.internal.logging + +import com.atlarge.odcsim.ActorContext +import org.slf4j.Logger +import org.slf4j.Marker + +/** + * A [Logger] implementation that is not aware of the calling location. + * + * @param ctx The owning [ActorContext] of this logger. + * @param delegate The [Logger] to delegate the messages to. + */ +internal class LocationIgnorantLoggerImpl( + ctx: ActorContext<*>, + private val delegate: Logger +) : LoggerImpl(ctx), Logger by delegate { + override fun warn(marker: Marker?, format: String?, arg1: Any?, arg2: Any?) { + if (!isWarnEnabled) { + return + } + + withMdc { delegate.warn(marker, format, arg1, arg2) } + } + + override fun warn(format: String?, arg1: Any?, arg2: Any?) { + if (!isWarnEnabled) { + return + } + + withMdc { delegate.warn(format, arg1, arg2) } + } + + override fun warn(msg: String?) { + if (!isWarnEnabled) { + return + } + + withMdc { delegate.warn(msg) } + } + + override fun warn(marker: Marker?, format: String?, arg: Any?) { + if (!isWarnEnabled) { + return + } + + withMdc { delegate.warn(marker, format, arg) } + } + + override fun warn(marker: Marker?, format: String?, vararg arguments: Any?) { + if (!isWarnEnabled) { + return + } + + withMdc { delegate.warn(marker, format, arguments) } + } + + override fun warn(format: String?, arg: Any?) { + if (!isWarnEnabled) { + return + } + + withMdc { delegate.warn(format, arg) } + } + + override fun warn(marker: Marker?, msg: String?) { + if (!isWarnEnabled) { + return + } + + withMdc { delegate.warn(marker, msg) } + } + + override fun warn(msg: String?, t: Throwable?) { + if (!isWarnEnabled) { + return + } + + withMdc { delegate.warn(msg, t) } + } + + override fun warn(format: String?, vararg arguments: Any?) { + if (!isWarnEnabled) { + return + } + + withMdc { delegate.warn(format, *arguments) } + } + + override fun warn(marker: Marker?, msg: String?, t: Throwable?) { + if (!isWarnEnabled) { + return + } + + withMdc { delegate.warn(marker, msg, t) } + } + + override fun info(marker: Marker?, format: String?, vararg arguments: Any?) { + if (!isInfoEnabled) { + return + } + + withMdc { delegate.info(marker, format, *arguments) } + } + + override fun info(format: String?, arg: Any?) { + if (!isInfoEnabled) { + return + } + + withMdc { delegate.info(format, arg) } + } + + override fun info(marker: Marker?, msg: String?, t: Throwable?) { + if (!isInfoEnabled) { + return + } + + withMdc { delegate.info(marker, msg, t) } + } + + override fun info(msg: String?) { + if (!isInfoEnabled) { + return + } + + withMdc { delegate.info(msg) } + } + + override fun info(format: String?, vararg arguments: Any?) { + if (!isInfoEnabled) { + return + } + + withMdc { delegate.info(format, *arguments) } + } + + override fun info(format: String?, arg1: Any?, arg2: Any?) { + if (!isInfoEnabled) { + return + } + + withMdc { delegate.info(format, arg1, arg2) } + } + + override fun info(marker: Marker?, msg: String?) { + if (!isInfoEnabled) { + return + } + + withMdc { delegate.info(marker, msg) } + } + + override fun info(marker: Marker?, format: String?, arg: Any?) { + if (!isInfoEnabled) { + return + } + + withMdc { delegate.info(marker, format, arg) } + } + + override fun info(marker: Marker?, format: String?, arg1: Any?, arg2: Any?) { + if (!isInfoEnabled) { + return + } + + withMdc { delegate.info(marker, format, arg1, arg2) } + } + + override fun info(msg: String?, t: Throwable?) { + if (!isInfoEnabled) { + return + } + + withMdc { delegate.info(msg, t) } + } + + override fun error(msg: String?) { + if (!isErrorEnabled) { + return + } + + withMdc { delegate.error(msg) } + } + + override fun error(marker: Marker?, msg: String?) { + if (!isErrorEnabled) { + return + } + + withMdc { delegate.error(marker, msg) } + } + + override fun error(format: String?, vararg arguments: Any?) { + if (!isErrorEnabled) { + return + } + + withMdc { delegate.error(format, *arguments) } + } + + override fun error(format: String?, arg: Any?) { + if (!isErrorEnabled) { + return + } + + withMdc { delegate.error(format, arg) } + } + + override fun error(msg: String?, t: Throwable?) { + if (!isErrorEnabled) { + return + } + + withMdc { delegate.error(msg, t) } + } + + override fun error(marker: Marker?, format: String?, arg1: Any?, arg2: Any?) { + if (!isErrorEnabled) { + return + } + + withMdc { delegate.error(marker, format, arg1, arg2) } + } + + override fun error(marker: Marker?, format: String?, vararg arguments: Any?) { + if (!isErrorEnabled) { + return + } + + withMdc { delegate.error(marker, format, *arguments) } + } + + override fun error(marker: Marker?, msg: String?, t: Throwable?) { + if (!isErrorEnabled) { + return + } + + withMdc { delegate.error(marker, msg, t) } + } + + override fun error(format: String?, arg1: Any?, arg2: Any?) { + if (!isErrorEnabled) { + return + } + + withMdc { delegate.error(format, arg1, arg2) } + } + + override fun error(marker: Marker?, format: String?, arg: Any?) { + if (!isErrorEnabled) { + return + } + + withMdc { delegate.error(marker, format, arg) } + } + + override fun debug(format: String?, vararg arguments: Any?) { + if (!isDebugEnabled) { + return + } + + withMdc { delegate.debug(format, *arguments) } + } + + override fun debug(format: String?, arg1: Any?, arg2: Any?) { + if (!isDebugEnabled) { + return + } + + withMdc { delegate.debug(format, arg1, arg2) } + } + + override fun debug(msg: String?, t: Throwable?) { + if (!isDebugEnabled) { + return + } + + withMdc { delegate.debug(msg, t) } + } + + override fun debug(format: String?, arg: Any?) { + if (!isDebugEnabled) { + return + } + + withMdc { delegate.debug(format, arg) } + } + + override fun debug(marker: Marker?, msg: String?) { + if (!isDebugEnabled) { + return + } + + withMdc { delegate.debug(marker, msg) } + } + + override fun debug(msg: String?) { + if (!isDebugEnabled) { + return + } + + withMdc { delegate.debug(msg) } + } + + override fun debug(marker: Marker?, msg: String?, t: Throwable?) { + if (!isDebugEnabled) { + return + } + + withMdc { delegate.debug(marker, msg, t) } + } + + override fun debug(marker: Marker?, format: String?, arg1: Any?, arg2: Any?) { + if (!isDebugEnabled) { + return + } + + withMdc { delegate.debug(marker, format, arg1, arg2) } + } + + override fun debug(marker: Marker?, format: String?, arg: Any?) { + if (!isDebugEnabled) { + return + } + + withMdc { delegate.debug(marker, format, arg) } + } + + override fun debug(marker: Marker?, format: String?, vararg arguments: Any?) { + if (!isDebugEnabled) { + return + } + + withMdc { delegate.debug(marker, format, *arguments) } + } + + override fun trace(format: String?, arg: Any?) { + if (!isTraceEnabled) { + return + } + + withMdc { delegate.trace(format, arg) } + } + + override fun trace(marker: Marker?, msg: String?) { + if (!isTraceEnabled) { + return + } + + withMdc { delegate.trace(marker, msg) } + } + + override fun trace(msg: String?) { + if (!isTraceEnabled) { + return + } + + withMdc { delegate.trace(msg) } + } + + override fun trace(msg: String?, t: Throwable?) { + if (!isTraceEnabled) { + return + } + + withMdc { delegate.trace(msg, t) } + } + + override fun trace(format: String?, arg1: Any?, arg2: Any?) { + if (!isTraceEnabled) { + return + } + + withMdc { delegate.trace(format, arg1, arg2) } + } + + override fun trace(marker: Marker?, format: String?, arg1: Any?, arg2: Any?) { + if (!isTraceEnabled) { + return + } + + withMdc { delegate.trace(marker, format, arg1, arg2) } + } + + override fun trace(marker: Marker?, format: String?, arg: Any?) { + if (!isTraceEnabled) { + return + } + + withMdc { delegate.trace(marker, format, arg) } + } + + override fun trace(marker: Marker?, format: String?, vararg argArray: Any?) { + if (!isTraceEnabled) { + return + } + + withMdc { delegate.trace(marker, format, *argArray) } + } + + override fun trace(marker: Marker?, msg: String?, t: Throwable?) { + if (!isTraceEnabled) { + return + } + + withMdc { delegate.trace(marker, msg, t) } + } + + override fun trace(format: String?, vararg arguments: Any?) { + if (!isTraceEnabled) { + return + } + + withMdc { delegate.trace(format, *arguments) } + } +} diff --git a/odcsim/odcsim-core/src/main/kotlin/com/atlarge/odcsim/internal/logging/LoggerImpl.kt b/odcsim/odcsim-core/src/main/kotlin/com/atlarge/odcsim/internal/logging/LoggerImpl.kt new file mode 100644 index 00000000..f971f08d --- /dev/null +++ b/odcsim/odcsim-core/src/main/kotlin/com/atlarge/odcsim/internal/logging/LoggerImpl.kt @@ -0,0 +1,77 @@ +/* + * MIT License + * + * Copyright (c) 2019 atlarge-research + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package com.atlarge.odcsim.internal.logging + +import com.atlarge.odcsim.ActorContext +import org.slf4j.Logger +import org.slf4j.LoggerFactory +import org.slf4j.MDC +import org.slf4j.spi.LocationAwareLogger + +/** + * An actor-specific [Logger] implementation. + * + * @param ctx The owning [ActorContext] of this logger. + */ +abstract class LoggerImpl internal constructor(protected val ctx: ActorContext<*>) : Logger { + /** + * Configure [MDC] with actor-specific information. + */ + protected inline fun withMdc(block: () -> Unit) { + MDC.put(MDC_ACTOR_SYSTEM, ctx.system.name) + MDC.put(MDC_ACTOR_TIME, String.format("%.2f", ctx.time)) + MDC.put(MDC_ACTOR_REF, ctx.self.path.toString()) + try { + block() + } finally { + MDC.remove(MDC_ACTOR_SYSTEM) + MDC.remove(MDC_ACTOR_TIME) + MDC.remove(MDC_ACTOR_REF) + } + } + + /** + * Mapped Diagnostic Context (MDC) attribute names. + */ + companion object { + val MDC_ACTOR_SYSTEM = "actor.system" + val MDC_ACTOR_TIME = "actor.time" + val MDC_ACTOR_REF = "actor.ref" + + /** + * Create a [Logger] for the specified [ActorContext]. + * + * @param ctx The actor context to create the logger for. + */ + operator fun invoke(ctx: ActorContext<*>): Logger { + val logger = LoggerFactory.getLogger(ctx.javaClass) + return if (logger is LocationAwareLogger) { + LocationAwareLoggerImpl(ctx, logger) + } else { + LocationIgnorantLoggerImpl(ctx, logger) + } + } + } +} diff --git a/odcsim/odcsim-core/src/test/kotlin/com/atlarge/odcsim/ActorPathTest.kt b/odcsim/odcsim-core/src/test/kotlin/com/atlarge/odcsim/ActorPathTest.kt new file mode 100644 index 00000000..023d3efd --- /dev/null +++ b/odcsim/odcsim-core/src/test/kotlin/com/atlarge/odcsim/ActorPathTest.kt @@ -0,0 +1,86 @@ +/* + * MIT License + * + * Copyright (c) 2018 atlarge-research + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package com.atlarge.odcsim + +import org.junit.jupiter.api.Assertions.assertEquals +import org.junit.jupiter.api.DisplayName +import org.junit.jupiter.api.Test +import org.junit.jupiter.api.assertThrows + +/** + * A test suite for the [ActorPath] class. + */ +@DisplayName("ActorPath") +class ActorPathTest { + /** + * Test whether an [ActorPath.Root] may only start with a slash. + */ + @Test + fun `root node may only start with a slash`() { + ActorPath.Root() // Assert slash at start + assertThrows { ActorPath.Root("abc/") } + } + + /** + * Test whether an [ActorPath.Child] disallows names with a slash. + */ + @Test + fun `child node should not allow name with a slash`() { + assertThrows { ActorPath.Child(ActorPath.Root(), "/") } + } + + /** + * Test whether a root node can have a custom name. + */ + @Test + fun `root node can have a custom name`() { + val name = "user" + assertEquals(name, ActorPath.Root(name).name) + } + + /** + * Test whether a child node can be created on a root. + */ + @Test + fun `child node can be created on a root`() { + val root = ActorPath.Root(name = "/user") + val child = root.child("child") + + assertEquals(root, child.parent) + assertEquals("child", child.name) + } + + /** + * Test whether a child node can be created on a child. + */ + @Test + fun `child node can be created on a child`() { + val root = ActorPath.Root(name = "/user").child("child") + val child = root.child("child") + + assertEquals(root, child.parent) + assertEquals("child", child.name) + } +} diff --git a/odcsim/odcsim-core/src/test/kotlin/com/atlarge/odcsim/BehaviorTest.kt b/odcsim/odcsim-core/src/test/kotlin/com/atlarge/odcsim/BehaviorTest.kt new file mode 100644 index 00000000..1eb4f3b9 --- /dev/null +++ b/odcsim/odcsim-core/src/test/kotlin/com/atlarge/odcsim/BehaviorTest.kt @@ -0,0 +1,77 @@ +/* + * MIT License + * + * Copyright (c) 2018 atlarge-research + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package com.atlarge.odcsim + +import com.atlarge.odcsim.internal.BehaviorInterpreter +import com.nhaarman.mockitokotlin2.mock +import org.junit.jupiter.api.DisplayName +import org.junit.jupiter.api.Test +import org.junit.jupiter.api.assertThrows + +/** + * Test suite for [Behavior] and [BehaviorInterpreter]. + */ +@DisplayName("Behavior") +class BehaviorTest { + /** + * Test whether we cannot start an actor with the [unhandled] behavior. + */ + @Test + fun `should not start with unhandled behavior`() { + val ctx = mock>() + val interpreter = BehaviorInterpreter(unhandled()) + assertThrows { interpreter.start(ctx) } + } + + /** + * Test whether we cannot start an actor with deferred unhandled behavior. + */ + @Test + fun `should not start with deferred unhandled behavior`() { + val ctx = mock>() + val interpreter = BehaviorInterpreter(setup { unhandled() }) + assertThrows { interpreter.start(ctx) } + } + + /** + * Test whether deferred behavior that returns [same] fails. + */ + @Test + fun `should not allow setup to return same`() { + val ctx = mock>() + val interpreter = BehaviorInterpreter(setup { same() }) + assertThrows { interpreter.start(ctx) } + } + + /** + * Test whether deferred behavior that returns [unhandled] fails. + */ + @Test + fun `should not allow setup to return unhandled`() { + val ctx = mock>() + val interpreter = BehaviorInterpreter(setup { unhandled() }) + assertThrows { interpreter.start(ctx) } + } +} diff --git a/odcsim/odcsim-core/src/test/kotlin/com/atlarge/odcsim/CoroutinesTest.kt b/odcsim/odcsim-core/src/test/kotlin/com/atlarge/odcsim/CoroutinesTest.kt new file mode 100644 index 00000000..af7619e6 --- /dev/null +++ b/odcsim/odcsim-core/src/test/kotlin/com/atlarge/odcsim/CoroutinesTest.kt @@ -0,0 +1,63 @@ +/* + * MIT License + * + * Copyright (c) 2019 atlarge-research + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package com.atlarge.odcsim + +import com.atlarge.odcsim.coroutines.SuspendingBehavior +import com.atlarge.odcsim.coroutines.suspending +import com.atlarge.odcsim.internal.BehaviorInterpreter +import com.atlarge.odcsim.internal.EmptyBehavior +import com.nhaarman.mockitokotlin2.mock +import org.junit.jupiter.api.Assertions.assertTrue +import org.junit.jupiter.api.DisplayName +import org.junit.jupiter.api.Test +import kotlin.coroutines.suspendCoroutine + +/** + * Test suite for [SuspendingBehavior] using Kotlin Coroutines. + */ +@DisplayName("Coroutines") +internal class CoroutinesTest { + + @Test + fun `should immediately return new behavior`() { + val ctx = mock>() + val behavior = suspending { empty() } + val interpreter = BehaviorInterpreter(behavior) + interpreter.start(ctx) + assertTrue(interpreter.behavior as Behavior<*> is EmptyBehavior) + } + + @Test + fun `should be able to invoke regular suspend methods`() { + val ctx = mock>() + val behavior = suspending { + suspendCoroutine {} + stopped() + } + val interpreter = BehaviorInterpreter(behavior) + interpreter.start(ctx) + interpreter.interpretMessage(ctx, Unit) + } +} diff --git a/odcsim/odcsim-engine-omega/build.gradle.kts b/odcsim/odcsim-engine-omega/build.gradle.kts new file mode 100644 index 00000000..d4383303 --- /dev/null +++ b/odcsim/odcsim-engine-omega/build.gradle.kts @@ -0,0 +1,46 @@ +/* + * MIT License + * + * Copyright (c) 2018 atlarge-research + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +/* Build configuration */ +plugins { + `kotlin-library-convention` +} + +/* Project configuration */ +repositories { + jcenter() +} + +dependencies { + api(project(":odcsim:odcsim-core")) + + implementation(kotlin("stdlib")) + implementation("org.jetbrains:annotations:17.0.0") + + testImplementation(project(":odcsim:odcsim-engine-tests")) + testImplementation("org.junit.jupiter:junit-jupiter-api:${Library.JUNIT_JUPITER}") + testRuntimeOnly("org.junit.jupiter:junit-jupiter-engine:${Library.JUNIT_JUPITER}") + testImplementation("org.junit.platform:junit-platform-launcher:${Library.JUNIT_PLATFORM}") + testRuntimeOnly("org.slf4j:slf4j-simple:${Library.SLF4J}") +} diff --git a/odcsim/odcsim-engine-omega/src/main/kotlin/com/atlarge/odcsim/engine/omega/OmegaActorSystem.kt b/odcsim/odcsim-engine-omega/src/main/kotlin/com/atlarge/odcsim/engine/omega/OmegaActorSystem.kt new file mode 100644 index 00000000..dd92f90a --- /dev/null +++ b/odcsim/odcsim-engine-omega/src/main/kotlin/com/atlarge/odcsim/engine/omega/OmegaActorSystem.kt @@ -0,0 +1,360 @@ +/* + * MIT License + * + * Copyright (c) 2018 atlarge-research + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package com.atlarge.odcsim.engine.omega + +import com.atlarge.odcsim.ActorContext +import com.atlarge.odcsim.ActorPath +import com.atlarge.odcsim.ActorRef +import com.atlarge.odcsim.ActorSystem +import com.atlarge.odcsim.Behavior +import com.atlarge.odcsim.Duration +import com.atlarge.odcsim.Envelope +import com.atlarge.odcsim.Instant +import com.atlarge.odcsim.PostStop +import com.atlarge.odcsim.PreStart +import com.atlarge.odcsim.Signal +import com.atlarge.odcsim.Terminated +import com.atlarge.odcsim.empty +import com.atlarge.odcsim.internal.BehaviorInterpreter +import com.atlarge.odcsim.internal.logging.LoggerImpl +import org.jetbrains.annotations.Async +import org.slf4j.Logger +import java.util.Collections +import java.util.PriorityQueue +import java.util.UUID +import java.util.WeakHashMap +import kotlin.math.max + +/** + * The reference implementation of the [ActorSystem] instance for the OpenDC simulation core. + * + * This engine implementation is a single-threaded implementation, running actors synchronously and + * provides a single priority queue for all events (messages, ticks, etc) that occur. + * + * @param guardianBehavior The behavior of the guardian (root) actor. + * @param name The name of the engine instance. + */ +class OmegaActorSystem(guardianBehavior: Behavior, override val name: String) : ActorSystem, ActorRef { + /** + * The state of the actor system. + */ + private var state: ActorSystemState = ActorSystemState.CREATED + + /** + * The event queue to process + */ + private val queue: PriorityQueue = PriorityQueue( + Comparator + .comparingDouble(EnvelopeImpl::time) + .thenComparingLong(EnvelopeImpl::id) + ) + + /** + * The registry of actors in the system. + */ + private val registry: MutableMap> = HashMap() + + /** + * The root actor path of the system. + */ + private val root: ActorPath = ActorPath.Root() + + /** + * The system actor path. + */ + private val system: ActorPath = root / "system" + + /** + * The current point in simulation time. + */ + override var time: Instant = .0 + + /** + * The path to the root actor. + */ + override val path: ActorPath = root / "user" + + init { + registry[system] = Actor(ActorRefImpl(this, system), empty()) + registry[path] = Actor(this, guardianBehavior) + schedule(path, PreStart, .0) + } + + override fun run(until: Duration) { + require(until >= .0) { "The given instant must be a non-negative number" } + + // Start the system/guardian actor on initial run + if (state == ActorSystemState.CREATED) { + state = ActorSystemState.STARTED + registry[system]!!.isolate { it.start() } + registry[path]!!.isolate { it.start() } + } else if (state == ActorSystemState.TERMINATED) { + throw IllegalStateException("The ActorSystem has been terminated.") + } + + while (time < until) { + // Check whether the system was interrupted + if (Thread.interrupted()) { + throw InterruptedException() + } + + val envelope = queue.peek() ?: break + val delivery = envelope.time.takeUnless { it > until } ?: break + + // A message should never be delivered out of order in this single-threaded implementation. Assert for + // sanity + assert(delivery >= time) { "Message delivered out of order [expected=$delivery, actual=$time]" } + + time = delivery + queue.poll() + + processEnvelope(envelope) + } + + // Jump forward in time as the caller expects the system to have run until the specified instant + // Taking the maximum value prevents the caller to jump backwards in time + time = max(time, until) + } + + override fun send(msg: T, after: Duration) = schedule(path, msg, after) + + override fun terminate() { + registry[path]?.stop(null) + registry[system]?.stop(null) + } + + override suspend fun spawnSystem(behavior: Behavior, name: String): ActorRef { + return registry[system]!!.spawn(behavior, name) + } + + override fun compareTo(other: ActorRef<*>): Int = path.compareTo(other.path) + + /** + * The identifier for the next message to be scheduled. + */ + private var nextId: Long = 0 + + /** + * Schedule a message to be processed by the engine. + * + * @param path The path to the destination of the message. + * @param message The message to schedule. + * @param delay The time to wait before processing the message. + */ + private fun schedule(@Async.Schedule path: ActorPath, message: Any, delay: Duration) { + require(delay >= .0) { "The given delay must be a non-negative number" } + scheduleEnvelope(EnvelopeImpl(nextId++, path, time + delay, message)) + } + + /** + * Schedule the specified envelope to be processed by the engine. + */ + private fun scheduleEnvelope(@Async.Schedule envelope: EnvelopeImpl) { + queue.add(envelope) + } + + /** + * Process the delivery of a message. + */ + private fun processEnvelope(@Async.Execute envelope: EnvelopeImpl) { + val actor = registry[envelope.destination] ?: return + + // Notice that messages for unknown/terminated actors are ignored for now + actor.isolate { it.interpretMessage(envelope.message) } + } + + /** + * An actor as represented in the Omega engine. + * + * @param self The [ActorRef] to this actor. + * @param initialBehavior The initial behavior of this actor. + */ + private inner class Actor(override val self: ActorRef, initialBehavior: Behavior) : ActorContext { + val childActors: MutableMap> = mutableMapOf() + val interpreter = BehaviorInterpreter(initialBehavior) + val watchers: MutableSet = Collections.newSetFromMap(WeakHashMap()) + + override val time: Instant + get() = this@OmegaActorSystem.time + + override val children: List> + get() = childActors.values.map { it.self } + + override val system: ActorSystem<*> + get() = this@OmegaActorSystem + + override val log: Logger by lazy(LazyThreadSafetyMode.NONE) { LoggerImpl(this) } + + override fun getChild(name: String): ActorRef<*>? = childActors[name]?.self + + override fun send(ref: ActorRef, msg: U, after: Duration) = schedule(ref.path, msg, after) + + override fun spawn(behavior: Behavior, name: String): ActorRef { + require(name.isNotEmpty()) { "Actor name may not be empty" } + require(!name.startsWith("$")) { "Actor name may not start with $-sign" } + return internalSpawn(behavior, name) + } + + override fun spawnAnonymous(behavior: Behavior): ActorRef { + val name = "$" + UUID.randomUUID() + return internalSpawn(behavior, name) + } + + private fun internalSpawn(behavior: Behavior, name: String): ActorRef { + require(name !in childActors) { "Actor name $name not unique" } + val ref = ActorRefImpl(this@OmegaActorSystem, self.path.child(name)) + val actor = Actor(ref, behavior) + registry[ref.path] = actor + childActors[name] = actor + schedule(ref.path, PreStart, .0) + actor.start() + return ref + } + + override fun stop(child: ActorRef<*>) { + when { + // Must be a direct child of this actor + child.path.parent == self.path -> { + val ref = childActors[child.path.name] ?: return + ref.stop(null) + } + self == child -> throw IllegalArgumentException( + "Only direct children of an actor may be stopped through the actor context, " + + "but you tried to stop [$self] by passing its ActorRef to the `stop` method. " + + "Stopping self has to be expressed as explicitly returning a Stop Behavior." + ) + else -> throw IllegalArgumentException( + "Only direct children of an actor may be stopped through the actor context, " + + "but [$child] is not a child of [$self]. Stopping other actors has to be expressed as " + + "an explicit stop message that the actor accepts." + ) + } + } + + override fun watch(target: ActorRef<*>) { + registry[target.path]?.watchers?.add(path) + } + + override fun unwatch(target: ActorRef<*>) { + registry[target.path]?.watchers?.remove(path) + } + + // Synchronization of actors in a single-threaded simulation is trivial: all actors are consistent in virtual + // time. + override fun sync(target: ActorRef<*>) {} + + override fun unsync(target: ActorRef<*>) {} + + override fun isSync(target: ActorRef<*>): Boolean = true + + /** + * Start this actor. + */ + fun start() { + interpreter.start(this) + } + + /** + * Stop this actor. + */ + fun stop(failure: Throwable?) { + interpreter.stop(this) + childActors.values.forEach { it.stop(failure) } + registry.remove(self.path) + interpreter.interpretSignal(this, PostStop) + val termination = Terminated(self, failure) + watchers.forEach { schedule(it, termination, 0.0) } + } + + /** + * Interpret the given message send to an actor. + */ + fun interpretMessage(msg: Any) { + if (msg is Signal) { + interpreter.interpretSignal(this, msg) + } else { + @Suppress("UNCHECKED_CAST") + interpreter.interpretMessage(this, msg as T) + } + + if (!interpreter.isAlive) { + stop(null) + } + } + + override fun equals(other: Any?): Boolean = + other is OmegaActorSystem<*>.Actor<*> && self.path == other.self.path + + override fun hashCode(): Int = self.path.hashCode() + } + + /** + * Isolate uncaught exceptions originating from actor interpreter invocations. + */ + private inline fun Actor.isolate(block: (Actor) -> U): U? { + return try { + block(this) + } catch (t: Throwable) { + // Forcefully stop the actor if it crashed + stop(t) + log.error("Unhandled exception in actor $path", t) + null + } + } + + /** + * Enumeration to track the state of the actor system. + */ + private enum class ActorSystemState { + CREATED, STARTED, TERMINATED + } + + /** + * Internal [ActorRef] implementation for this actor system. + */ + private data class ActorRefImpl( + private val owner: OmegaActorSystem<*>, + override val path: ActorPath + ) : ActorRef { + override fun toString(): String = "Actor[$path]" + + override fun compareTo(other: ActorRef<*>): Int = path.compareTo(other.path) + } + + /** + * A wrapper around a message that has been scheduled for processing. + * + * @property id The identifier of the message to keep the priority queue stable. + * @property destination The destination of the message. + * @property time The point in time to deliver the message. + * @property message The message to wrap. + */ + private class EnvelopeImpl( + val id: Long, + val destination: ActorPath, + override val time: Instant, + override val message: Any + ) : Envelope +} diff --git a/odcsim/odcsim-engine-omega/src/main/kotlin/com/atlarge/odcsim/engine/omega/OmegaActorSystemFactory.kt b/odcsim/odcsim-engine-omega/src/main/kotlin/com/atlarge/odcsim/engine/omega/OmegaActorSystemFactory.kt new file mode 100644 index 00000000..84bf1efb --- /dev/null +++ b/odcsim/odcsim-engine-omega/src/main/kotlin/com/atlarge/odcsim/engine/omega/OmegaActorSystemFactory.kt @@ -0,0 +1,38 @@ +/* + * MIT License + * + * Copyright (c) 2018 atlarge-research + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package com.atlarge.odcsim.engine.omega + +import com.atlarge.odcsim.ActorSystem +import com.atlarge.odcsim.ActorSystemFactory +import com.atlarge.odcsim.Behavior +import java.util.ServiceLoader + +/** + * An [ActorSystemFactory] for the Omega engine, used by the [ServiceLoader] API to create [OmegaActorSystem] instances. + */ +class OmegaActorSystemFactory : ActorSystemFactory { + override operator fun invoke(root: Behavior, name: String): ActorSystem = + OmegaActorSystem(root, name) +} diff --git a/odcsim/odcsim-engine-omega/src/main/resources/META-INF/services/com.atlarge.odcsim.ActorSystemFactory b/odcsim/odcsim-engine-omega/src/main/resources/META-INF/services/com.atlarge.odcsim.ActorSystemFactory new file mode 100644 index 00000000..d0ca8859 --- /dev/null +++ b/odcsim/odcsim-engine-omega/src/main/resources/META-INF/services/com.atlarge.odcsim.ActorSystemFactory @@ -0,0 +1 @@ +com.atlarge.odcsim.engine.omega.OmegaActorSystemFactory diff --git a/odcsim/odcsim-engine-omega/src/test/kotlin/com/atlarge/odcsim/engine/omega/OmegaActorSystemFactoryTest.kt b/odcsim/odcsim-engine-omega/src/test/kotlin/com/atlarge/odcsim/engine/omega/OmegaActorSystemFactoryTest.kt new file mode 100644 index 00000000..4e195e6e --- /dev/null +++ b/odcsim/odcsim-engine-omega/src/test/kotlin/com/atlarge/odcsim/engine/omega/OmegaActorSystemFactoryTest.kt @@ -0,0 +1,37 @@ +/* + * MIT License + * + * Copyright (c) 2018 atlarge-research + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package com.atlarge.odcsim.engine.omega + +import com.atlarge.odcsim.ActorSystemFactory +import com.atlarge.odcsim.engine.tests.ActorSystemFactoryContract +import org.junit.jupiter.api.DisplayName + +/** + * The [ActorSystemFactory] test suite for the Omega engine implementation. + */ +@DisplayName("OmegaActorSystemFactory") +class OmegaActorSystemFactoryTest : ActorSystemFactoryContract() { + override fun createFactory(): ActorSystemFactory = OmegaActorSystemFactory() +} diff --git a/odcsim/odcsim-engine-omega/src/test/kotlin/com/atlarge/odcsim/engine/omega/OmegaActorSystemTest.kt b/odcsim/odcsim-engine-omega/src/test/kotlin/com/atlarge/odcsim/engine/omega/OmegaActorSystemTest.kt new file mode 100644 index 00000000..dc310d47 --- /dev/null +++ b/odcsim/odcsim-engine-omega/src/test/kotlin/com/atlarge/odcsim/engine/omega/OmegaActorSystemTest.kt @@ -0,0 +1,37 @@ +/* + * MIT License + * + * Copyright (c) 2018 atlarge-research + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package com.atlarge.odcsim.engine.omega + +import com.atlarge.odcsim.ActorSystem +import com.atlarge.odcsim.engine.tests.ActorSystemContract +import org.junit.jupiter.api.DisplayName + +/** + * The [ActorSystem] test suite for the Omega engine implementation. + */ +@DisplayName("OmegaActorSystem") +class OmegaActorSystemTest : ActorSystemContract() { + override val factory = OmegaActorSystemFactory() +} diff --git a/odcsim/odcsim-engine-tests/build.gradle.kts b/odcsim/odcsim-engine-tests/build.gradle.kts new file mode 100644 index 00000000..7b236300 --- /dev/null +++ b/odcsim/odcsim-engine-tests/build.gradle.kts @@ -0,0 +1,40 @@ +/* + * MIT License + * + * Copyright (c) 2019 atlarge-research + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +/* Build configuration */ +plugins { + `kotlin-library-convention` +} + +/* Project configuration */ +repositories { + jcenter() +} + +dependencies { + api(project(":odcsim:odcsim-core")) + + implementation(kotlin("stdlib")) + implementation("org.junit.jupiter:junit-jupiter-api:${Library.JUNIT_JUPITER}") +} diff --git a/odcsim/odcsim-engine-tests/src/main/kotlin/com/atlarge/odcsim/engine/tests/ActorSystemContract.kt b/odcsim/odcsim-engine-tests/src/main/kotlin/com/atlarge/odcsim/engine/tests/ActorSystemContract.kt new file mode 100644 index 00000000..593f587b --- /dev/null +++ b/odcsim/odcsim-engine-tests/src/main/kotlin/com/atlarge/odcsim/engine/tests/ActorSystemContract.kt @@ -0,0 +1,403 @@ +/* + * MIT License + * + * Copyright (c) 2019 atlarge-research + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package com.atlarge.odcsim.engine.tests + +import com.atlarge.odcsim.ActorPath +import com.atlarge.odcsim.ActorRef +import com.atlarge.odcsim.ActorSystemFactory +import com.atlarge.odcsim.Behavior +import com.atlarge.odcsim.Terminated +import com.atlarge.odcsim.coroutines.dsl.timeout +import com.atlarge.odcsim.coroutines.suspending +import com.atlarge.odcsim.empty +import com.atlarge.odcsim.ignore +import com.atlarge.odcsim.receiveMessage +import com.atlarge.odcsim.receiveSignal +import com.atlarge.odcsim.same +import com.atlarge.odcsim.setup +import com.atlarge.odcsim.stopped +import org.junit.jupiter.api.Assertions.assertEquals +import org.junit.jupiter.api.Assertions.assertTrue +import org.junit.jupiter.api.DisplayName +import org.junit.jupiter.api.Nested +import org.junit.jupiter.api.Test +import org.junit.jupiter.api.assertThrows + +/** + * A conformance test suite for implementors of the [ActorSystem] interface. + */ +abstract class ActorSystemContract { + /** + * An [ActorSystemFactory] provided by implementors to create the [ActorSystem] to be tested. + */ + abstract val factory: ActorSystemFactory + + /** + * Test whether the created [ActorSystem] has the correct name. + */ + @Test + fun `should have a name`() { + val name = "test" + val system = factory(empty(), name) + + assertEquals(name, system.name) + system.terminate() + } + + /** + * Test whether the created [ActorSystem] has a path. + */ + @Test + fun `should have a path`() { + val system = factory(empty(), "test") + + assertTrue(system.path is ActorPath.Root) + system.terminate() + } + + /** + * Test whether creating an [ActorSystem] sets the initial time at 0. + */ + @Test + fun `should start at t=0`() { + val system = factory(empty(), name = "test") + + assertEquals(.0, system.time, DELTA) + system.terminate() + } + + /** + * Test whether an [ActorSystem] does not accept invalid points in time. + */ + @Test + fun `should not accept negative instants for running`() { + val system = factory(empty(), name = "test") + assertThrows { system.run(-10.0) } + system.terminate() + } + + /** + * Test whether an [ActorSystem] will not jump backward in time when asking to run until a specified instant + * that has already occurred. + */ + @Test + fun `should not jump backward in time`() { + val until = 10.0 + val system = factory(empty(), name = "test") + + system.run(until = until) + system.run(until = until - 0.5) + assertEquals(until, system.time, DELTA) + system.terminate() + } + + /** + * Test whether an [ActorSystem] will jump forward in time when asking to run until a specified instant. + */ + @Test + fun `should jump forward in time`() { + val until = 10.0 + val system = factory(empty(), name = "test") + + system.run(until = until) + assertEquals(until, system.time, DELTA) + system.terminate() + } + + /** + * Test whether an [ActorSystem] will jump forward in time when asking to run until a specified instant. + */ + @Test + fun `should order messages at the instant by insertion time`() { + val behavior = receiveMessage { msg -> + assertEquals(1, msg) + receiveMessage { + assertEquals(2, it) + ignore() + } + } + val system = factory(behavior, name = "test") + system.send(1, after = 1.0) + system.send(2, after = 1.0) + system.run(until = 10.0) + system.terminate() + } + + /** + * Test whether an [ActorSystem] will not process messages in the queue after the deadline. + */ + @Test + fun `should not process messages after deadline`() { + var counter = 0 + val behavior = receiveMessage { _ -> + counter++ + same() + } + val system = factory(behavior, name = "test") + system.send(Unit, after = 3.0) + system.send(Unit, after = 1.0) + system.run(until = 2.0) + assertEquals(1, counter) + system.terminate() + } + + /** + * Test whether an [ActorSystem] will not initialize the root actor if the system has not been run yet. + */ + @Test + fun `should not initialize root actor if not run`() { + val system = factory(setup { TODO() }, name = "test") + system.terminate() + } + + @Nested + @DisplayName("ActorRef") + inner class ActorRefTest { + /** + * Test whether an [ActorSystem] disallows sending messages in the past. + */ + @Test + fun `should disallow messages in the past`() { + val system = factory(empty(), name = "test") + assertThrows { system.send(Unit, after = -1.0) } + system.terminate() + } + } + + @Nested + @DisplayName("Actor") + inner class Actor { + /** + * Test whether the pre-start time of the root actor is at 0. + */ + @Test + fun `should pre-start at t=0 if root`() { + val behavior = setup { ctx -> + assertEquals(.0, ctx.time, DELTA) + ignore() + } + + val system = factory(behavior, "test") + system.run() + system.terminate() + } + + /** + * Test whether a child actor can be created from an actor. + */ + @Test + fun `should allow spawning of child actors`() { + var spawned = false + val behavior = setup { spawned = true; empty() } + + val system = factory(setup { ctx -> + val ref = ctx.spawn(behavior, "child") + assertEquals("child", ref.path.name) + ignore() + }, name = "test") + + system.run(until = 10.0) + assertTrue(spawned) + system.terminate() + } + + /** + * Test whether a child actor can be stopped from an actor. + */ + @Test + fun `should allow stopping of child actors`() { + val system = factory(setup { ctx -> + val ref = ctx.spawn(receiveMessage { throw UnsupportedOperationException() }, "child") + ctx.stop(ref) + assertEquals("child", ref.path.name) + ignore() + }, name = "test") + + system.run(until = 10.0) + system.terminate() + } + + /** + * Test whether only the parent of a child can terminate it. + */ + @Test + fun `should only be able to terminate child actors`() { + val system = factory(setup { ctx1 -> + val child1 = ctx1.spawn(ignore(), "child-1") + ctx1.spawn(setup { ctx2 -> + ctx2.stop(child1) + ignore() + }, "child-2") + + ignore() + }, name = "test") + system.run() + system.terminate() + } + + /** + * Test whether stopping a child is idempotent. + */ + @Test + fun `should be able to stop a child twice`() { + val system = factory(setup { ctx -> + val child = ctx.spawn(ignore(), "child") + ctx.stop(child) + ctx.stop(child) + ignore() + }, name = "test") + system.run() + system.terminate() + } + + /** + * Test whether termination of a child also results in termination of its children. + */ + @Test + fun `should terminate children of child when terminating it`() { + val system = factory(setup> { ctx -> + val root = ctx.self + val child = ctx.spawn(setup { + val child = it.spawn(receiveMessage { + throw IllegalStateException("DELIBERATE") + }, "child") + ctx.send(root, child) + ignore() + }, "child") + + receiveMessage { msg -> + ctx.stop(child) + ctx.send(msg, Unit) // This actor should be stopped now and not receive the message anymore + stopped() + } + }, name = "test") + + system.run() + system.terminate() + } + + /** + * Test whether [same] works correctly. + */ + @Test + fun `should keep same behavior on same`() { + var counter = 0 + + val behavior = setup { ctx -> + counter++ + ctx.send(ctx.self, Unit) + receiveMessage { + counter++ + same() + } + } + + val system = factory(behavior, "test") + system.run() + assertEquals(2, counter) + system.terminate() + } + + /** + * Test whether the reference to the actor itself is valid. + */ + @Test + fun `should have reference to itself`() { + var flag = false + val behavior: Behavior = setup { ctx -> + ctx.send(ctx.self, Unit) + receiveMessage { flag = true; same() } + } + + val system = factory(behavior, "test") + system.run() + assertTrue(flag) + system.terminate() + } + + /** + * Test whether we can start an actor with the [stopped] behavior. + */ + @Test + fun `should start with stopped behavior`() { + val system = factory(stopped(), "test") + system.run() + system.terminate() + } + + /** + * Test whether an actor that is crashed cannot receive more messages. + */ + @Test + fun `should stop if it crashes`() { + var counter = 0 + val system = factory(receiveMessage { + counter++ + throw IllegalArgumentException("STAGED") + }, "test") + + system.send(Unit) + system.send(Unit) + + system.run() + assertEquals(1, counter) + system.terminate() + } + + /** + * Test whether an actor can watch for termination. + */ + @Test + fun `should watch for termination`() { + var received = false + val system = factory(setup { ctx -> + val child = ctx.spawn(suspending { + timeout(50.0) + stopped() + }, "child") + ctx.watch(child) + + receiveSignal { _, signal -> + when (signal) { + is Terminated -> { + received = true + stopped() + } + else -> + same() + } + } + }, "test") + + system.run() + system.terminate() + assertTrue(received) + } + } + + companion object { + private const val DELTA: Double = 0.0001 + } +} diff --git a/odcsim/odcsim-engine-tests/src/main/kotlin/com/atlarge/odcsim/engine/tests/ActorSystemFactoryContract.kt b/odcsim/odcsim-engine-tests/src/main/kotlin/com/atlarge/odcsim/engine/tests/ActorSystemFactoryContract.kt new file mode 100644 index 00000000..565f4f4c --- /dev/null +++ b/odcsim/odcsim-engine-tests/src/main/kotlin/com/atlarge/odcsim/engine/tests/ActorSystemFactoryContract.kt @@ -0,0 +1,73 @@ +/* + * MIT License + * + * Copyright (c) 2019 atlarge-research + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package com.atlarge.odcsim.engine.tests + +import com.atlarge.odcsim.ActorSystemFactory +import com.atlarge.odcsim.empty +import com.atlarge.odcsim.setup +import com.atlarge.odcsim.stopped +import org.junit.jupiter.api.Assertions.assertEquals +import org.junit.jupiter.api.Assertions.assertTrue +import org.junit.jupiter.api.Test + +/** + * A conformance test suite for implementors of the [ActorSystemFactory] interface. + */ +abstract class ActorSystemFactoryContract { + /** + * Create an [ActorSystemFactory] instance to test. + */ + abstract fun createFactory(): ActorSystemFactory + + /** + * Test whether the factory will create an [ActorSystem] with correct name. + */ + @Test + fun `should create a system with correct name`() { + val factory = createFactory() + val name = "test" + val system = factory(empty(), name) + + assertEquals(name, system.name) + system.terminate() + } + + /** + * Test whether the factory will create an [ActorSystem] with valid root behavior. + */ + @Test + fun `should create a system with correct root behavior`() { + var flag = false + val factory = createFactory() + val system = factory(setup { + flag = true + stopped() + }, "test") + + system.run(until = 10.0) + system.terminate() + assertTrue(flag) + } +} diff --git a/opendc-core/build.gradle.kts b/opendc-core/build.gradle.kts deleted file mode 100644 index c89e27e6..00000000 --- a/opendc-core/build.gradle.kts +++ /dev/null @@ -1,51 +0,0 @@ -/* - * MIT License - * - * Copyright (c) 2018 atlarge-research - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -/* Build configuration */ -apply(from = "../gradle/kotlin.gradle") -plugins { - `java-library` -} - -/* Project configuration */ -repositories { - jcenter() -} - -val junitJupiterVersion: String by extra -val junitPlatformVersion: String by extra - -dependencies { - api(project(":odcsim-core")) - - implementation(kotlin("stdlib")) - - testImplementation(project(":odcsim-testkit")) - testImplementation(project(":odcsim-engine-omega")) - testImplementation("org.junit.jupiter:junit-jupiter-api:$junitJupiterVersion") - testRuntimeOnly("org.junit.jupiter:junit-jupiter-engine:$junitJupiterVersion") - testImplementation("org.junit.platform:junit-platform-launcher:$junitPlatformVersion") - testRuntimeOnly("org.slf4j:slf4j-simple:1.7.25") - testImplementation("com.nhaarman.mockitokotlin2:mockito-kotlin:2.0.0") -} diff --git a/opendc-core/src/main/kotlin/com/atlarge/opendc/model/Broker.kt b/opendc-core/src/main/kotlin/com/atlarge/opendc/model/Broker.kt deleted file mode 100644 index 0e2adb40..00000000 --- a/opendc-core/src/main/kotlin/com/atlarge/opendc/model/Broker.kt +++ /dev/null @@ -1,40 +0,0 @@ -/* - * MIT License - * - * Copyright (c) 2019 atlarge-research - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -package com.atlarge.opendc.model - -import com.atlarge.odcsim.Behavior - -/** - * A broker acting on the various cloud platforms on behalf of the user. - */ -interface Broker { - /** - * Build the runtime behavior of the [Broker]. - * - * @param platforms A list of available cloud platforms. - * @return The runtime behavior of the broker. - */ - operator fun invoke(platforms: List): Behavior<*> -} diff --git a/opendc-core/src/main/kotlin/com/atlarge/opendc/model/Cluster.kt b/opendc-core/src/main/kotlin/com/atlarge/opendc/model/Cluster.kt deleted file mode 100644 index 1ed11e87..00000000 --- a/opendc-core/src/main/kotlin/com/atlarge/opendc/model/Cluster.kt +++ /dev/null @@ -1,56 +0,0 @@ -/* - * MIT License - * - * Copyright (c) 2019 atlarge-research - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -package com.atlarge.opendc.model - -import com.atlarge.odcsim.Behavior -import com.atlarge.odcsim.empty -import com.atlarge.odcsim.setup -import com.atlarge.odcsim.unsafeCast -import com.atlarge.opendc.model.resources.compute.host.Host -import com.atlarge.opendc.model.services.resources.ResourceManagerRef -import java.util.UUID - -/** - * A logical grouping of heterogeneous hosts and primary storage within a zone. - * - * @property uid The unique identifier of the cluster. - * @property name The name of this cluster. - * @property hosts The machines in this cluster. - */ -data class Cluster(override val uid: UUID, override val name: String, val hosts: List) : Identity { - /** - * Build the runtime [Behavior] of this cluster of hosts. - * - * @param manager The manager of the cluster. - */ - operator fun invoke(manager: ResourceManagerRef): Behavior = setup { ctx -> - // Launch all hosts in the cluster - for (host in hosts) { - ctx.spawn(host(manager.unsafeCast()), name = host.name) - } - - empty() - } -} diff --git a/opendc-core/src/main/kotlin/com/atlarge/opendc/model/Environment.kt b/opendc-core/src/main/kotlin/com/atlarge/opendc/model/Environment.kt deleted file mode 100644 index b7fe33db..00000000 --- a/opendc-core/src/main/kotlin/com/atlarge/opendc/model/Environment.kt +++ /dev/null @@ -1,36 +0,0 @@ -/* - * MIT License - * - * Copyright (c) 2019 atlarge-research - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -package com.atlarge.opendc.model - -/** - * A description of a large-scale computing environment. This description includes including key size and topology - * information of the environment, types of resources, but also various operational and management rules such as - * scheduled maintenance, allocation and other constraints. - * - * @property name The name of the environment. - * @property description A small textual description about the environment that is being modeled - * @property platforms The cloud platforms (such as AWS or GCE) in this environment. - */ -data class Environment(val name: String, val description: String?, val platforms: List) diff --git a/opendc-core/src/main/kotlin/com/atlarge/opendc/model/Identity.kt b/opendc-core/src/main/kotlin/com/atlarge/opendc/model/Identity.kt deleted file mode 100644 index 2455d138..00000000 --- a/opendc-core/src/main/kotlin/com/atlarge/opendc/model/Identity.kt +++ /dev/null @@ -1,42 +0,0 @@ -/* - * MIT License - * - * Copyright (c) 2019 atlarge-research - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -package com.atlarge.opendc.model - -import java.util.UUID - -/** - * An object that has a unique identity. - */ -interface Identity { - /** - * A unique, opaque, system-generated value, representing the object. - */ - val uid: UUID - - /** - * A non-empty, human-readable string representing the object. - */ - val name: String -} diff --git a/opendc-core/src/main/kotlin/com/atlarge/opendc/model/Model.kt b/opendc-core/src/main/kotlin/com/atlarge/opendc/model/Model.kt deleted file mode 100644 index 098bfbde..00000000 --- a/opendc-core/src/main/kotlin/com/atlarge/opendc/model/Model.kt +++ /dev/null @@ -1,71 +0,0 @@ -/* - * MIT License - * - * Copyright (c) 2019 atlarge-research - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -package com.atlarge.opendc.model - -import com.atlarge.odcsim.ActorRef -import com.atlarge.odcsim.Behavior -import com.atlarge.odcsim.Terminated -import com.atlarge.odcsim.receiveSignal -import com.atlarge.odcsim.same -import com.atlarge.odcsim.setup -import com.atlarge.odcsim.stopped -import com.atlarge.odcsim.unhandled - -/** - * A simulation model for large-scale simulation of datacenter infrastructure, built with the *odcsim* API. - * - * @property environment The environment in which brokers operate. - * @property brokers The brokers acting on the cloud platforms. - */ -data class Model(val environment: Environment, val brokers: List) { - /** - * Build the runtime behavior of the universe. - */ - operator fun invoke(): Behavior = setup { ctx -> - // Setup the environment - val platforms = environment.platforms.map { platform -> - ctx.spawn(platform(), name = platform.name) - } - - // Launch all brokers - val remaining = mutableSetOf>() - for (broker in brokers) { - val ref = ctx.spawnAnonymous(broker(platforms)) - ctx.watch(ref) - remaining += ref - } - - receiveSignal { _, signal -> - when (signal) { - is Terminated -> { - remaining -= signal.ref - if (remaining.isEmpty()) stopped() else same() - } - else -> - unhandled() - } - } - } -} diff --git a/opendc-core/src/main/kotlin/com/atlarge/opendc/model/Platform.kt b/opendc-core/src/main/kotlin/com/atlarge/opendc/model/Platform.kt deleted file mode 100644 index 129ee3a9..00000000 --- a/opendc-core/src/main/kotlin/com/atlarge/opendc/model/Platform.kt +++ /dev/null @@ -1,103 +0,0 @@ -/* - * MIT License - * - * Copyright (c) 2019 atlarge-research - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -package com.atlarge.opendc.model - -import com.atlarge.odcsim.ActorRef -import com.atlarge.odcsim.Behavior -import com.atlarge.odcsim.coroutines.actorContext -import com.atlarge.odcsim.coroutines.dsl.ask -import com.atlarge.odcsim.receiveMessage -import com.atlarge.odcsim.same -import com.atlarge.odcsim.setup -import java.util.UUID - -/** - * A representation of a cloud platform such as Amazon Web Services (AWS), Microsoft Azure or Google Cloud. - * - * @property uid The unique identifier of this datacenter. - * @property name the name of the platform. - * @property zones The availability zones available on this platform. - */ -data class Platform(override val uid: UUID, override val name: String, val zones: List) : Identity { - /** - * Build the runtime [Behavior] of this cloud platform. - */ - operator fun invoke(): Behavior = setup { ctx -> - ctx.log.info("Starting cloud platform {} [{}] with {} zones", name, uid, zones.size) - - // Launch all zones of the cloud platform - val zoneInstances = zones.associateWith { zone -> - ctx.spawn(zone(), name = zone.name) - } - - receiveMessage { msg -> - when (msg) { - is PlatformMessage.ListZones -> { - ctx.send(msg.replyTo, PlatformResponse.Zones(ctx.self, zoneInstances.mapKeys { it.key.name })) - same() - } - } - } - } -} - -/** - * A reference to the actor managing the zone. - */ -typealias PlatformRef = ActorRef - -/** - * A message protocol for communicating with a cloud platform. - */ -sealed class PlatformMessage { - /** - * Request the available zones on this platform. - * - * @property replyTo The actor address to send the response to. - */ - data class ListZones(val replyTo: ActorRef) : PlatformMessage() -} - -/** - * A message protocol used by platform actors to respond to [PlatformMessage]s. - */ -sealed class PlatformResponse { - /** - * The zones available on this cloud platform. - * - * @property platform The reference to the cloud platform these are the zones of. - * @property zones The zones in this cloud platform. - */ - data class Zones(val platform: PlatformRef, val zones: Map) : PlatformResponse() -} - -/** - * Retrieve the available zones of a platform. - */ -suspend fun PlatformRef.zones(): Map { - val ctx = actorContext() - val zones: PlatformResponse.Zones = ctx.ask(this) { PlatformMessage.ListZones(it) } - return zones.zones -} diff --git a/opendc-core/src/main/kotlin/com/atlarge/opendc/model/User.kt b/opendc-core/src/main/kotlin/com/atlarge/opendc/model/User.kt deleted file mode 100644 index 502f7a74..00000000 --- a/opendc-core/src/main/kotlin/com/atlarge/opendc/model/User.kt +++ /dev/null @@ -1,35 +0,0 @@ -/* - * MIT License - * - * Copyright (c) 2019 atlarge-research - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -package com.atlarge.opendc.model - -/** - * A user of the cloud network. - */ -interface User : Identity { - /** - * The name of the user. - */ - override val name: String -} diff --git a/opendc-core/src/main/kotlin/com/atlarge/opendc/model/Zone.kt b/opendc-core/src/main/kotlin/com/atlarge/opendc/model/Zone.kt deleted file mode 100644 index b1d00dac..00000000 --- a/opendc-core/src/main/kotlin/com/atlarge/opendc/model/Zone.kt +++ /dev/null @@ -1,212 +0,0 @@ -/* - * MIT License - * - * Copyright (c) 2019 atlarge-research - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -package com.atlarge.opendc.model - -import com.atlarge.odcsim.ActorContext -import com.atlarge.odcsim.ActorRef -import com.atlarge.odcsim.Behavior -import com.atlarge.odcsim.ReceivingBehavior -import com.atlarge.odcsim.Signal -import com.atlarge.odcsim.Terminated -import com.atlarge.odcsim.coroutines.actorContext -import com.atlarge.odcsim.coroutines.dsl.ask -import com.atlarge.odcsim.same -import com.atlarge.odcsim.setup -import com.atlarge.odcsim.unhandled -import com.atlarge.opendc.model.services.Service -import com.atlarge.opendc.model.services.ServiceProvider -import java.util.ArrayDeque -import java.util.UUID - -/** - * An isolated location within a datacenter region from which public cloud services operate, roughly equivalent to a - * single datacenter. Zones contain one or more clusters and secondary storage. - * - * This class models *only* the static information of a zone, with dynamic information being contained within the zone's - * actor. During runtime, it's actor acts as a registry for all the cloud services provided by the zone. - * - * @property uid The unique identifier of this availability zone - * @property name The name of the zone within its platform. - * @property services The initial set of services provided by the zone. - * @property clusters The clusters of machines in this zone. - */ -data class Zone( - override val uid: UUID, - override val name: String, - val services: Set, - val clusters: List -) : Identity { - /** - * Build the runtime [Behavior] of this datacenter. - */ - operator fun invoke(): Behavior = setup { ctx -> - ctx.log.info("Starting zone {} [{}]", name, uid) - - // Launch all services of the zone - val instances: MutableMap, ActorRef<*>> = mutableMapOf() - validateDependencies(services) - - for (provider in services) { - val ref = ctx.spawn(provider(this, ctx.self), name = "${provider.name}-${provider.uid}") - ctx.watch(ref) - provider.provides.forEach { instances[it] = ref } - } - - object : ReceivingBehavior() { - override fun receive(ctx: ActorContext, msg: ZoneMessage): Behavior { - return when (msg) { - is ZoneMessage.Find -> { - ctx.send(msg.replyTo, ZoneResponse.Listing(ctx.self, msg.key, instances[msg.key])) - same() - } - } - } - - override fun receiveSignal(ctx: ActorContext, signal: Signal): Behavior { - return when (signal) { - is Terminated -> { - instances.values.remove(signal.ref) - same() - } - else -> - unhandled() - } - } - } - } - - /** - * Validate the service for unsatisfiable dependencies. - */ - private fun validateDependencies(providers: Set) { - val providersByKey = HashMap, ServiceProvider>() - for (provider in providers) { - if (provider.provides.isEmpty()) { - throw IllegalArgumentException(("Service provider $provider does not provide any service.")) - } - for (key in provider.provides) { - if (key in providersByKey) { - throw IllegalArgumentException("Multiple providers for service $key") - } - providersByKey[key] = provider - } - } - - val visited = HashSet() - val queue = ArrayDeque(providers) - while (queue.isNotEmpty()) { - val service = queue.poll() - visited.add(service) - - for (dependencyKey in service.dependencies) { - val dependency = providersByKey[dependencyKey] - ?: throw IllegalArgumentException("Dependency $dependencyKey not satisfied for service $service") - if (dependency !in visited) { - queue.add(dependency) - } - } - } - } - - override fun equals(other: Any?): Boolean = other is Zone && uid == other.uid - override fun hashCode(): Int = uid.hashCode() -} - -/** - * A reference to the actor managing the zone. - */ -typealias ZoneRef = ActorRef - -/** - * A message protocol for communicating with the service registry - */ -sealed class ZoneMessage { - /** - * Lookup the specified service in this availability zone. - * - * @property key The key of the service to lookup. - * @property replyTo The address to reply to. - */ - data class Find( - val key: Service<*>, - val replyTo: ActorRef - ) : ZoneMessage() -} - -/** - * A message protocol used by service registry actors to respond to [ZoneMessage]s. - */ -sealed class ZoneResponse { - /** - * The response sent when looking up services in a zone. - * - * @property zone The zone from which the response originates. - * @property key The key of the service that was looked up. - * @property ref The reference to the service or `null` if it is not present in the zone. - */ - data class Listing( - val zone: ZoneRef, - val key: Service<*>, - private val ref: ActorRef<*>? - ) : ZoneResponse() { - /** - * A flag to indicate whether the service is present. - */ - val isPresent: Boolean - get() = ref != null - - /** - * Determine whether this listing is for the specified key. - * - * @param key The key to check for. - * @return `true` if the listing is for this key, `false` otherwise. - */ - fun isForKey(key: Service<*>): Boolean = key == this.key - - /** - * Extract the result from the service lookup. - * - * @param key The key of the lookup. - * @return The reference to the service or `null` if it is not present in the zone. - */ - operator fun invoke(key: Service): ActorRef? { - require(this.key == key) { "Invalid key" } - @Suppress("UNCHECKED_CAST") - return ref as? ActorRef - } - } -} - -/** - * Find the reference to the specified [ServiceProvider]. - * - * @param key The key of the service to find. - * @throws IllegalArgumentException if the service is not found. - */ -suspend fun ZoneRef.find(key: Service): ActorRef { - val ctx = actorContext() - val listing: ZoneResponse.Listing = ctx.ask(this) { ZoneMessage.Find(key, it) } - return listing(key) ?: throw IllegalArgumentException("Unknown key $key") -} diff --git a/opendc-core/src/main/kotlin/com/atlarge/opendc/model/resources/compute/Machine.kt b/opendc-core/src/main/kotlin/com/atlarge/opendc/model/resources/compute/Machine.kt deleted file mode 100644 index 08d94e14..00000000 --- a/opendc-core/src/main/kotlin/com/atlarge/opendc/model/resources/compute/Machine.kt +++ /dev/null @@ -1,105 +0,0 @@ -/* - * MIT License - * - * Copyright (c) 2019 atlarge-research - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -package com.atlarge.opendc.model.resources.compute - -import com.atlarge.odcsim.ActorRef -import com.atlarge.odcsim.Behavior -import com.atlarge.opendc.model.Identity -import com.atlarge.opendc.model.resources.compute.supervision.MachineSupervisionEvent -import com.atlarge.opendc.model.workload.application.Application -import com.atlarge.opendc.model.workload.application.Pid - -/** - * A generic representation of a compute node (either physical or virtual) that is able to run [Application]s. - */ -interface Machine : Identity { - /** - * The details of the machine in key/value pairs. - */ - val details: Map - - /** - * Build the runtime [Behavior] of this compute resource, accepting messages of [MachineMessage]. - * - * @param supervisor The supervisor of the machine. - */ - operator fun invoke(supervisor: ActorRef): Behavior -} - -/** - * A reference to a machine instance that accepts messages of type [MachineMessage]. - */ -typealias MachineRef = ActorRef - -/** - * A message protocol for communicating with machine instances. - */ -sealed class MachineMessage { - /** - * Launch the specified [Application] on the machine instance. - * - * @property application The application to submit. - * @property key The key to identify this submission. - * @property broker The broker of the process to spawn. - */ - data class Submit( - val application: Application, - val key: Any, - val broker: ActorRef - ) : MachineMessage() -} - -/** - * A message protocol used by machine instances to respond to [MachineMessage]s. - */ -sealed class MachineEvent { - /** - * Indicate that an [Application] was spawned on a machine instance. - * - * @property instance The machine instance to which the application was submitted. - * @property application The application that has been submitted. - * @property key The key used to identify the submission. - * @property pid The spawned application instance. - */ - data class Submitted( - val instance: MachineRef, - val application: Application, - val key: Any, - val pid: Pid - ) : MachineEvent() - - /** - * Indicate that an [Application] has terminated on the specified machine. - * - * @property instance The machine instance to which the application was submitted. - * @property pid The reference to the application instance that has terminated. - * @property status The exit code of the task, where zero means successful. - */ - data class Terminated( - val instance: MachineRef, - val pid: Pid, - val status: Int = 0 - ) : MachineEvent() -} diff --git a/opendc-core/src/main/kotlin/com/atlarge/opendc/model/resources/compute/MachineStatus.kt b/opendc-core/src/main/kotlin/com/atlarge/opendc/model/resources/compute/MachineStatus.kt deleted file mode 100644 index f4fe9494..00000000 --- a/opendc-core/src/main/kotlin/com/atlarge/opendc/model/resources/compute/MachineStatus.kt +++ /dev/null @@ -1,33 +0,0 @@ -/* - * MIT License - * - * Copyright (c) 2019 atlarge-research - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -package com.atlarge.opendc.model.resources.compute - -/** - * The status of a machine. - */ -enum class MachineStatus { - HALT, - RUNNING -} diff --git a/opendc-core/src/main/kotlin/com/atlarge/opendc/model/resources/compute/ProcessingElement.kt b/opendc-core/src/main/kotlin/com/atlarge/opendc/model/resources/compute/ProcessingElement.kt deleted file mode 100644 index 9e5b1b71..00000000 --- a/opendc-core/src/main/kotlin/com/atlarge/opendc/model/resources/compute/ProcessingElement.kt +++ /dev/null @@ -1,33 +0,0 @@ -/* - * MIT License - * - * Copyright (c) 2019 atlarge-research - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -package com.atlarge.opendc.model.resources.compute - -/** - * A logical core in a CPU. - * - * @property id The identifier of the core within the CPU. - * @property unit The [ProcessingUnit] the core is part of. - */ -data class ProcessingElement(val id: Int, val unit: ProcessingUnit) diff --git a/opendc-core/src/main/kotlin/com/atlarge/opendc/model/resources/compute/ProcessingUnit.kt b/opendc-core/src/main/kotlin/com/atlarge/opendc/model/resources/compute/ProcessingUnit.kt deleted file mode 100644 index 025087a4..00000000 --- a/opendc-core/src/main/kotlin/com/atlarge/opendc/model/resources/compute/ProcessingUnit.kt +++ /dev/null @@ -1,44 +0,0 @@ -/* - * MIT License - * - * Copyright (c) 2019 atlarge-research - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -package com.atlarge.opendc.model.resources.compute - -/** - * A processing unit of a compute resource, either virtual or physical. - * - * @property vendor The vendor string of the cpu. - * @property family The cpu family number. - * @property model The model number of the cpu. - * @property modelName The name of the cpu model. - * @property clockRate The clock speed of the cpu in MHz. - * @property cores The number of logical cores in the cpu. - */ -data class ProcessingUnit( - val vendor: String, - val family: Int, - val model: Int, - val modelName: String, - val clockRate: Double, - val cores: Int -) diff --git a/opendc-core/src/main/kotlin/com/atlarge/opendc/model/resources/compute/host/Host.kt b/opendc-core/src/main/kotlin/com/atlarge/opendc/model/resources/compute/host/Host.kt deleted file mode 100644 index ca7ea204..00000000 --- a/opendc-core/src/main/kotlin/com/atlarge/opendc/model/resources/compute/host/Host.kt +++ /dev/null @@ -1,86 +0,0 @@ -/* - * MIT License - * - * Copyright (c) 2019 atlarge-research - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -package com.atlarge.opendc.model.resources.compute.host - -import com.atlarge.odcsim.ActorRef -import com.atlarge.odcsim.Behavior -import com.atlarge.odcsim.join -import com.atlarge.odcsim.receiveMessage -import com.atlarge.odcsim.same -import com.atlarge.odcsim.setup -import com.atlarge.odcsim.unhandled -import com.atlarge.odcsim.withTimers -import com.atlarge.opendc.model.resources.compute.Machine -import com.atlarge.opendc.model.resources.compute.MachineMessage -import com.atlarge.opendc.model.resources.compute.ProcessingElement -import com.atlarge.opendc.model.resources.compute.scheduling.MachineScheduler -import com.atlarge.opendc.model.resources.compute.supervision.MachineSupervisionEvent -import com.atlarge.opendc.model.workload.application.Application -import com.atlarge.opendc.model.workload.application.ProcessSupervisor -import java.util.UUID - -/** - * A physical compute node in a datacenter that is able to run [Application]s. - * - * @property uid The unique identifier of this machine. - * @property name The name of the machine. - * @property scheduler The process scheduler of this machine. - * @property cores The list of processing elements in the machine. - * @property details The details of this host. - */ -data class Host( - override val uid: UUID, - override val name: String, - val scheduler: MachineScheduler, - val cores: List, - override val details: Map = emptyMap() -) : Machine { - /** - * Build the [Behavior] for a physical machine. - */ - override fun invoke(supervisor: ActorRef): Behavior = setup { ctx -> - ctx.send(supervisor, MachineSupervisionEvent.Announce(this, ctx.self)) - ctx.send(supervisor, MachineSupervisionEvent.Up(ctx.self)) - - withTimers { timers -> - val sched = scheduler(this, ctx, timers) - sched.updateResources(cores) - receiveMessage { msg -> - when (msg) { - is MachineMessage.Submit -> { - sched.submit(msg.application, msg.key, msg.broker) - same() - } - else -> - unhandled() - } - }.join(ProcessSupervisor(sched).unsafeCast()).narrow() - } - } - - override fun equals(other: Any?): Boolean = other is Machine && uid == other.uid - - override fun hashCode(): Int = uid.hashCode() -} diff --git a/opendc-core/src/main/kotlin/com/atlarge/opendc/model/resources/compute/scheduling/MachineScheduler.kt b/opendc-core/src/main/kotlin/com/atlarge/opendc/model/resources/compute/scheduling/MachineScheduler.kt deleted file mode 100644 index de14f0fe..00000000 --- a/opendc-core/src/main/kotlin/com/atlarge/opendc/model/resources/compute/scheduling/MachineScheduler.kt +++ /dev/null @@ -1,48 +0,0 @@ -/* - * MIT License - * - * Copyright (c) 2019 atlarge-research - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -package com.atlarge.opendc.model.resources.compute.scheduling - -import com.atlarge.odcsim.ActorContext -import com.atlarge.odcsim.TimerScheduler -import com.atlarge.opendc.model.resources.compute.Machine -import com.atlarge.opendc.model.resources.compute.MachineMessage - -/** - * A factory interface for constructing a [MachineSchedulerLogic]. - */ -interface MachineScheduler { - /** - * Construct a [MachineSchedulerLogic] in the given [ActorContext]. - * - * @param machine The machine to create the scheduler for. - * @param ctx The actor context to construct a scheduler for. - * @param scheduler The timer scheduler to use. - */ - operator fun invoke( - machine: Machine, - ctx: ActorContext, - scheduler: TimerScheduler - ): MachineSchedulerLogic -} diff --git a/opendc-core/src/main/kotlin/com/atlarge/opendc/model/resources/compute/scheduling/MachineSchedulerLogic.kt b/opendc-core/src/main/kotlin/com/atlarge/opendc/model/resources/compute/scheduling/MachineSchedulerLogic.kt deleted file mode 100644 index 879d6ed8..00000000 --- a/opendc-core/src/main/kotlin/com/atlarge/opendc/model/resources/compute/scheduling/MachineSchedulerLogic.kt +++ /dev/null @@ -1,64 +0,0 @@ -/* - * MIT License - * - * Copyright (c) 2019 atlarge-research - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -package com.atlarge.opendc.model.resources.compute.scheduling - -import com.atlarge.odcsim.ActorContext -import com.atlarge.odcsim.ActorRef -import com.atlarge.odcsim.TimerScheduler -import com.atlarge.opendc.model.resources.compute.Machine -import com.atlarge.opendc.model.resources.compute.MachineEvent -import com.atlarge.opendc.model.resources.compute.MachineMessage -import com.atlarge.opendc.model.resources.compute.ProcessingElement -import com.atlarge.opendc.model.workload.application.Application -import com.atlarge.opendc.model.workload.application.ProcessSupervisor - -/** - * A scheduler that distributes processes over processing elements in a machine. - * - * @property machine The machine to create the scheduler for. - * @property ctx The context in which the scheduler runs. - * @property scheduler The timer scheduler to use. - */ -abstract class MachineSchedulerLogic( - protected val machine: Machine, - protected val ctx: ActorContext, - protected val scheduler: TimerScheduler -) : ProcessSupervisor { - /** - * Update the available resources in the machine. - * - * @param cores The available processing cores for the scheduler. - */ - abstract fun updateResources(cores: List) - - /** - * Submit the specified application for scheduling. - * - * @param application The application to submit. - * @param key The key to identify the application instance. - * @param handler The broker of this application. - */ - abstract fun submit(application: Application, key: Any, handler: ActorRef) -} diff --git a/opendc-core/src/main/kotlin/com/atlarge/opendc/model/resources/compute/scheduling/ProcessObserver.kt b/opendc-core/src/main/kotlin/com/atlarge/opendc/model/resources/compute/scheduling/ProcessObserver.kt deleted file mode 100644 index 11262d79..00000000 --- a/opendc-core/src/main/kotlin/com/atlarge/opendc/model/resources/compute/scheduling/ProcessObserver.kt +++ /dev/null @@ -1,68 +0,0 @@ -/* - * MIT License - * - * Copyright (c) 2019 atlarge-research - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -package com.atlarge.opendc.model.resources.compute.scheduling - -import com.atlarge.odcsim.Behavior -import com.atlarge.odcsim.receiveMessage -import com.atlarge.odcsim.same -import com.atlarge.opendc.model.resources.compute.MachineEvent -import com.atlarge.opendc.model.resources.compute.MachineRef -import com.atlarge.opendc.model.workload.application.Application -import com.atlarge.opendc.model.workload.application.Pid - -/** - * An interface for observing processes. - */ -interface ProcessObserver { - /** - * This method is invoked when the setup of an application completed successfully. - * - * @param pid The process id of the process that has been initialized. - */ - fun onSubmission(instance: MachineRef, application: Application, key: Any, pid: Pid) - - /** - * This method is invoked when a process exits. - * - * @property pid A reference to the application instance. - * @property status The exit code of the task, where zero means successful. - */ - fun onTermination(instance: MachineRef, pid: Pid, status: Int) - - companion object { - /** - * Create the [Behavior] for a [ProcessObserver]. - * - * @param observer The observer to create the behavior for. - */ - operator fun invoke(observer: ProcessObserver): Behavior = receiveMessage { msg -> - when (msg) { - is MachineEvent.Submitted -> observer.onSubmission(msg.instance, msg.application, msg.key, msg.pid) - is MachineEvent.Terminated -> observer.onTermination(msg.instance, msg.pid, msg.status) - } - same() - } - } -} diff --git a/opendc-core/src/main/kotlin/com/atlarge/opendc/model/resources/compute/scheduling/ProcessState.kt b/opendc-core/src/main/kotlin/com/atlarge/opendc/model/resources/compute/scheduling/ProcessState.kt deleted file mode 100644 index 4c685fc4..00000000 --- a/opendc-core/src/main/kotlin/com/atlarge/opendc/model/resources/compute/scheduling/ProcessState.kt +++ /dev/null @@ -1,50 +0,0 @@ -/* - * MIT License - * - * Copyright (c) 2019 atlarge-research - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -package com.atlarge.opendc.model.resources.compute.scheduling - -/** - * An enumeration of the distinct states of an application instance (process). - */ -enum class ProcessState { - /** - * Default state of a process, where the task is waiting to be assigned and installed on a machine. - */ - CREATED, - - /** - * State to indicate that the process is waiting to be ran. - */ - READY, - - /** - * State to indicate that the process is currently running. - */ - RUNNING, - - /** - * State to indicate that the process has been terminated, either successfully or due to failure. - */ - TERMINATED, -} diff --git a/opendc-core/src/main/kotlin/com/atlarge/opendc/model/resources/compute/scheduling/ProcessView.kt b/opendc-core/src/main/kotlin/com/atlarge/opendc/model/resources/compute/scheduling/ProcessView.kt deleted file mode 100644 index bc88e01f..00000000 --- a/opendc-core/src/main/kotlin/com/atlarge/opendc/model/resources/compute/scheduling/ProcessView.kt +++ /dev/null @@ -1,53 +0,0 @@ -/* - * MIT License - * - * Copyright (c) 2019 atlarge-research - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -package com.atlarge.opendc.model.resources.compute.scheduling - -import com.atlarge.odcsim.ActorRef -import com.atlarge.opendc.model.resources.compute.MachineEvent -import com.atlarge.opendc.model.workload.application.Application -import com.atlarge.opendc.model.workload.application.Pid -import com.atlarge.opendc.model.workload.application.ProcessMessage - -/** - * A process represents a application instance running on a particular machine from the machine scheduler's point of - * view. - * - * @property application The application this is an instance of. - * @property broker The broker of the process, which is informed about its progress. - * @property pid The reference to the application instance. - * @property state The state of the process. - */ -data class ProcessView( - val application: Application, - val broker: ActorRef, - val pid: Pid, - var state: ProcessState = ProcessState.CREATED -) { - /** - * The slice of processing elements allocated for the process. Available as soon as the state - * becomes [ProcessState.RUNNING] - */ - lateinit var allocation: ProcessMessage.Allocation -} diff --git a/opendc-core/src/main/kotlin/com/atlarge/opendc/model/resources/compute/scheduling/SpaceSharedMachineScheduler.kt b/opendc-core/src/main/kotlin/com/atlarge/opendc/model/resources/compute/scheduling/SpaceSharedMachineScheduler.kt deleted file mode 100644 index 457c7070..00000000 --- a/opendc-core/src/main/kotlin/com/atlarge/opendc/model/resources/compute/scheduling/SpaceSharedMachineScheduler.kt +++ /dev/null @@ -1,178 +0,0 @@ -/* - * MIT License - * - * Copyright (c) 2019 atlarge-research - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -package com.atlarge.opendc.model.resources.compute.scheduling - -import com.atlarge.odcsim.ActorContext -import com.atlarge.odcsim.ActorRef -import com.atlarge.odcsim.Instant -import com.atlarge.odcsim.TimerScheduler -import com.atlarge.odcsim.unsafeCast -import com.atlarge.opendc.model.resources.compute.Machine -import com.atlarge.opendc.model.resources.compute.MachineEvent -import com.atlarge.opendc.model.resources.compute.MachineMessage -import com.atlarge.opendc.model.resources.compute.ProcessingElement -import com.atlarge.opendc.model.workload.application.Application -import com.atlarge.opendc.model.workload.application.Pid -import com.atlarge.opendc.model.workload.application.ProcessMessage -import com.atlarge.opendc.model.workload.application.ProcessSupervisor -import java.util.ArrayDeque -import java.util.UUID - -/** - * A machine scheduling policy where processes are space-shared on the machine. - * - * Space-sharing for machine scheduling means that all running processes will be allocated a separate set of the - * [ProcessingElement]s in a [Machine]. Applications are scheduled on the machine in first-in-first-out (FIFO) order, - * thus larger applications may block smaller tasks from proceeding, while space is available (no backfilling). - * - * @property machine The machine to create the scheduler for. - * @property ctx The context in which the scheduler runs. - * @property scheduler The timer scheduler to use. - */ -class SpaceSharedMachineScheduler( - machine: Machine, - ctx: ActorContext, - scheduler: TimerScheduler -) : MachineSchedulerLogic(machine, ctx, scheduler), ProcessSupervisor { - private var cores = 0 - private val available = ArrayDeque() - private val queue = ArrayDeque>() - private val running = LinkedHashSet>() - private val processes = HashMap, ProcessView>() - - override fun updateResources(cores: List) { - available.addAll(cores) - this.cores = cores.size - - // Add all running tasks in front of the queue - running.reversed().forEach { queue.addFirst(it) } - running.clear() - - reschedule() - } - - override fun submit(application: Application, key: Any, handler: ActorRef) { - // Create application instance on the machine - val pid = ctx.spawn(application(), name = application.name + ":" + application.uid + ":" + UUID.randomUUID().toString()) - processes[pid] = ProcessView(application, handler, pid) - - // Setup the task - ctx.send(pid, ProcessMessage.Setup(machine, ctx.self.unsafeCast())) - - // Inform the owner that the task has been submitted - ctx.send(handler, MachineEvent.Submitted(ctx.self, application, key, pid)) - } - - /** - * Reschedule the tasks on this machine. - */ - private fun reschedule() { - while (queue.isNotEmpty()) { - val pid = queue.peek() - val process = processes[pid]!! - - if (process.application.cores >= cores) { - // The task will never fit on the machine - // TODO Fail task - ctx.log.warn("Process {} will not fit in machine: dropping.", process) - queue.remove() - return - } else if (process.application.cores > available.size) { - // The task will not fit at the moment - // Try again if resources become available - ctx.log.debug("Application queued: not enough processing elements available [requested={}, available={}]", - process.application.cores, available.size) - return - } - queue.remove() - - // Compute the available resources - val resources = List(process.application.cores) { - val pe = available.poll() - Pair(pe, 1.0) - }.toMap() - process.state = ProcessState.RUNNING - process.allocation = ProcessMessage.Allocation(resources, Instant.POSITIVE_INFINITY) - running += pid - - ctx.send(pid, process.allocation) - } - } - - override fun onReady(pid: Pid) { - val process = processes[pid]!! - - // Schedule the task if it has been setup - queue.add(pid) - process.state = ProcessState.READY - - reschedule() - } - - override fun onConsume(pid: Pid, utilization: Map, until: Instant) { - val process = processes[pid]!! - val allocation = process.allocation - - if (until > allocation.until) { - // Tasks are not allowed to extend allocation provided by the machine - // TODO Fail the task - ctx.log.warn("Task {} must not extend allocation provided by the machine", pid) - } else if (until < allocation.until) { - // Shrink allocation - process.allocation = allocation.copy(until = until) - } - - // Reschedule the process after the allocation expires - scheduler.after(pid, process.allocation.until - ctx.time) { - // We just extend the allocation - process.allocation = process.allocation.copy(until = Instant.POSITIVE_INFINITY) - ctx.send(pid, process.allocation) - } - } - - override fun onExit(pid: Pid, status: Int) { - val process = processes.remove(pid)!! - running -= pid - scheduler.cancel(pid) - process.allocation.resources.keys.forEach { available.add(it) } - - ctx.log.debug("Application {} terminated with status {}", pid, status) - - // Inform the owner that the task has terminated - ctx.send(process.broker, MachineEvent.Terminated(ctx.self, pid, status)) - - reschedule() - } - - companion object : MachineScheduler { - override fun invoke( - machine: Machine, - ctx: ActorContext, - scheduler: TimerScheduler - ): MachineSchedulerLogic { - return SpaceSharedMachineScheduler(machine, ctx, scheduler) - } - } -} diff --git a/opendc-core/src/main/kotlin/com/atlarge/opendc/model/resources/compute/supervision/MachineSupervisionEvent.kt b/opendc-core/src/main/kotlin/com/atlarge/opendc/model/resources/compute/supervision/MachineSupervisionEvent.kt deleted file mode 100644 index 2b3fae3d..00000000 --- a/opendc-core/src/main/kotlin/com/atlarge/opendc/model/resources/compute/supervision/MachineSupervisionEvent.kt +++ /dev/null @@ -1,49 +0,0 @@ -/* - * MIT License - * - * Copyright (c) 2019 atlarge-research - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -package com.atlarge.opendc.model.resources.compute.supervision - -import com.atlarge.opendc.model.resources.compute.Machine -import com.atlarge.opendc.model.resources.compute.MachineRef - -/** - * A supervision protocol for [Machine] instances. - */ -sealed class MachineSupervisionEvent { - /** - * Initialization message to introduce to the supervisor a new machine by specifying its static information and - * address. - * - * @property machine The machine that is being announced. - * @property ref The address to talk to the host. - */ - data class Announce(val machine: Machine, val ref: MachineRef) : MachineSupervisionEvent() - - /** - * Indicate that the specified machine has booted up. - * - * @property ref The address to talk to the machine. - */ - data class Up(val ref: MachineRef) : MachineSupervisionEvent() -} diff --git a/opendc-core/src/main/kotlin/com/atlarge/opendc/model/resources/compute/supervision/MachineSupervisor.kt b/opendc-core/src/main/kotlin/com/atlarge/opendc/model/resources/compute/supervision/MachineSupervisor.kt deleted file mode 100644 index c3607a22..00000000 --- a/opendc-core/src/main/kotlin/com/atlarge/opendc/model/resources/compute/supervision/MachineSupervisor.kt +++ /dev/null @@ -1,67 +0,0 @@ -/* - * MIT License - * - * Copyright (c) 2019 atlarge-research - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -package com.atlarge.opendc.model.resources.compute.supervision - -import com.atlarge.odcsim.Behavior -import com.atlarge.odcsim.receiveMessage -import com.atlarge.odcsim.same -import com.atlarge.opendc.model.resources.compute.Machine -import com.atlarge.opendc.model.resources.compute.MachineRef - -/** - * An interface for supervising [Machine] instances. - */ -interface MachineSupervisor { - /** - * This method is invoked when a new machine is introduced to the supervisor by specifying its static information - * and address. - * - * @param machine The machine that is being announced. - * @param ref The address to talk to the host. - */ - fun onAnnounce(machine: Machine, ref: MachineRef) - - /** - * This method is invoked when a process exits. - * - * @param ref The address to talk to the machine. - */ - fun onUp(ref: MachineRef) - - companion object { - /** - * Create the [Behavior] for a [MachineSupervisor]. - * - * @param supervisor The supervisor to create the behavior for. - */ - operator fun invoke(supervisor: MachineSupervisor): Behavior = receiveMessage { msg -> - when (msg) { - is MachineSupervisionEvent.Announce -> supervisor.onAnnounce(msg.machine, msg.ref) - is MachineSupervisionEvent.Up -> supervisor.onUp(msg.ref) - } - same() - } - } -} diff --git a/opendc-core/src/main/kotlin/com/atlarge/opendc/model/services/Service.kt b/opendc-core/src/main/kotlin/com/atlarge/opendc/model/services/Service.kt deleted file mode 100644 index e8b25b88..00000000 --- a/opendc-core/src/main/kotlin/com/atlarge/opendc/model/services/Service.kt +++ /dev/null @@ -1,47 +0,0 @@ -/* - * MIT License - * - * Copyright (c) 2019 atlarge-research - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -package com.atlarge.opendc.model.services - -import com.atlarge.opendc.model.Identity -import java.util.UUID - -/** - * An interface for identifying service implementations of the same type (providing the same service). - * - * @param T The shape of the messages the service responds to. - */ -interface Service : Identity - -/** - * Helper class for constructing a [Service]. - * - * @property uid The unique identifier of the service. - * @property name The name of the service. - */ -abstract class AbstractService(override val uid: UUID, override val name: String) : Service { - override fun equals(other: Any?): Boolean = other is Service<*> && uid == other.uid - override fun hashCode(): Int = uid.hashCode() - override fun toString(): String = "Service[uid=$uid,name=$name]" -} diff --git a/opendc-core/src/main/kotlin/com/atlarge/opendc/model/services/ServiceMap.kt b/opendc-core/src/main/kotlin/com/atlarge/opendc/model/services/ServiceMap.kt deleted file mode 100644 index d91208bf..00000000 --- a/opendc-core/src/main/kotlin/com/atlarge/opendc/model/services/ServiceMap.kt +++ /dev/null @@ -1,49 +0,0 @@ -/* - * MIT License - * - * Copyright (c) 2019 atlarge-research - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -package com.atlarge.opendc.model.services - -import com.atlarge.odcsim.ActorRef - -/** - * A map containing services. - */ -interface ServiceMap { - /** - * Determine if this map contains the service with the specified [Service]. - * - * @param key The key of the service to check for. - * @return `true` if the service is in the map, `false` otherwise. - */ - operator fun contains(key: Service<*>): Boolean - - /** - * Obtain the service with the specified [Service]. - * - * @param key The key of the service to obtain. - * @return The references to the service. - * @throws IllegalArgumentException if the key does not exists in the map. - */ - operator fun get(key: Service): ActorRef -} diff --git a/opendc-core/src/main/kotlin/com/atlarge/opendc/model/services/ServiceProvider.kt b/opendc-core/src/main/kotlin/com/atlarge/opendc/model/services/ServiceProvider.kt deleted file mode 100644 index 1bf5b22e..00000000 --- a/opendc-core/src/main/kotlin/com/atlarge/opendc/model/services/ServiceProvider.kt +++ /dev/null @@ -1,64 +0,0 @@ -/* - * MIT License - * - * Copyright (c) 2019 atlarge-research - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -package com.atlarge.opendc.model.services - -import com.atlarge.odcsim.Behavior -import com.atlarge.opendc.model.Identity -import com.atlarge.opendc.model.Zone -import com.atlarge.opendc.model.ZoneRef -import java.util.UUID - -/** - * An abstract representation of a cloud service implementation provided by a cloud platform. - */ -interface ServiceProvider : Identity { - /** - * The unique identifier of the service implementation. - */ - override val uid: UUID - - /** - * The name of the service implementation. - */ - override val name: String - - /** - * The set of services provided by this [ServiceProvider]. - */ - val provides: Set> - - /** - * The dependencies of the service implementation. - */ - val dependencies: Set> - - /** - * Build the runtime [Behavior] for this service. - * - * @param zone The zone model for which the service should be build. - * @param ref The runtime reference to the zone's actor for communication. - */ - operator fun invoke(zone: Zone, ref: ZoneRef): Behavior<*> -} diff --git a/opendc-core/src/main/kotlin/com/atlarge/opendc/model/services/provisioning/ProvisioningService.kt b/opendc-core/src/main/kotlin/com/atlarge/opendc/model/services/provisioning/ProvisioningService.kt deleted file mode 100644 index 22b70f35..00000000 --- a/opendc-core/src/main/kotlin/com/atlarge/opendc/model/services/provisioning/ProvisioningService.kt +++ /dev/null @@ -1,77 +0,0 @@ -/* - * MIT License - * - * Copyright (c) 2019 atlarge-research - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -package com.atlarge.opendc.model.services.provisioning - -import com.atlarge.odcsim.ActorRef -import com.atlarge.opendc.model.Zone -import com.atlarge.opendc.model.services.AbstractService -import com.atlarge.opendc.model.services.Service -import com.atlarge.opendc.model.services.ServiceProvider -import com.atlarge.opendc.model.services.resources.HostView -import java.util.UUID - -/** - * A cloud platform service that provisions the native resources on the platform. - * - * This service assumes control over all hosts in its [Zone]. - */ -abstract class ProvisioningService : ServiceProvider { - override val provides: Set> = setOf(ProvisioningService) - - /** - * The service key of the provisioner service. - */ - companion object : AbstractService(UUID.randomUUID(), "provisioner") -} - -/** - * A message protocol for communicating to the resource provisioner. - */ -sealed class ProvisioningMessage { - /** - * Request the specified number of resources from the provisioner. - * - * @property numHosts The number of hosts to request from the provisioner. - * @property replyTo The actor to reply to. - */ - data class Request(val numHosts: Int, val replyTo: ActorRef) : ProvisioningMessage() - - /** - * Release the specified resource [ProvisioningResponse.Lease]. - * - * @property lease The lease to release. - */ - data class Release(val lease: ProvisioningResponse.Lease) : ProvisioningMessage() -} - -/** - * A message protocol used by the resource provisioner to respond to [ProvisioningMessage]s. - */ -sealed class ProvisioningResponse { - /** - * A lease for the specified hosts. - */ - data class Lease(val hosts: List) : ProvisioningResponse() -} diff --git a/opendc-core/src/main/kotlin/com/atlarge/opendc/model/services/provisioning/SimpleProvisioningService.kt b/opendc-core/src/main/kotlin/com/atlarge/opendc/model/services/provisioning/SimpleProvisioningService.kt deleted file mode 100644 index 1ddc1b69..00000000 --- a/opendc-core/src/main/kotlin/com/atlarge/opendc/model/services/provisioning/SimpleProvisioningService.kt +++ /dev/null @@ -1,125 +0,0 @@ -/* - * MIT License - * - * Copyright (c) 2019 atlarge-research - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -package com.atlarge.opendc.model.services.provisioning - -import com.atlarge.odcsim.Behavior -import com.atlarge.odcsim.StashBuffer -import com.atlarge.odcsim.receiveMessage -import com.atlarge.odcsim.same -import com.atlarge.odcsim.setup -import com.atlarge.odcsim.unhandled -import com.atlarge.odcsim.unsafeCast -import com.atlarge.opendc.model.Zone -import com.atlarge.opendc.model.ZoneMessage -import com.atlarge.opendc.model.ZoneRef -import com.atlarge.opendc.model.ZoneResponse -import com.atlarge.opendc.model.resources.compute.MachineRef -import com.atlarge.opendc.model.services.Service -import com.atlarge.opendc.model.services.resources.HostView -import com.atlarge.opendc.model.services.resources.ResourceManagementMessage -import com.atlarge.opendc.model.services.resources.ResourceManagementResponse -import com.atlarge.opendc.model.services.resources.ResourceManagementService -import com.atlarge.opendc.model.services.resources.ResourceManagerRef -import java.util.ArrayDeque -import java.util.UUID - -/** - * A cloud platform service that provisions the native resources on the platform. - * - * This service assumes control over all hosts in its [Zone]. - */ -object SimpleProvisioningService : ProvisioningService() { - override val uid: UUID = UUID.randomUUID() - override val name: String = "simple-provisioner" - override val dependencies: Set> = setOf(ResourceManagementService) - - /** - * Build the runtime [Behavior] for the resource provisioner, responding to messages of shape [ProvisioningMessage]. - */ - override fun invoke(zone: Zone, ref: ZoneRef): Behavior = setup { ctx -> - val buffer = StashBuffer(capacity = 30) - ctx.send(ref, ZoneMessage.Find(ResourceManagementService, ctx.self.unsafeCast())) - - receiveMessage { msg -> - when (msg) { - is ZoneResponse.Listing -> { - val service = msg(ResourceManagementService) ?: throw IllegalStateException("Resource management service not available") - buffer.unstashAll(ctx.unsafeCast(), active(zone, service).unsafeCast()) - } - else -> { - buffer.stash(msg) - same() - } - } - }.narrow() - } - - private fun active(zone: Zone, manager: ResourceManagerRef) = setup { ctx -> - val hosts = mutableMapOf() - val available = ArrayDeque() - val leases = mutableSetOf() - - // Subscribe to all machines in the zone - for (cluster in zone.clusters) { - for (host in cluster.hosts) { - ctx.send(manager, ResourceManagementMessage.Lookup(host, ctx.self.unsafeCast())) - } - } - - receiveMessage { msg -> - when (msg) { - is ProvisioningMessage.Request -> { - ctx.log.info("Provisioning {} hosts", msg.numHosts) - val leaseHosts = mutableListOf() - while (available.isNotEmpty() && leaseHosts.size < msg.numHosts) { - leaseHosts += available.poll() - } - val lease = ProvisioningResponse.Lease(leaseHosts) - leases += lease - ctx.send(msg.replyTo, lease) - same() - } - is ProvisioningMessage.Release -> { - val lease = msg.lease - if (lease in leases) { - return@receiveMessage same() - } - available.addAll(lease.hosts) - leases -= lease - same() - } - is ResourceManagementResponse.Listing -> { - if (msg.instance != null) { - hosts[msg.instance.ref] = msg.instance - available.add(msg.instance) - } - same() - } - else -> - unhandled() - } - }.narrow() - } -} diff --git a/opendc-core/src/main/kotlin/com/atlarge/opendc/model/services/resources/HostView.kt b/opendc-core/src/main/kotlin/com/atlarge/opendc/model/services/resources/HostView.kt deleted file mode 100644 index 943461cd..00000000 --- a/opendc-core/src/main/kotlin/com/atlarge/opendc/model/services/resources/HostView.kt +++ /dev/null @@ -1,42 +0,0 @@ -/* - * MIT License - * - * Copyright (c) 2019 atlarge-research - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -package com.atlarge.opendc.model.services.resources - -import com.atlarge.opendc.model.resources.compute.MachineRef -import com.atlarge.opendc.model.resources.compute.MachineStatus -import com.atlarge.opendc.model.resources.compute.host.Host - -/** - * The dynamic information of a [Host] instance that is being tracked by the [ResourceManagementService]. This means - * that information may not be up-to-date. - * - * @property host The static information of the host. - * @property ref The reference to the host's actor. - * @property status The status of the machine. - */ -data class HostView(val host: Host, val ref: MachineRef, val status: MachineStatus = MachineStatus.HALT) { - override fun equals(other: Any?): Boolean = other is HostView && host.uid == other.host.uid - override fun hashCode(): Int = host.uid.hashCode() -} diff --git a/opendc-core/src/main/kotlin/com/atlarge/opendc/model/services/resources/ResourceManagementService.kt b/opendc-core/src/main/kotlin/com/atlarge/opendc/model/services/resources/ResourceManagementService.kt deleted file mode 100644 index 5e38c6da..00000000 --- a/opendc-core/src/main/kotlin/com/atlarge/opendc/model/services/resources/ResourceManagementService.kt +++ /dev/null @@ -1,121 +0,0 @@ -/* - * MIT License - * - * Copyright (c) 2019 atlarge-research - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -package com.atlarge.opendc.model.services.resources - -import com.atlarge.odcsim.ActorRef -import com.atlarge.odcsim.Behavior -import com.atlarge.odcsim.receiveMessage -import com.atlarge.odcsim.same -import com.atlarge.odcsim.setup -import com.atlarge.odcsim.unhandled -import com.atlarge.opendc.model.Zone -import com.atlarge.opendc.model.ZoneRef -import com.atlarge.opendc.model.resources.compute.MachineRef -import com.atlarge.opendc.model.resources.compute.MachineStatus -import com.atlarge.opendc.model.resources.compute.host.Host -import com.atlarge.opendc.model.resources.compute.supervision.MachineSupervisionEvent -import com.atlarge.opendc.model.services.Service -import com.atlarge.opendc.model.services.ServiceProvider -import java.util.UUID - -/** - * A cloud platform service that manages the native resources on the platform. - * - * This service assumes control over all hosts in its [Zone]. - */ -object ResourceManagementService : ServiceProvider, Service { - override val uid: UUID = UUID.randomUUID() - override val name: String = "resource-manager" - override val provides: Set> = setOf(ResourceManagementService) - override val dependencies: Set> = emptySet() - - /** - * Build the runtime behavior of the [ResourceManagementService]. - */ - override fun invoke(zone: Zone, ref: ZoneRef): Behavior = setup { ctx -> - // Launch the clusters of the zone - for (cluster in zone.clusters) { - ctx.spawn(cluster(ctx.self), name = "${cluster.name}-${cluster.uid}") - } - - val hosts = mutableMapOf() - receiveMessage { msg -> - when (msg) { - is MachineSupervisionEvent.Announce -> { - val host = msg.machine as? Host - if (host != null) { - hosts[msg.ref] = HostView(host, msg.ref) - } - same() - } - is MachineSupervisionEvent.Up -> { - hosts.computeIfPresent(msg.ref) { _, value -> - value.copy(status = MachineStatus.RUNNING) - } - same() - } - is ResourceManagementMessage.Lookup -> { - ctx.send(msg.replyTo, ResourceManagementResponse.Listing(hosts.values.find { it.host == msg.host })) - same() - } - else -> - unhandled() - } - }.narrow() - } -} - -/** - * A reference to the resource manager of a zone. - */ -typealias ResourceManagerRef = ActorRef - -/** - * A message protocol for communicating to the resource manager. - */ -sealed class ResourceManagementMessage { - /** - * Lookup the specified [Host]. - * - * @property host The host to lookup. - * @property replyTo The address to sent the response to. - */ - data class Lookup( - val host: Host, - val replyTo: ActorRef - ) : ResourceManagementMessage() -} - -/** - * A message protocol used by the resource manager to respond to [ResourceManagementMessage]s. - */ -sealed class ResourceManagementResponse { - /** - * A response to a [ResourceManagementMessage.Lookup] request. - * - * @property instance The instance that was found or `null` if it does not exist. - */ - data class Listing(val instance: HostView?) : ResourceManagementResponse() -} diff --git a/opendc-core/src/main/kotlin/com/atlarge/opendc/model/workload/Workload.kt b/opendc-core/src/main/kotlin/com/atlarge/opendc/model/workload/Workload.kt deleted file mode 100644 index c1215715..00000000 --- a/opendc-core/src/main/kotlin/com/atlarge/opendc/model/workload/Workload.kt +++ /dev/null @@ -1,39 +0,0 @@ -/* - * MIT License - * - * Copyright (c) 2019 atlarge-research - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -package com.atlarge.opendc.model.workload - -import com.atlarge.opendc.model.Identity -import com.atlarge.opendc.model.User - -/** - * A high-level abstraction that represents the actual work that a set of compute resources perform, such - * as running an application on a machine or a whole workflow running multiple tasks on numerous machines. - */ -interface Workload : Identity { - /** - * The owner of this workload. - */ - val owner: User -} diff --git a/opendc-core/src/main/kotlin/com/atlarge/opendc/model/workload/application/Application.kt b/opendc-core/src/main/kotlin/com/atlarge/opendc/model/workload/application/Application.kt deleted file mode 100644 index 00ab98b6..00000000 --- a/opendc-core/src/main/kotlin/com/atlarge/opendc/model/workload/application/Application.kt +++ /dev/null @@ -1,47 +0,0 @@ -/* - * MIT License - * - * Copyright (c) 2019 atlarge-research - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -package com.atlarge.opendc.model.workload.application - -import com.atlarge.odcsim.Behavior -import com.atlarge.opendc.model.workload.Workload - -/** - * A generic representation of a workload that can directly be executed by physical or virtual compute resources, - * such as a web server application. - */ -interface Application : Workload { - /** - * The number of processing elements required by the task. - */ - val cores: Int - - /** - * Build the runtime [Behavior] of an application, accepting messages of [ProcessMessage]. - * - * This is a model for the runtime behavior of an application instance (process) that describes how an application - * instance consumes the allocated resources on a machine. - */ - operator fun invoke(): Behavior -} diff --git a/opendc-core/src/main/kotlin/com/atlarge/opendc/model/workload/application/FlopsApplication.kt b/opendc-core/src/main/kotlin/com/atlarge/opendc/model/workload/application/FlopsApplication.kt deleted file mode 100644 index 60a896d4..00000000 --- a/opendc-core/src/main/kotlin/com/atlarge/opendc/model/workload/application/FlopsApplication.kt +++ /dev/null @@ -1,164 +0,0 @@ -/* - * MIT License - * - * Copyright (c) 2019 atlarge-research - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -package com.atlarge.opendc.model.workload.application - -import com.atlarge.odcsim.ActorContext -import com.atlarge.odcsim.ActorRef -import com.atlarge.odcsim.Behavior -import com.atlarge.odcsim.DeferredBehavior -import com.atlarge.odcsim.Instant -import com.atlarge.odcsim.receive -import com.atlarge.odcsim.stopped -import com.atlarge.odcsim.unhandled -import com.atlarge.opendc.model.User -import com.atlarge.opendc.model.resources.compute.Machine -import com.atlarge.opendc.model.resources.compute.ProcessingElement -import java.lang.Double.min -import java.util.UUID -import kotlin.math.ceil - -/** - * An [Application] implementation that models applications performing a static number of floating point operations - * ([flops]) on a compute resource. - * - * @property uid A unique identifier for this application. - * @property name The name of the application. - * @property owner The owner of this application. - * @property cores The number of cores needed for this application. - * @property flops The number of floating point operations to perform for this task. - */ -class FlopsApplication( - override val uid: UUID, - override val name: String, - override val owner: User, - override val cores: Int, - val flops: Long -) : Application { - - init { - require(flops >= 0) { "Negative number of flops" } - } - - /** - * Build the runtime [Behavior] based on a number of floating point operations to execute. - */ - override fun invoke(): Behavior = object : DeferredBehavior() { - /** - * The remaining number of floating point operations to execute. - */ - var remaining = flops - - /** - * The machine to which the task is assigned. - */ - lateinit var machine: Machine - - /** - * The reference to the machine instance. - */ - lateinit var ref: ActorRef - - /** - * The start of the last allocation - */ - var start: Instant = 0.0 - - /** - * The given resource allocation. - */ - lateinit var allocation: Map - - override fun invoke(ctx: ActorContext) = created() - - /** - * Handle the initial, created state of a task instance. - */ - private fun created(): Behavior = receive { ctx, msg -> - when (msg) { - is ProcessMessage.Setup -> { - machine = msg.machine - ref = msg.ref - /* TODO implement setup time */ - ctx.send(ref, ProcessEvent.Ready(ctx.self)) - ready().narrow() - } - else -> unhandled() - } - } - - /** - * Handle the ready state of a task instance. - */ - private fun ready(): Behavior = receive { ctx, msg -> - when (msg) { - is ProcessMessage.Allocation -> { - processAllocation(ctx, msg.resources, msg.until) - running() - } - else -> unhandled() - } - } - - /** - * Handle the running state of a task instance. - */ - private fun running(): Behavior = receive { ctx, msg -> - when (msg) { - is ProcessMessage.Allocation -> { - /* Compute the consumption of flops */ - val consumed = allocation.asSequence() - .map { (key, value) -> key.unit.clockRate * value * (ctx.time - start) } - .sum() - // Ceil to prevent consumed flops being rounded to 0 - remaining -= ceil(consumed).toLong() - - /* Test whether all flops have been consumed and the task is finished */ - if (remaining <= 0) { - ctx.send(ref, ProcessEvent.Exit(ctx.self, 0)) - return@receive stopped() - } - - processAllocation(ctx, msg.resources, msg.until) - running().narrow() - } - else -> unhandled() - } - } - - private fun processAllocation(ctx: ActorContext, resources: Map, until: Instant) { - start = ctx.time - allocation = resources - .asSequence() - .take(cores) - .associateBy({ it.key }, { it.value }) - - val speed = allocation.asSequence() - .map { (key, value) -> key.unit.clockRate * value } - .average() - val finishedAt = ctx.time + remaining / speed - ctx.send(ref, ProcessEvent.Consume(ctx.self, allocation, min(finishedAt, until))) - } - } -} diff --git a/opendc-core/src/main/kotlin/com/atlarge/opendc/model/workload/application/Process.kt b/opendc-core/src/main/kotlin/com/atlarge/opendc/model/workload/application/Process.kt deleted file mode 100644 index a78b8572..00000000 --- a/opendc-core/src/main/kotlin/com/atlarge/opendc/model/workload/application/Process.kt +++ /dev/null @@ -1,91 +0,0 @@ -/* - * MIT License - * - * Copyright (c) 2019 atlarge-research - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -package com.atlarge.opendc.model.workload.application - -import com.atlarge.odcsim.ActorRef -import com.atlarge.odcsim.Instant -import com.atlarge.opendc.model.resources.compute.Machine -import com.atlarge.opendc.model.resources.compute.ProcessingElement - -/** - * The process id (pid) is a reference to the application instance (process) that accepts messages of - * type [ProcessMessage]. - */ -typealias Pid = ActorRef - -/** - * A message protocol for actors to communicate with task instances (called processes). - */ -sealed class ProcessMessage { - /** - * Indicate that the task should be installed to the specified machine. - * - * @property machine The machine to install the task. - * @property ref The reference to the machine instance. - */ - data class Setup(val machine: Machine, val ref: ActorRef) : ProcessMessage() - - /** - * Indicate an allocation of compute resources on a machine for a certain duration. - * The task may assume that the reservation occurs after installation on the same machine. - * - * @property resources The cpu cores (and the utilization percentages) allocated for the task. - * @property until The point in time till which the reservation is valid. - */ - data class Allocation(val resources: Map, val until: Instant) : ProcessMessage() -} - -/** - * The message protocol used by application instances respond to [ProcessMessage]s. - */ -sealed class ProcessEvent { - /** - * Indicate that the process is ready to start processing. - * - * @property pid A reference to the application instance. - */ - data class Ready(val pid: Pid) : ProcessEvent() - - /** - * Indicate the estimated resource utilization of the task until a specified point in time. - * - * @property pid A reference to the application instance of the represented utilization. - * @property utilization The utilization of the cpu cores as a percentage. - * @property until The point in time until which the utilization is valid. - */ - data class Consume( - val pid: Pid, - val utilization: Map, - val until: Instant - ) : ProcessEvent() - - /** - * Indicate that a process has been terminated. - * - * @property pid A reference to the application instance. - * @property status The exit code of the task, where zero means successful. - */ - data class Exit(val pid: Pid, val status: Int) : ProcessEvent() -} diff --git a/opendc-core/src/main/kotlin/com/atlarge/opendc/model/workload/application/ProcessSupervisor.kt b/opendc-core/src/main/kotlin/com/atlarge/opendc/model/workload/application/ProcessSupervisor.kt deleted file mode 100644 index 2952c82e..00000000 --- a/opendc-core/src/main/kotlin/com/atlarge/opendc/model/workload/application/ProcessSupervisor.kt +++ /dev/null @@ -1,79 +0,0 @@ -/* - * MIT License - * - * Copyright (c) 2019 atlarge-research - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -package com.atlarge.opendc.model.workload.application - -import com.atlarge.odcsim.Behavior -import com.atlarge.odcsim.Instant -import com.atlarge.odcsim.receiveMessage -import com.atlarge.odcsim.same -import com.atlarge.opendc.model.resources.compute.ProcessingElement - -/** - * An interface for supervising processes. - */ -interface ProcessSupervisor { - /** - * This method is invoked when the setup of an application completed successfully. - * - * @param pid The process id of the process that has been initialized. - */ - fun onReady(pid: Pid) {} - - /** - * This method is invoked when a process informs the machine that it is running with the - * estimated resource utilization until a specified point in time. - * - * @param pid The process id of the process that is running. - * @param utilization The utilization of the cpu cores as a percentage. - * @param until The point in time until which the utilization is valid. - */ - fun onConsume(pid: Pid, utilization: Map, until: Instant) {} - - /** - * This method is invoked when a process exits. - * - * @property pid A reference to the application instance. - * @property status The exit code of the task, where zero means successful. - */ - fun onExit(pid: Pid, status: Int) {} - - companion object { - /** - * Create the [Behavior] for a [ProcessSupervisor]. - * - * @param supervisor The supervisor to create the behavior for. - */ - operator fun invoke(supervisor: ProcessSupervisor): Behavior { - return receiveMessage { msg -> - when (msg) { - is ProcessEvent.Ready -> supervisor.onReady(msg.pid) - is ProcessEvent.Consume -> supervisor.onConsume(msg.pid, msg.utilization, msg.until) - is ProcessEvent.Exit -> supervisor.onExit(msg.pid, msg.status) - } - same() - } - } - } -} diff --git a/opendc-core/src/test/kotlin/com/atlarge/opendc/model/workload/application/FlopsApplicationTest.kt b/opendc-core/src/test/kotlin/com/atlarge/opendc/model/workload/application/FlopsApplicationTest.kt deleted file mode 100644 index 2cde1b6f..00000000 --- a/opendc-core/src/test/kotlin/com/atlarge/opendc/model/workload/application/FlopsApplicationTest.kt +++ /dev/null @@ -1,128 +0,0 @@ -/* - * MIT License - * - * Copyright (c) 2019 atlarge-research - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -package com.atlarge.opendc.model.workload.application - -import com.atlarge.odcsim.testkit.BehaviorTestKit -import com.atlarge.opendc.model.User -import com.atlarge.opendc.model.resources.compute.Machine -import com.atlarge.opendc.model.resources.compute.ProcessingElement -import com.atlarge.opendc.model.resources.compute.ProcessingUnit -import com.nhaarman.mockitokotlin2.mock -import org.junit.jupiter.api.Assertions.assertFalse -import org.junit.jupiter.api.Assertions.assertTrue -import org.junit.jupiter.api.BeforeEach -import org.junit.jupiter.api.DisplayName -import org.junit.jupiter.api.Test -import java.util.UUID - -/** - * A test suite for the [FlopsApplication]. - */ -@DisplayName("FlopsApplication") -internal class FlopsApplicationTest { - private val flops = 10000L - private val cores = 2 - private val machine: Machine = mock() - private val user: User = mock() - private val cpu: ProcessingUnit = ProcessingUnit("Intel", 6, 8600, "Intel(R) Core(TM) i7-6920HQ CPU @ 2.90GHz", 2900.0, 1) - - private lateinit var application: FlopsApplication - - @BeforeEach - fun setUp() { - application = FlopsApplication(UUID.randomUUID(), "java", user, cores, flops) - } - - @Test - fun `should become ready after triggering installation`() { - val test = BehaviorTestKit(application()) - val inbox = test.createInbox() - test.run(ProcessMessage.Setup(machine, inbox.ref)) - inbox.expectMessage(ProcessEvent.Ready(test.ref)) - } - - @Test - fun `should not respond to setup request after being created`() { - val test = BehaviorTestKit(application()) - val inbox = test.createInbox() - - // Setup Machine - test.run(ProcessMessage.Setup(machine, inbox.ref)) - inbox.clear() - - // Try again - assertFalse(test.run(ProcessMessage.Setup(machine, inbox.ref))) - } - - @Test - fun `should respond to allocation with consumption`() { - val test = BehaviorTestKit(application()) - val inbox = test.createInbox() - - val allocation = ProcessMessage.Allocation(mapOf(ProcessingElement(0, cpu) to 0.5), until = 10.0) - - // Setup Machine - test.run(ProcessMessage.Setup(machine, inbox.ref)) - inbox.clear() - - // Test allocation - test.run(allocation) - val msg = inbox.receiveMessage() - assertTrue(msg is ProcessEvent.Consume) - } - - @Test - fun `should inform the machine that it finished processing`() { - val test = BehaviorTestKit(application()) - val inbox = test.createInbox() - val allocation = ProcessMessage.Allocation(mapOf(ProcessingElement(0, cpu) to 0.5), until = 10.0) - - // Setup - test.run(ProcessMessage.Setup(machine, inbox.ref)) - test.run(allocation) - test.runTo(10.0) - inbox.clear() - - test.run(allocation) - inbox.expectMessage(ProcessEvent.Exit(test.ref, 0)) - } - - @Test - fun `should be able to update its utilization`() { - val test = BehaviorTestKit(application()) - val inbox = test.createInbox() - val allocation1 = ProcessMessage.Allocation(mapOf(ProcessingElement(0, cpu) to 0.5), until = 10.0) - val allocation2 = ProcessMessage.Allocation(mapOf(ProcessingElement(0, cpu) to 0.25), until = 10.0) - - // Setup - test.run(ProcessMessage.Setup(machine, inbox.ref)) - test.run(allocation1) - test.runTo(5.0) - inbox.clear() - - test.run(allocation2) - assertTrue(inbox.receiveMessage() is ProcessEvent.Consume) - } -} diff --git a/opendc-experiments-tpds/build.gradle.kts b/opendc-experiments-tpds/build.gradle.kts deleted file mode 100644 index 3ec580af..00000000 --- a/opendc-experiments-tpds/build.gradle.kts +++ /dev/null @@ -1,50 +0,0 @@ -/* - * MIT License - * - * Copyright (c) 2019 atlarge-research - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -/* Build configuration */ -apply(from = "../gradle/kotlin.gradle") -plugins { - `java-library` - application -} - -/* Project configuration */ -repositories { - jcenter() -} - -application { - mainClassName = "com.atlarge.opendc.experiments.tpds.TestExperiment" -} - -dependencies { - api(project(":opendc-core")) - implementation(project(":opendc-format-gwf")) - implementation(project(":opendc-format-sc18")) - implementation(project(":opendc-workflows")) - implementation(kotlin("stdlib")) - - runtimeOnly(project(":odcsim-engine-omega")) - runtimeOnly("org.apache.logging.log4j:log4j-slf4j-impl:2.11.2") -} diff --git a/opendc-experiments-tpds/src/main/kotlin/com/atlarge/opendc/experiments/tpds/TestExperiment.kt b/opendc-experiments-tpds/src/main/kotlin/com/atlarge/opendc/experiments/tpds/TestExperiment.kt deleted file mode 100644 index ad302889..00000000 --- a/opendc-experiments-tpds/src/main/kotlin/com/atlarge/opendc/experiments/tpds/TestExperiment.kt +++ /dev/null @@ -1,148 +0,0 @@ -/* - * MIT License - * - * Copyright (c) 2019 atlarge-research - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -package com.atlarge.opendc.experiments.tpds - -import com.atlarge.odcsim.ActorContext -import com.atlarge.odcsim.ActorSystemFactory -import com.atlarge.odcsim.Behavior -import com.atlarge.odcsim.TimerScheduler -import com.atlarge.odcsim.coroutines.suspending -import com.atlarge.odcsim.receiveMessage -import com.atlarge.odcsim.same -import com.atlarge.odcsim.stopped -import com.atlarge.odcsim.unhandled -import com.atlarge.odcsim.withTimers -import com.atlarge.opendc.format.environment.sc18.Sc18EnvironmentReader -import com.atlarge.opendc.format.trace.gwf.GwfTraceReader -import com.atlarge.opendc.model.Broker -import com.atlarge.opendc.model.Model -import com.atlarge.opendc.model.PlatformRef -import com.atlarge.opendc.model.find -import com.atlarge.opendc.model.services.provisioning.SimpleProvisioningService -import com.atlarge.opendc.model.services.resources.ResourceManagementService -import com.atlarge.opendc.model.services.workflows.StageWorkflowScheduler -import com.atlarge.opendc.model.services.workflows.WorkflowEvent -import com.atlarge.opendc.model.services.workflows.WorkflowMessage -import com.atlarge.opendc.model.services.workflows.WorkflowSchedulerMode -import com.atlarge.opendc.model.services.workflows.WorkflowService -import com.atlarge.opendc.model.services.workflows.stages.job.FifoJobSortingPolicy -import com.atlarge.opendc.model.services.workflows.stages.job.NullJobAdmissionPolicy -import com.atlarge.opendc.model.services.workflows.stages.resources.FirstFitResourceSelectionPolicy -import com.atlarge.opendc.model.services.workflows.stages.resources.FunctionalResourceDynamicFilterPolicy -import com.atlarge.opendc.model.services.workflows.stages.task.FifoTaskSortingPolicy -import com.atlarge.opendc.model.services.workflows.stages.task.FunctionalTaskEligibilityPolicy -import com.atlarge.opendc.model.workload.workflow.Job -import com.atlarge.opendc.model.zones -import java.io.File -import java.util.ServiceLoader -import kotlin.math.max - -/** - * Main entry point of the experiment. - */ -fun main(args: Array) { - if (args.isEmpty()) { - println("error: Please provide path to GWF trace") - return - } - - - val scheduler = StageWorkflowScheduler( - mode = WorkflowSchedulerMode.Batch(100.0), - jobAdmissionPolicy = NullJobAdmissionPolicy, - jobSortingPolicy = FifoJobSortingPolicy(), - taskEligibilityPolicy = FunctionalTaskEligibilityPolicy(), - taskSortingPolicy = FifoTaskSortingPolicy(), - resourceDynamicFilterPolicy = FunctionalResourceDynamicFilterPolicy(), - resourceSelectionPolicy = FirstFitResourceSelectionPolicy() - ) - - val environment = Sc18EnvironmentReader(object {}.javaClass.getResourceAsStream("/env/setup-test.json")) - .use { it.read() } - .let { env -> - env.copy(platforms = env.platforms.map { platform -> - platform.copy(zones = platform.zones.map { zone -> - val services = zone.services + setOf(ResourceManagementService, SimpleProvisioningService, WorkflowService(scheduler)) - zone.copy(services = services) - }) - }) - } - - val broker = object : Broker { - override fun invoke(platforms: List): Behavior<*> = suspending { ctx -> - val zones = platforms.first().zones() - val service = zones.values.first().find(WorkflowService) - - val activeJobs = mutableSetOf() - val reader = GwfTraceReader(File(args[0])) - - fun submitNext(ctx: ActorContext, timers: TimerScheduler) { - if (!reader.hasNext()) { - return - } - - val (time, job) = reader.next() - timers.after(job, max(.0, time - ctx.time)) { - ctx.send(service, WorkflowMessage.Submit(job, ctx.self)) - submitNext(ctx, timers) - } - } - - var total = 0 - var finished = 0 - - withTimers { timers -> - submitNext(ctx, timers) - receiveMessage { msg -> - when (msg) { - is WorkflowEvent.JobSubmitted -> { - ctx.log.info("Job {} submitted", msg.job.uid) - total += 1 - same() - } - is WorkflowEvent.JobStarted -> { - activeJobs += msg.job - same() - } - is WorkflowEvent.JobFinished -> { - activeJobs -= msg.job - finished += 1 - ctx.log.info("Jobs {}/{} finished ({} tasks)", finished, total, msg.job.tasks.size) - if (activeJobs.isEmpty()) stopped() else same() - } - else -> - unhandled() - } - } - } - } - } - - val model = Model(environment, listOf(broker)) - val factory = ServiceLoader.load(ActorSystemFactory::class.java).first() - val system = factory(model(), name = "sim") - system.run() - system.terminate() -} diff --git a/opendc-experiments-tpds/src/main/resources/env/setup-test.json b/opendc-experiments-tpds/src/main/resources/env/setup-test.json deleted file mode 100644 index 0965b250..00000000 --- a/opendc-experiments-tpds/src/main/resources/env/setup-test.json +++ /dev/null @@ -1,36 +0,0 @@ -{ - "name": "Experimental Setup 2", - "rooms": [ - { - "type": "SERVER", - "objects": [ - { - "type": "RACK", - "machines": [ - { "cpus": [2] }, { "cpus": [2]}, - { "cpus": [2] }, { "cpus": [2]}, - { "cpus": [2] }, { "cpus": [2]}, - { "cpus": [2] }, { "cpus": [2]}, - { "cpus": [2] }, { "cpus": [2]}, - { "cpus": [2] }, { "cpus": [2]}, - { "cpus": [2] }, { "cpus": [2]}, - { "cpus": [2] }, { "cpus": [2]} - ] - }, - { - "type": "RACK", - "machines": [ - { "cpus": [1] }, { "cpus": [1]}, - { "cpus": [1] }, { "cpus": [1]}, - { "cpus": [1] }, { "cpus": [1]}, - { "cpus": [1] }, { "cpus": [1]}, - { "cpus": [1] }, { "cpus": [1]}, - { "cpus": [1] }, { "cpus": [1]}, - { "cpus": [1] }, { "cpus": [1]}, - { "cpus": [1] }, { "cpus": [1]} - ] - } - ] - } - ] -} diff --git a/opendc-experiments-tpds/src/main/resources/log4j2.xml b/opendc-experiments-tpds/src/main/resources/log4j2.xml deleted file mode 100644 index 67bf34ab..00000000 --- a/opendc-experiments-tpds/src/main/resources/log4j2.xml +++ /dev/null @@ -1,52 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/opendc-format-gwf/build.gradle.kts b/opendc-format-gwf/build.gradle.kts deleted file mode 100644 index caf86c0c..00000000 --- a/opendc-format-gwf/build.gradle.kts +++ /dev/null @@ -1,48 +0,0 @@ -/* - * MIT License - * - * Copyright (c) 2018 atlarge-research - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -/* Build configuration */ -apply(from = "../gradle/kotlin.gradle") -plugins { - `java-library` -} - -/* Project configuration */ -repositories { - jcenter() -} - -val junitJupiterVersion: String by extra -val junitPlatformVersion: String by extra - -dependencies { - api(project(":opendc-core")) - api(project(":opendc-format")) - api(project(":opendc-workflows")) - implementation(kotlin("stdlib")) - - testImplementation("org.junit.jupiter:junit-jupiter-api:$junitJupiterVersion") - testRuntimeOnly("org.junit.jupiter:junit-jupiter-engine:$junitJupiterVersion") - testImplementation("org.junit.platform:junit-platform-launcher:$junitPlatformVersion") -} diff --git a/opendc-format-gwf/src/main/kotlin/com/atlarge/opendc/format/trace/gwf/GwfTraceReader.kt b/opendc-format-gwf/src/main/kotlin/com/atlarge/opendc/format/trace/gwf/GwfTraceReader.kt deleted file mode 100644 index df6a4b11..00000000 --- a/opendc-format-gwf/src/main/kotlin/com/atlarge/opendc/format/trace/gwf/GwfTraceReader.kt +++ /dev/null @@ -1,168 +0,0 @@ -/* - * MIT License - * - * Copyright (c) 2019 atlarge-research - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -package com.atlarge.opendc.format.trace.gwf - -import com.atlarge.odcsim.Instant -import com.atlarge.opendc.format.trace.TraceEntry -import com.atlarge.opendc.format.trace.TraceReader -import com.atlarge.opendc.model.User -import com.atlarge.opendc.model.workload.application.FlopsApplication -import com.atlarge.opendc.model.workload.workflow.Job -import com.atlarge.opendc.model.workload.workflow.Task -import java.io.BufferedReader -import java.io.File -import java.io.InputStream -import java.util.UUID -import kotlin.math.max -import kotlin.math.min - -/** - * A [TraceReader] for the Grid Workload Format. See the Grid Workloads Archive (http://gwa.ewi.tudelft.nl/) for more - * information about the format. - * - * Be aware that in the Grid Workload Format, workflows are not required to be ordered by submission time and therefore - * this reader needs to read the whole trace into memory before an entry can be read. Consider converting the trace to a - * different format for better performance. - * - * @param reader The buffered reader to read the trace with. - */ -class GwfTraceReader(reader: BufferedReader) : TraceReader { - /** - * The internal iterator to use for this reader. - */ - private val iterator: Iterator> - - /** - * Create a [GwfTraceReader] instance from the specified [File]. - * - * @param file The file to read from. - */ - constructor(file: File) : this(file.bufferedReader()) - - /** - * Create a [GwfTraceReader] instance from the specified [InputStream]. - * - * @param input The input stream to read from. - */ - constructor(input: InputStream) : this(input.bufferedReader()) - - /** - * Initialize the reader. - */ - init { - val entries = mutableMapOf() - val tasks = mutableMapOf() - val taskDependencies = mutableMapOf>() - - var workflowIdCol = 0 - var taskIdCol = 0 - var submitTimeCol = 0 - var runtimeCol = 0 - var coreCol = 0 - var dependencyCol = 0 - - try { - reader.lineSequence() - .filter { line -> - // Ignore comments in the trace - !line.startsWith("#") && line.isNotBlank() - } - .forEachIndexed { idx, line -> - val values = line.split(",") - - // Parse GWF header - if (idx == 0) { - val header = values.mapIndexed { col, name -> Pair(name.trim(), col) }.toMap() - workflowIdCol = header["WorkflowID"]!! - taskIdCol = header["JobID"]!! - submitTimeCol = header["SubmitTime"]!! - runtimeCol = header["RunTime"]!! - coreCol = header["NProcs"]!! - dependencyCol = header["Dependencies"]!! - return@forEachIndexed - } - - val workflowId = values[workflowIdCol].trim().toLong() - val taskId = values[taskIdCol].trim().toLong() - val submitTime = values[submitTimeCol].trim().toDouble() - val runtime = max(0, values[runtimeCol].trim().toLong()) - val cores = values[coreCol].trim().toInt() - val dependencies = values[dependencyCol].split(" ") - .filter { it.isNotEmpty() } - .map { it.trim().toLong() } - - val flops: Long = 4000 * runtime * cores - - val entry = entries.getOrPut(workflowId) { - TraceEntryImpl(submitTime, Job(UUID(0L, taskId), "", UnnamedUser, HashSet())) - } - val workflow = entry.workload - val task = Task( - UUID(0L, taskId), "", - FlopsApplication(UUID(0L, taskId), "", workflow.owner, cores, flops), - HashSet() - ) - entry.submissionTime = min(entry.submissionTime, submitTime) - (workflow.tasks as MutableSet).add(task) - tasks[taskId] = task - taskDependencies[task] = dependencies - } - } finally { - reader.close() - } - - // Fix dependencies and dependents for all tasks - taskDependencies.forEach { (task, dependencies) -> - (task.dependencies as MutableSet).addAll(dependencies.map { taskId -> - tasks[taskId] ?: throw IllegalArgumentException("Dependency task with id $taskId not found") - }) - } - - // Create the entry iterator - iterator = entries.values.sortedBy { it.submissionTime }.iterator() - } - - override fun hasNext(): Boolean = iterator.hasNext() - - override fun next(): TraceEntry = iterator.next() - - override fun close() {} - - /** - * An unnamed user. - */ - private object UnnamedUser : User { - override val name: String = "" - override val uid: UUID = UUID.randomUUID() - } - - /** - * An entry in the trace. - */ - private data class TraceEntryImpl( - override var submissionTime: Instant, - override val workload: Job - ) : TraceEntry -} diff --git a/opendc-format-gwf/src/test/kotlin/com/atlarge/opendc/format/trace/gwf/GwfTraceReaderTest.kt b/opendc-format-gwf/src/test/kotlin/com/atlarge/opendc/format/trace/gwf/GwfTraceReaderTest.kt deleted file mode 100644 index ca60f61d..00000000 --- a/opendc-format-gwf/src/test/kotlin/com/atlarge/opendc/format/trace/gwf/GwfTraceReaderTest.kt +++ /dev/null @@ -1,41 +0,0 @@ -/* - * MIT License - * - * Copyright (c) 2019 atlarge-research - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -package com.atlarge.opendc.format.trace.gwf - -import org.junit.jupiter.api.DisplayName -import org.junit.jupiter.api.Test - -/** - * Test suite for the [GwfTraceReader] class. - */ -@DisplayName("GwfTraceReader") -internal class GwfTraceReaderTest { - @Test - fun `should open from InputStream`() { - val input = GwfTraceReaderTest::class.java.getResourceAsStream("/askalon_workload_olde.gwf") - val reader = GwfTraceReader(input) - reader.close() - } -} diff --git a/opendc-format-gwf/src/test/resources/trace.gwf b/opendc-format-gwf/src/test/resources/trace.gwf deleted file mode 100644 index 75b4c8d8..00000000 --- a/opendc-format-gwf/src/test/resources/trace.gwf +++ /dev/null @@ -1,4 +0,0 @@ -WorkflowID, JobID , SubmitTime, RunTime , NProcs , ReqNProcs , Dependencies -0 , 1 , 50 , 13 , 1 , 1 , -1 , 2 , 64 , 13 , 1 , 1 , -1 , 3 , 1821 , 12 , 1 , 1 , 2 diff --git a/opendc-format-sc18/build.gradle.kts b/opendc-format-sc18/build.gradle.kts deleted file mode 100644 index b656f4e3..00000000 --- a/opendc-format-sc18/build.gradle.kts +++ /dev/null @@ -1,50 +0,0 @@ -/* - * MIT License - * - * Copyright (c) 2018 atlarge-research - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -/* Build configuration */ -apply(from = "../gradle/kotlin.gradle") -plugins { - `java-library` -} - -/* Project configuration */ -repositories { - jcenter() -} - -val junitJupiterVersion: String by extra -val junitPlatformVersion: String by extra - -dependencies { - api(project(":opendc-core")) - api(project(":opendc-format")) - api(project(":opendc-workflows")) - api("com.fasterxml.jackson.module:jackson-module-kotlin:2.9.8") - implementation(kotlin("stdlib")) - implementation(kotlin("reflect")) - - testImplementation("org.junit.jupiter:junit-jupiter-api:$junitJupiterVersion") - testRuntimeOnly("org.junit.jupiter:junit-jupiter-engine:$junitJupiterVersion") - testImplementation("org.junit.platform:junit-platform-launcher:$junitPlatformVersion") -} diff --git a/opendc-format-sc18/src/main/kotlin/com/atlarge/opendc/format/environment/sc18/Model.kt b/opendc-format-sc18/src/main/kotlin/com/atlarge/opendc/format/environment/sc18/Model.kt deleted file mode 100644 index 4fbde269..00000000 --- a/opendc-format-sc18/src/main/kotlin/com/atlarge/opendc/format/environment/sc18/Model.kt +++ /dev/null @@ -1,44 +0,0 @@ -package com.atlarge.opendc.format.environment.sc18 - -import com.fasterxml.jackson.annotation.JsonSubTypes -import com.fasterxml.jackson.annotation.JsonTypeInfo - -/** - * A datacenter setup. - * - * @property name The name of the setup. - * @property rooms The rooms in the datacenter. - */ -data class Setup(val name: String, val rooms: List) - -/** - * A room in a datacenter. - * - * @property type The type of room in the datacenter. - * @property objects The objects in the room. - */ -data class Room(val type: String, val objects: List) - -/** - * An object in a [Room]. - * - * @property type The type of the room object. - */ -@JsonTypeInfo(use = JsonTypeInfo.Id.NAME, include = JsonTypeInfo.As.PROPERTY, property = "type") -@JsonSubTypes(value = [JsonSubTypes.Type(name = "RACK", value = RoomObject.Rack::class)]) -sealed class RoomObject(val type: String) { - /** - * A rack in a server room. - * - * @property machines The machines in the rack. - */ - data class Rack(val machines: List) : RoomObject("RACK") -} - -/** - * A machine in the setup that consists of the specified CPU's represented as - * integer identifiers and ethernet speed. - * - * @property cpus The CPUs in the machine represented as integer identifiers. - */ -data class Machine(val cpus: List) diff --git a/opendc-format-sc18/src/main/kotlin/com/atlarge/opendc/format/environment/sc18/Sc18EnvironmentReader.kt b/opendc-format-sc18/src/main/kotlin/com/atlarge/opendc/format/environment/sc18/Sc18EnvironmentReader.kt deleted file mode 100644 index 40ed5a45..00000000 --- a/opendc-format-sc18/src/main/kotlin/com/atlarge/opendc/format/environment/sc18/Sc18EnvironmentReader.kt +++ /dev/null @@ -1,95 +0,0 @@ -/* - * MIT License - * - * Copyright (c) 2019 atlarge-research - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -package com.atlarge.opendc.format.environment.sc18 - -import com.atlarge.opendc.format.environment.EnvironmentReader -import com.atlarge.opendc.model.Cluster -import com.atlarge.opendc.model.Environment -import com.atlarge.opendc.model.Platform -import com.atlarge.opendc.model.Zone -import com.atlarge.opendc.model.resources.compute.ProcessingElement -import com.atlarge.opendc.model.resources.compute.ProcessingUnit -import com.atlarge.opendc.model.resources.compute.host.Host -import com.atlarge.opendc.model.resources.compute.scheduling.SpaceSharedMachineScheduler -import com.fasterxml.jackson.databind.ObjectMapper -import com.fasterxml.jackson.module.kotlin.jacksonObjectMapper -import com.fasterxml.jackson.module.kotlin.readValue -import java.io.InputStream -import java.util.UUID - -/** - * A parser for the JSON experiment setup files used for the SC18 paper: "A Reference Architecture for Datacenter - * Schedulers". - * - * @param input The input stream to read from. - * @param mapper The Jackson object mapper to use. - */ -class Sc18EnvironmentReader(input: InputStream, mapper: ObjectMapper = jacksonObjectMapper()) : EnvironmentReader { - /** - * The environment that was read from the file. - */ - private val environment: Environment - - init { - val setup = mapper.readValue(input) - val clusters = setup.rooms.mapIndexed { i, room -> - var counter = 0 - val hosts = room.objects.flatMap { roomObject -> - when (roomObject) { - is RoomObject.Rack -> { - roomObject.machines.map { machine -> - val cores = machine.cpus.flatMap { id -> - when (id) { - 1 -> List(4) { ProcessingElement(it, CPUS[0]) } - 2 -> List(2) { ProcessingElement(it, CPUS[1]) } - else -> throw IllegalArgumentException("The cpu id $id is not recognized") - } - } - Host(UUID.randomUUID(), "node-${counter++}", SpaceSharedMachineScheduler, cores) - } - } - } - } - Cluster(UUID.randomUUID(), "cluster-$i", hosts) - } - - val platform = Platform(UUID.randomUUID(), "sc18-platform", listOf( - Zone(UUID.randomUUID(), "zone", emptySet(), clusters) - )) - - environment = Environment(setup.name, null, listOf(platform)) - } - - override fun read(): Environment = environment - - override fun close() {} - - companion object { - val CPUS = arrayOf( - ProcessingUnit("Intel", 6, 6920, "Intel(R) Core(TM) i7-6920HQ CPU @ 4.10GHz", 4100.0, 1), - ProcessingUnit("Intel", 6, 6930, "Intel(R) Core(TM) i7-6920HQ CPU @ 3.50GHz", 3500.0, 1) - ) - } -} diff --git a/opendc-format/build.gradle.kts b/opendc-format/build.gradle.kts deleted file mode 100644 index 68f9aa5d..00000000 --- a/opendc-format/build.gradle.kts +++ /dev/null @@ -1,51 +0,0 @@ -/* - * MIT License - * - * Copyright (c) 2019 atlarge-research - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -/* Build configuration */ -apply(from = "../gradle/kotlin.gradle") -plugins { - `java-library` -} - -/* Project configuration */ -repositories { - jcenter() -} - -val junitJupiterVersion: String by extra -val junitPlatformVersion: String by extra - -dependencies { - api(project(":odcsim-core")) - api(project(":opendc-core")) - - implementation(kotlin("stdlib")) - - testImplementation(project(":odcsim-testkit")) - testImplementation("org.junit.jupiter:junit-jupiter-api:$junitJupiterVersion") - testRuntimeOnly("org.junit.jupiter:junit-jupiter-engine:$junitJupiterVersion") - testImplementation("org.junit.platform:junit-platform-launcher:$junitPlatformVersion") - testRuntimeOnly("org.slf4j:slf4j-simple:1.7.25") - testImplementation("com.nhaarman.mockitokotlin2:mockito-kotlin:2.0.0") -} diff --git a/opendc-format/src/main/kotlin/com/atlarge/opendc/format/environment/EnvironmentReader.kt b/opendc-format/src/main/kotlin/com/atlarge/opendc/format/environment/EnvironmentReader.kt deleted file mode 100644 index 0ba3ae25..00000000 --- a/opendc-format/src/main/kotlin/com/atlarge/opendc/format/environment/EnvironmentReader.kt +++ /dev/null @@ -1,38 +0,0 @@ -/* - * MIT License - * - * Copyright (c) 2019 atlarge-research - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -package com.atlarge.opendc.format.environment - -import com.atlarge.opendc.model.Environment -import java.io.Closeable - -/** - * An interface for reading descriptions of datacenter environments into memory as [Environment]. - */ -interface EnvironmentReader : Closeable { - /** - * Read the description of the datacenter environment as [Environment]. - */ - fun read(): Environment -} diff --git a/opendc-format/src/main/kotlin/com/atlarge/opendc/format/trace/TraceEntry.kt b/opendc-format/src/main/kotlin/com/atlarge/opendc/format/trace/TraceEntry.kt deleted file mode 100644 index cf0ab526..00000000 --- a/opendc-format/src/main/kotlin/com/atlarge/opendc/format/trace/TraceEntry.kt +++ /dev/null @@ -1,55 +0,0 @@ -/* - * MIT License - * - * Copyright (c) 2019 atlarge-research - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -package com.atlarge.opendc.format.trace - -import com.atlarge.odcsim.Instant -import com.atlarge.opendc.model.workload.Workload - -/** - * An entry in a workload trace. - * - * @param T The shape of the workload in this entry. - */ -interface TraceEntry { - /** - * The time of submission of the workload. - */ - val submissionTime: Instant - - /** - * The workload in this trace entry. - */ - val workload: T - - /** - * Extract the submission time from this entry. - */ - operator fun component1() = submissionTime - - /** - * Extract the workload from this entry. - */ - operator fun component2() = workload -} diff --git a/opendc-format/src/main/kotlin/com/atlarge/opendc/format/trace/TraceReader.kt b/opendc-format/src/main/kotlin/com/atlarge/opendc/format/trace/TraceReader.kt deleted file mode 100644 index af8b272d..00000000 --- a/opendc-format/src/main/kotlin/com/atlarge/opendc/format/trace/TraceReader.kt +++ /dev/null @@ -1,37 +0,0 @@ -/* - * MIT License - * - * Copyright (c) 2019 atlarge-research - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -package com.atlarge.opendc.format.trace - -import com.atlarge.opendc.model.workload.Workload -import java.io.Closeable - -/** - * An interface for reading [Workload]s into memory. - * - * This interface must guarantee that the entries are delivered in order of submission time. - * - * @param T The shape of the workloads supported by this reader. - */ -interface TraceReader : Iterator>, Closeable diff --git a/opendc-format/src/main/kotlin/com/atlarge/opendc/format/trace/TraceWriter.kt b/opendc-format/src/main/kotlin/com/atlarge/opendc/format/trace/TraceWriter.kt deleted file mode 100644 index b5424fd2..00000000 --- a/opendc-format/src/main/kotlin/com/atlarge/opendc/format/trace/TraceWriter.kt +++ /dev/null @@ -1,46 +0,0 @@ -/* - * MIT License - * - * Copyright (c) 2019 atlarge-research - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -package com.atlarge.opendc.format.trace - -import com.atlarge.odcsim.Instant -import com.atlarge.opendc.model.workload.Workload -import java.io.Closeable - -/** - * An interface for persisting workload traces (e.g. to disk). - * - * @param T The type of [Workload] supported by this writer. - */ -interface TraceWriter : Closeable { - /** - * Write an entry to the trace. - * - * Entries must be written in order of submission time. Failing to do so results in a [IllegalArgumentException]. - * - * @param submissionTime The time of submission of the workload. - * @param workload The workload to write to the trace. - */ - fun write(submissionTime: Instant, workload: T) -} diff --git a/opendc-workflows/build.gradle.kts b/opendc-workflows/build.gradle.kts deleted file mode 100644 index 68f9aa5d..00000000 --- a/opendc-workflows/build.gradle.kts +++ /dev/null @@ -1,51 +0,0 @@ -/* - * MIT License - * - * Copyright (c) 2019 atlarge-research - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -/* Build configuration */ -apply(from = "../gradle/kotlin.gradle") -plugins { - `java-library` -} - -/* Project configuration */ -repositories { - jcenter() -} - -val junitJupiterVersion: String by extra -val junitPlatformVersion: String by extra - -dependencies { - api(project(":odcsim-core")) - api(project(":opendc-core")) - - implementation(kotlin("stdlib")) - - testImplementation(project(":odcsim-testkit")) - testImplementation("org.junit.jupiter:junit-jupiter-api:$junitJupiterVersion") - testRuntimeOnly("org.junit.jupiter:junit-jupiter-engine:$junitJupiterVersion") - testImplementation("org.junit.platform:junit-platform-launcher:$junitPlatformVersion") - testRuntimeOnly("org.slf4j:slf4j-simple:1.7.25") - testImplementation("com.nhaarman.mockitokotlin2:mockito-kotlin:2.0.0") -} diff --git a/opendc-workflows/src/main/kotlin/com/atlarge/opendc/model/services/workflows/StageWorkflowScheduler.kt b/opendc-workflows/src/main/kotlin/com/atlarge/opendc/model/services/workflows/StageWorkflowScheduler.kt deleted file mode 100644 index 45f3c4b0..00000000 --- a/opendc-workflows/src/main/kotlin/com/atlarge/opendc/model/services/workflows/StageWorkflowScheduler.kt +++ /dev/null @@ -1,58 +0,0 @@ -/* - * MIT License - * - * Copyright (c) 2019 atlarge-research - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -package com.atlarge.opendc.model.services.workflows - -import com.atlarge.odcsim.ActorContext -import com.atlarge.odcsim.TimerScheduler -import com.atlarge.opendc.model.services.provisioning.ProvisioningResponse -import com.atlarge.opendc.model.services.workflows.stages.job.JobAdmissionPolicy -import com.atlarge.opendc.model.services.workflows.stages.job.JobSortingPolicy -import com.atlarge.opendc.model.services.workflows.stages.resources.ResourceDynamicFilterPolicy -import com.atlarge.opendc.model.services.workflows.stages.resources.ResourceSelectionPolicy -import com.atlarge.opendc.model.services.workflows.stages.task.TaskEligibilityPolicy -import com.atlarge.opendc.model.services.workflows.stages.task.TaskSortingPolicy - -/** - * A [WorkflowScheduler] that distributes work through a multi-stage process based on the Reference Architecture for - * Datacenter Scheduling. - */ -class StageWorkflowScheduler( - private val mode: WorkflowSchedulerMode, - private val jobAdmissionPolicy: JobAdmissionPolicy, - private val jobSortingPolicy: JobSortingPolicy, - private val taskEligibilityPolicy: TaskEligibilityPolicy, - private val taskSortingPolicy: TaskSortingPolicy, - private val resourceDynamicFilterPolicy: ResourceDynamicFilterPolicy, - private val resourceSelectionPolicy: ResourceSelectionPolicy -) : WorkflowScheduler { - override fun invoke( - ctx: ActorContext, - timers: TimerScheduler, - lease: ProvisioningResponse.Lease - ): WorkflowSchedulerLogic { - return StageWorkflowSchedulerLogic(ctx, timers, lease, mode, jobAdmissionPolicy, - jobSortingPolicy, taskEligibilityPolicy, taskSortingPolicy, resourceDynamicFilterPolicy, resourceSelectionPolicy) - } -} diff --git a/opendc-workflows/src/main/kotlin/com/atlarge/opendc/model/services/workflows/StageWorkflowSchedulerLogic.kt b/opendc-workflows/src/main/kotlin/com/atlarge/opendc/model/services/workflows/StageWorkflowSchedulerLogic.kt deleted file mode 100644 index 9d5f4bea..00000000 --- a/opendc-workflows/src/main/kotlin/com/atlarge/opendc/model/services/workflows/StageWorkflowSchedulerLogic.kt +++ /dev/null @@ -1,248 +0,0 @@ -/* - * MIT License - * - * Copyright (c) 2019 atlarge-research - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -package com.atlarge.opendc.model.services.workflows - -import com.atlarge.odcsim.ActorContext -import com.atlarge.odcsim.ActorRef -import com.atlarge.odcsim.TimerScheduler -import com.atlarge.odcsim.unsafeCast -import com.atlarge.opendc.model.resources.compute.MachineMessage -import com.atlarge.opendc.model.resources.compute.MachineRef -import com.atlarge.opendc.model.resources.compute.scheduling.ProcessState -import com.atlarge.opendc.model.services.provisioning.ProvisioningResponse -import com.atlarge.opendc.model.services.resources.HostView -import com.atlarge.opendc.model.services.workflows.stages.job.JobAdmissionPolicy -import com.atlarge.opendc.model.services.workflows.stages.job.JobSortingPolicy -import com.atlarge.opendc.model.services.workflows.stages.resources.ResourceDynamicFilterPolicy -import com.atlarge.opendc.model.services.workflows.stages.resources.ResourceSelectionPolicy -import com.atlarge.opendc.model.services.workflows.stages.task.TaskEligibilityPolicy -import com.atlarge.opendc.model.services.workflows.stages.task.TaskSortingPolicy -import com.atlarge.opendc.model.workload.application.Application -import com.atlarge.opendc.model.workload.application.Pid -import com.atlarge.opendc.model.workload.workflow.Job -import com.atlarge.opendc.model.workload.workflow.Task - -/** - * Logic of the [StageWorkflowScheduler]. - */ -class StageWorkflowSchedulerLogic( - ctx: ActorContext, - timers: TimerScheduler, - lease: ProvisioningResponse.Lease, - private val mode: WorkflowSchedulerMode, - private val jobAdmissionPolicy: JobAdmissionPolicy, - private val jobSortingPolicy: JobSortingPolicy, - private val taskEligibilityPolicy: TaskEligibilityPolicy, - private val taskSortingPolicy: TaskSortingPolicy, - private val resourceDynamicFilterPolicy: ResourceDynamicFilterPolicy, - private val resourceSelectionPolicy: ResourceSelectionPolicy -) : WorkflowSchedulerLogic(ctx, timers, lease) { - - /** - * The incoming jobs ready to be processed by the scheduler. - */ - internal val incomingJobs: MutableSet = mutableSetOf() - - /** - * The active jobs in the system. - */ - internal val activeJobs: MutableSet = mutableSetOf() - - /** - * The running tasks by [Pid]. - */ - internal val taskByPid = mutableMapOf() - - /** - * The available processor cores on the leased machines. - */ - internal val machineCores: MutableMap = HashMap() - - init { - lease.hosts.forEach { machineCores[it] = it.host.cores.count() } - } - - override fun submit(job: Job, handler: ActorRef) { - // J1 Incoming Jobs - val jobInstance = JobView(job, handler) - val instances = job.tasks.associateWith { - TaskView(jobInstance, it) - } - - for ((task, instance) in instances) { - instance.dependencies.addAll(task.dependencies.map { instances[it]!! }) - task.dependencies.forEach { - instances[it]!!.dependents.add(instance) - } - - // If the task has no dependency, it is a root task and can immediately be evaluated - if (instance.isRoot) { - instance.state = ProcessState.READY - } - } - - jobInstance.tasks = instances.values.toMutableSet() - incomingJobs += jobInstance - ctx.send(handler, WorkflowEvent.JobSubmitted(ctx.self, job, ctx.time)) - requestCycle() - } - - /** - * Indicate to the scheduler that a scheduling cycle is needed. - */ - private fun requestCycle() { - when (mode) { - is WorkflowSchedulerMode.Interactive -> { - schedule() - } - is WorkflowSchedulerMode.Batch -> { - timers.after(mode, mode.quantum) { - schedule() - } - } - } - } - - /** - * Perform a scheduling cycle immediately. - */ - override fun schedule() { - // J2 Create list of eligible jobs - jobAdmissionPolicy.startCycle(this) - val eligibleJobs = incomingJobs.filter { jobAdmissionPolicy.shouldAdmit(this, it) } - for (jobInstance in eligibleJobs) { - incomingJobs -= jobInstance - activeJobs += jobInstance - ctx.send(jobInstance.broker, WorkflowEvent.JobStarted(ctx.self, jobInstance.job, ctx.time)) - } - - // J3 Sort jobs on criterion - val sortedJobs = jobSortingPolicy(this, activeJobs) - - // J4 Per job - for (jobInstance in sortedJobs) { - // T1 Create list of eligible tasks - taskEligibilityPolicy.startCycle(this) - val eligibleTasks = jobInstance.tasks.filter { taskEligibilityPolicy.isEligible(this, it) } - - // T2 Sort tasks on criterion - val sortedTasks = taskSortingPolicy(this, eligibleTasks) - - // T3 Per task - for (instance in sortedTasks) { - val hosts = resourceDynamicFilterPolicy(this, lease.hosts, instance) - val host = resourceSelectionPolicy.select(this, hosts, instance) - - if (host != null) { - // T4 Submit task to machine - ctx.send(host.ref, MachineMessage.Submit(instance.task.application, instance, ctx.self.unsafeCast())) - instance.host = host - instance.state = ProcessState.RUNNING // Assume the application starts running - machineCores.merge(host, instance.task.application.cores, Int::minus) - } else { - return - } - } - } - } - - override fun onSubmission(instance: MachineRef, application: Application, key: Any, pid: Pid) { - val task = key as TaskView - task.pid = pid - taskByPid[pid] = task - ctx.send(task.job.broker, WorkflowEvent.TaskStarted(ctx.self, task.job.job, task.task, ctx.time)) - } - - override fun onTermination(instance: MachineRef, pid: Pid, status: Int) { - val task = taskByPid.remove(pid)!! - val job = task.job - task.state = ProcessState.TERMINATED - job.tasks.remove(task) - machineCores.merge(task.host!!, task.task.application.cores, Int::plus) - ctx.send(job.broker, WorkflowEvent.TaskFinished(ctx.self, job.job, task.task, status, ctx.time)) - - if (job.isFinished) { - activeJobs -= job - ctx.send(job.broker, WorkflowEvent.JobFinished(ctx.self, job.job, ctx.time)) - } - - requestCycle() - } - - class JobView(val job: Job, val broker: ActorRef) { - /** - * A flag to indicate whether this job is finished. - */ - val isFinished: Boolean - get() = tasks.isEmpty() - - lateinit var tasks: MutableSet - } - - class TaskView(val job: JobView, val task: Task) { - /** - * The dependencies of this task. - */ - val dependencies = HashSet() - - /** - * The dependents of this task. - */ - val dependents = HashSet() - - /** - * A flag to indicate whether this workflow task instance is a workflow root. - */ - val isRoot: Boolean - get() = dependencies.isEmpty() - - var state: ProcessState = ProcessState.CREATED - set(value) { - field = value - - // Mark the process as terminated in the graph - if (value == ProcessState.TERMINATED) { - markTerminated() - } - } - - var pid: Pid? = null - - var host: HostView? = null - - /** - * Mark the specified [TaskView] as terminated. - */ - private fun markTerminated() { - for (dependent in dependents) { - dependent.dependencies.remove(this) - - if (dependent.isRoot) { - dependent.state = ProcessState.READY - } - } - } - } -} diff --git a/opendc-workflows/src/main/kotlin/com/atlarge/opendc/model/services/workflows/WorkflowScheduler.kt b/opendc-workflows/src/main/kotlin/com/atlarge/opendc/model/services/workflows/WorkflowScheduler.kt deleted file mode 100644 index c81085d4..00000000 --- a/opendc-workflows/src/main/kotlin/com/atlarge/opendc/model/services/workflows/WorkflowScheduler.kt +++ /dev/null @@ -1,47 +0,0 @@ -/* - * MIT License - * - * Copyright (c) 2019 atlarge-research - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -package com.atlarge.opendc.model.services.workflows - -import com.atlarge.odcsim.ActorContext -import com.atlarge.odcsim.TimerScheduler -import com.atlarge.opendc.model.services.provisioning.ProvisioningResponse - -/** - * A factory interface for constructing a [WorkflowSchedulerLogic]. - */ -interface WorkflowScheduler { - /** - * Construct a [WorkflowSchedulerLogic] in the given [ActorContext]. - * - * @param ctx The context in which the scheduler runs. - * @param timers The timer scheduler to use. - * @param lease The resource lease to use. - */ - operator fun invoke( - ctx: ActorContext, - timers: TimerScheduler, - lease: ProvisioningResponse.Lease - ): WorkflowSchedulerLogic -} diff --git a/opendc-workflows/src/main/kotlin/com/atlarge/opendc/model/services/workflows/WorkflowSchedulerLogic.kt b/opendc-workflows/src/main/kotlin/com/atlarge/opendc/model/services/workflows/WorkflowSchedulerLogic.kt deleted file mode 100644 index 09cb0ef9..00000000 --- a/opendc-workflows/src/main/kotlin/com/atlarge/opendc/model/services/workflows/WorkflowSchedulerLogic.kt +++ /dev/null @@ -1,55 +0,0 @@ -/* - * MIT License - * - * Copyright (c) 2019 atlarge-research - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -package com.atlarge.opendc.model.services.workflows - -import com.atlarge.odcsim.ActorContext -import com.atlarge.odcsim.ActorRef -import com.atlarge.odcsim.TimerScheduler -import com.atlarge.opendc.model.resources.compute.scheduling.ProcessObserver -import com.atlarge.opendc.model.services.provisioning.ProvisioningResponse -import com.atlarge.opendc.model.workload.workflow.Job - -/** - * A workflow scheduler interface that schedules jobs across machines. - * - * @property ctx The context in which the scheduler runs. - * @property timers The timer scheduler to use. - * @property lease The resource lease to use. - */ -abstract class WorkflowSchedulerLogic( - protected val ctx: ActorContext, - protected val timers: TimerScheduler, - protected val lease: ProvisioningResponse.Lease -) : ProcessObserver { - /** - * Submit the specified workflow for scheduling. - */ - abstract fun submit(job: Job, handler: ActorRef) - - /** - * Trigger an immediate scheduling cycle. - */ - abstract fun schedule() -} diff --git a/opendc-workflows/src/main/kotlin/com/atlarge/opendc/model/services/workflows/WorkflowSchedulerMode.kt b/opendc-workflows/src/main/kotlin/com/atlarge/opendc/model/services/workflows/WorkflowSchedulerMode.kt deleted file mode 100644 index 0a4b40e5..00000000 --- a/opendc-workflows/src/main/kotlin/com/atlarge/opendc/model/services/workflows/WorkflowSchedulerMode.kt +++ /dev/null @@ -1,42 +0,0 @@ -/* - * MIT License - * - * Copyright (c) 2019 atlarge-research - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -package com.atlarge.opendc.model.services.workflows - -import com.atlarge.odcsim.Duration - -/** - * The operating mode of a workflow scheduler. - */ -sealed class WorkflowSchedulerMode { - /** - * An interactive scheduler immediately triggers a new scheduling cycle when a workflow is received. - */ - object Interactive : WorkflowSchedulerMode() - - /** - * A batch scheduler triggers a scheduling cycle every time quantum if needed. - */ - data class Batch(val quantum: Duration) : WorkflowSchedulerMode() -} diff --git a/opendc-workflows/src/main/kotlin/com/atlarge/opendc/model/services/workflows/WorkflowService.kt b/opendc-workflows/src/main/kotlin/com/atlarge/opendc/model/services/workflows/WorkflowService.kt deleted file mode 100644 index 72397203..00000000 --- a/opendc-workflows/src/main/kotlin/com/atlarge/opendc/model/services/workflows/WorkflowService.kt +++ /dev/null @@ -1,193 +0,0 @@ -/* - * MIT License - * - * Copyright (c) 2019 atlarge-research - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -package com.atlarge.opendc.model.services.workflows - -import com.atlarge.odcsim.ActorRef -import com.atlarge.odcsim.Behavior -import com.atlarge.odcsim.Instant -import com.atlarge.odcsim.TimerScheduler -import com.atlarge.odcsim.coroutines.actorContext -import com.atlarge.odcsim.coroutines.dsl.ask -import com.atlarge.odcsim.coroutines.suspending -import com.atlarge.odcsim.receiveMessage -import com.atlarge.odcsim.same -import com.atlarge.odcsim.unhandled -import com.atlarge.odcsim.withTimers -import com.atlarge.opendc.model.Zone -import com.atlarge.opendc.model.ZoneRef -import com.atlarge.opendc.model.find -import com.atlarge.opendc.model.resources.compute.MachineEvent -import com.atlarge.opendc.model.services.AbstractService -import com.atlarge.opendc.model.services.Service -import com.atlarge.opendc.model.services.ServiceProvider -import com.atlarge.opendc.model.services.provisioning.ProvisioningMessage -import com.atlarge.opendc.model.services.provisioning.ProvisioningResponse -import com.atlarge.opendc.model.services.provisioning.ProvisioningService -import com.atlarge.opendc.model.workload.workflow.Job -import com.atlarge.opendc.model.workload.workflow.Task -import java.util.UUID - -/** - * A service for cloud workflow management. - * - * The workflow scheduler is modelled after the Reference Architecture for Datacenter Scheduling by Andreadis et al. - */ -class WorkflowService(val scheduler: WorkflowScheduler) : ServiceProvider { - override val uid: UUID = UUID.randomUUID() - override val name: String = "workflows" - override val provides: Set> = setOf(WorkflowService) - override val dependencies: Set> = setOf(ProvisioningService) - - /** - * Build the runtime [Behavior] for the workflow service, responding to messages of shape [WorkflowMessage]. - */ - override fun invoke(zone: Zone, ref: ZoneRef): Behavior = suspending { ctx -> - val provisioner = ref.find(ProvisioningService) - // Wait for 0.1 sec before asking the provisioner to allow it to initialize. Will return empty response if asked - // immediately. - val lease: ProvisioningResponse.Lease = actorContext().ask(provisioner, after = 0.1) { ProvisioningMessage.Request(Int.MAX_VALUE, it) } - - withTimers { timers -> - @Suppress("UNCHECKED_CAST") - val schedulerLogic = scheduler(ctx, timers as TimerScheduler, lease) - - receiveMessage { msg -> - when (msg) { - is WorkflowMessage.Submit -> { - schedulerLogic.submit(msg.job, msg.broker) - same() - } - is MachineEvent.Submitted -> { - schedulerLogic.onSubmission(msg.instance, msg.application, msg.key, msg.pid) - same() - } - is MachineEvent.Terminated -> { - schedulerLogic.onTermination(msg.instance, msg.pid, msg.status) - same() - } - else -> - unhandled() - } - } - }.narrow() - } - - companion object : AbstractService(UUID.randomUUID(), "workflows") -} - -/** - * A reference to the workflow service instance. - */ -typealias WorkflowServiceRef = ActorRef - -/** - * A message protocol for communicating to the workflow service. - */ -sealed class WorkflowMessage { - /** - * Submit the specified [Job] to the workflow service for scheduling. - * - * @property job The workflow to submit for scheduling. - * @property broker The broker that has submitted this workflow on behalf of a user and that needs to be kept - * up-to-date. - */ - data class Submit(val job: Job, val broker: ActorRef) : WorkflowMessage() -} - -/** - * A message protocol used by the workflow service to respond to [WorkflowMessage]s. - */ -sealed class WorkflowEvent { - /** - * Indicate that the specified [Job] was submitted to the workflow service. - * - * @property service The reference to the service the job was submitted to. - * @property job The job that has been submitted. - * @property time A timestamp of the moment the job was received. - */ - data class JobSubmitted( - val service: WorkflowServiceRef, - val job: Job, - val time: Instant - ) : WorkflowEvent() - - /** - * Indicate that the specified [Job] has become active. - * - * @property service The reference to the service the job was submitted to. - * @property job The job that has been submitted. - * @property time A timestamp of the moment the job started. - */ - data class JobStarted( - val service: WorkflowServiceRef, - val job: Job, - val time: Instant - ) : WorkflowEvent() - - /** - * Indicate that the specified [Task] has started processing. - * - * @property service The reference to the service the job was submitted to. - * @property job The job that contains this task. - * @property task The task that has started processing. - * @property time A timestamp of the moment the task started. - */ - data class TaskStarted( - val service: WorkflowServiceRef, - val job: Job, - val task: Task, - val time: Instant - ) : WorkflowEvent() - - /** - * Indicate that the specified [Task] has started processing. - * - * @property service The reference to the service the job was submitted to. - * @property job The job that contains this task. - * @property task The task that has started processing. - * @property status The exit code of the task, where zero means successful. - * @property time A timestamp of the moment the task finished. - */ - data class TaskFinished( - val service: WorkflowServiceRef, - val job: Job, - val task: Task, - val status: Int, - val time: Instant - ) : WorkflowEvent() - - /** - * Indicate that the specified [Job] has finished processing. - * - * @property service The reference to the service the job was submitted to. - * @property job The job that has finished processing. - * @property time A timestamp of the moment the task finished. - */ - data class JobFinished( - val service: WorkflowServiceRef, - val job: Job, - val time: Instant - ) : WorkflowEvent() -} diff --git a/opendc-workflows/src/main/kotlin/com/atlarge/opendc/model/services/workflows/stages/job/FifoJobSortingPolicy.kt b/opendc-workflows/src/main/kotlin/com/atlarge/opendc/model/services/workflows/stages/job/FifoJobSortingPolicy.kt deleted file mode 100644 index c58d2210..00000000 --- a/opendc-workflows/src/main/kotlin/com/atlarge/opendc/model/services/workflows/stages/job/FifoJobSortingPolicy.kt +++ /dev/null @@ -1,37 +0,0 @@ -/* - * MIT License - * - * Copyright (c) 2019 atlarge-research - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -package com.atlarge.opendc.model.services.workflows.stages.job - -import com.atlarge.opendc.model.services.workflows.StageWorkflowSchedulerLogic - -/** - * The [FifoJobSortingPolicy] sorts tasks based on the order of arrival in the queue. - */ -class FifoJobSortingPolicy : JobSortingPolicy { - override fun invoke( - scheduler: StageWorkflowSchedulerLogic, - jobs: Collection - ): List = jobs.toList() -} diff --git a/opendc-workflows/src/main/kotlin/com/atlarge/opendc/model/services/workflows/stages/job/JobAdmissionPolicy.kt b/opendc-workflows/src/main/kotlin/com/atlarge/opendc/model/services/workflows/stages/job/JobAdmissionPolicy.kt deleted file mode 100644 index be60fa9b..00000000 --- a/opendc-workflows/src/main/kotlin/com/atlarge/opendc/model/services/workflows/stages/job/JobAdmissionPolicy.kt +++ /dev/null @@ -1,48 +0,0 @@ -/* - * MIT License - * - * Copyright (c) 2019 atlarge-research - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -package com.atlarge.opendc.model.services.workflows.stages.job - -import com.atlarge.opendc.model.services.workflows.StageWorkflowSchedulerLogic - -/** - * A policy interface for admitting [StageWorkflowSchedulerLogic.JobView]s to a scheduling cycle. - */ -interface JobAdmissionPolicy { - /** - * A method that is invoked at the start of each scheduling cycle. - * - * @param scheduler The scheduler that started the cycle. - */ - fun startCycle(scheduler: StageWorkflowSchedulerLogic) {} - - /** - * Determine whether the specified [StageWorkflowSchedulerLogic.JobView] should be admitted to the scheduling cycle. - * - * @param scheduler The scheduler that should admit or reject the job. - * @param job The workflow that has been submitted. - * @return `true` if the workflow may be admitted to the scheduling cycle, `false` otherwise. - */ - fun shouldAdmit(scheduler: StageWorkflowSchedulerLogic, job: StageWorkflowSchedulerLogic.JobView): Boolean -} diff --git a/opendc-workflows/src/main/kotlin/com/atlarge/opendc/model/services/workflows/stages/job/JobSortingPolicy.kt b/opendc-workflows/src/main/kotlin/com/atlarge/opendc/model/services/workflows/stages/job/JobSortingPolicy.kt deleted file mode 100644 index 3af88aa7..00000000 --- a/opendc-workflows/src/main/kotlin/com/atlarge/opendc/model/services/workflows/stages/job/JobSortingPolicy.kt +++ /dev/null @@ -1,44 +0,0 @@ -/* - * MIT License - * - * Copyright (c) 2019 atlarge-research - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -package com.atlarge.opendc.model.services.workflows.stages.job - -import com.atlarge.opendc.model.services.workflows.StageWorkflowSchedulerLogic - -/** - * A policy interface for ordering admitted workflows in the scheduling queue. - */ -interface JobSortingPolicy { - /** - * Sort the given collection of jobs on a given criterion. - * - * @param scheduler The scheduler that started the cycle. - * @param jobs The collection of tasks that should be sorted. - * @return The sorted list of jobs. - */ - operator fun invoke( - scheduler: StageWorkflowSchedulerLogic, - jobs: Collection - ): List -} diff --git a/opendc-workflows/src/main/kotlin/com/atlarge/opendc/model/services/workflows/stages/job/NullJobAdmissionPolicy.kt b/opendc-workflows/src/main/kotlin/com/atlarge/opendc/model/services/workflows/stages/job/NullJobAdmissionPolicy.kt deleted file mode 100644 index 5436a1a1..00000000 --- a/opendc-workflows/src/main/kotlin/com/atlarge/opendc/model/services/workflows/stages/job/NullJobAdmissionPolicy.kt +++ /dev/null @@ -1,40 +0,0 @@ -/* - * MIT License - * - * Copyright (c) 2019 atlarge-research - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -package com.atlarge.opendc.model.services.workflows.stages.job - -import com.atlarge.opendc.model.services.workflows.StageWorkflowSchedulerLogic - -/** - * A [JobAdmissionPolicy] that admits all jobs. - */ -object NullJobAdmissionPolicy : JobAdmissionPolicy { - /** - * Admit every submitted job. - */ - override fun shouldAdmit( - scheduler: StageWorkflowSchedulerLogic, - job: StageWorkflowSchedulerLogic.JobView - ): Boolean = true -} diff --git a/opendc-workflows/src/main/kotlin/com/atlarge/opendc/model/services/workflows/stages/job/RandomJobSortingPolicy.kt b/opendc-workflows/src/main/kotlin/com/atlarge/opendc/model/services/workflows/stages/job/RandomJobSortingPolicy.kt deleted file mode 100644 index 7da59692..00000000 --- a/opendc-workflows/src/main/kotlin/com/atlarge/opendc/model/services/workflows/stages/job/RandomJobSortingPolicy.kt +++ /dev/null @@ -1,40 +0,0 @@ -/* - * MIT License - * - * Copyright (c) 2019 atlarge-research - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -package com.atlarge.opendc.model.services.workflows.stages.job - -import com.atlarge.opendc.model.services.workflows.StageWorkflowSchedulerLogic -import kotlin.random.Random - -/** - * The [RandomJobSortingPolicy] sorts tasks randomly. - * - * @property random The [Random] instance to use when sorting the list of tasks. - */ -class RandomJobSortingPolicy(private val random: Random = Random.Default) : JobSortingPolicy { - override fun invoke( - scheduler: StageWorkflowSchedulerLogic, - jobs: Collection - ): List = jobs.shuffled(random) -} diff --git a/opendc-workflows/src/main/kotlin/com/atlarge/opendc/model/services/workflows/stages/resources/FirstFitResourceSelectionPolicy.kt b/opendc-workflows/src/main/kotlin/com/atlarge/opendc/model/services/workflows/stages/resources/FirstFitResourceSelectionPolicy.kt deleted file mode 100644 index afaf075d..00000000 --- a/opendc-workflows/src/main/kotlin/com/atlarge/opendc/model/services/workflows/stages/resources/FirstFitResourceSelectionPolicy.kt +++ /dev/null @@ -1,40 +0,0 @@ -/* - * MIT License - * - * Copyright (c) 2019 atlarge-research - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -package com.atlarge.opendc.model.services.workflows.stages.resources - -import com.atlarge.opendc.model.services.resources.HostView -import com.atlarge.opendc.model.services.workflows.StageWorkflowSchedulerLogic - -/** - * A [ResourceSelectionPolicy] that selects the first machine that is available. - */ -class FirstFitResourceSelectionPolicy : ResourceSelectionPolicy { - override fun select( - scheduler: StageWorkflowSchedulerLogic, - machines: List, - task: StageWorkflowSchedulerLogic.TaskView - ): HostView? = - machines.firstOrNull() -} diff --git a/opendc-workflows/src/main/kotlin/com/atlarge/opendc/model/services/workflows/stages/resources/FunctionalResourceDynamicFilterPolicy.kt b/opendc-workflows/src/main/kotlin/com/atlarge/opendc/model/services/workflows/stages/resources/FunctionalResourceDynamicFilterPolicy.kt deleted file mode 100644 index 3f28a040..00000000 --- a/opendc-workflows/src/main/kotlin/com/atlarge/opendc/model/services/workflows/stages/resources/FunctionalResourceDynamicFilterPolicy.kt +++ /dev/null @@ -1,43 +0,0 @@ -/* - * MIT License - * - * Copyright (c) 2019 atlarge-research - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -package com.atlarge.opendc.model.services.workflows.stages.resources - -import com.atlarge.opendc.model.services.resources.HostView -import com.atlarge.opendc.model.services.workflows.StageWorkflowSchedulerLogic - -/** - * A [ResourceDynamicFilterPolicy] based on the amount of cores available on the machine and the cores required for - * the task. - */ -class FunctionalResourceDynamicFilterPolicy : ResourceDynamicFilterPolicy { - override fun invoke( - scheduler: StageWorkflowSchedulerLogic, - machines: List, - task: StageWorkflowSchedulerLogic.TaskView - ): List { - return machines - .filter { scheduler.machineCores[it] ?: 0 >= task.task.application.cores } - } -} diff --git a/opendc-workflows/src/main/kotlin/com/atlarge/opendc/model/services/workflows/stages/resources/ResourceDynamicFilterPolicy.kt b/opendc-workflows/src/main/kotlin/com/atlarge/opendc/model/services/workflows/stages/resources/ResourceDynamicFilterPolicy.kt deleted file mode 100644 index f73c0d9e..00000000 --- a/opendc-workflows/src/main/kotlin/com/atlarge/opendc/model/services/workflows/stages/resources/ResourceDynamicFilterPolicy.kt +++ /dev/null @@ -1,49 +0,0 @@ -/* - * MIT License - * - * Copyright (c) 2019 atlarge-research - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -package com.atlarge.opendc.model.services.workflows.stages.resources - -import com.atlarge.opendc.model.services.resources.HostView -import com.atlarge.opendc.model.services.workflows.StageWorkflowSchedulerLogic - -/** - * This interface represents the **R4** stage of the Reference Architecture for Schedulers and acts as a filter yielding - * a list of resources with sufficient resource-capacities, based on fixed or dynamic requirements, and on predicted or - * monitored information about processing unit availability, memory occupancy, etc. - */ -interface ResourceDynamicFilterPolicy { - /** - * Filter the list of machines based on dynamic information. - * - * @param scheduler The scheduler to filter the machines. - * @param machines The list of machines in the system. - * @param task The task that is to be scheduled. - * @return The machines on which the task can be scheduled. - */ - operator fun invoke( - scheduler: StageWorkflowSchedulerLogic, - machines: List, - task: StageWorkflowSchedulerLogic.TaskView - ): List -} diff --git a/opendc-workflows/src/main/kotlin/com/atlarge/opendc/model/services/workflows/stages/resources/ResourceSelectionPolicy.kt b/opendc-workflows/src/main/kotlin/com/atlarge/opendc/model/services/workflows/stages/resources/ResourceSelectionPolicy.kt deleted file mode 100644 index a9172a53..00000000 --- a/opendc-workflows/src/main/kotlin/com/atlarge/opendc/model/services/workflows/stages/resources/ResourceSelectionPolicy.kt +++ /dev/null @@ -1,48 +0,0 @@ -/* - * MIT License - * - * Copyright (c) 2019 atlarge-research - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -package com.atlarge.opendc.model.services.workflows.stages.resources - -import com.atlarge.opendc.model.services.resources.HostView -import com.atlarge.opendc.model.services.workflows.StageWorkflowSchedulerLogic - -/** - * This interface represents the **R5** stage of the Reference Architecture for Schedulers and matches the the selected - * task with a (set of) resource(s), using policies such as First-Fit, Worst-Fit, and Best-Fit. - */ -interface ResourceSelectionPolicy { - /** - * Select a machine on which the task should be scheduled. - * - * @param scheduler The scheduler to select the machine. - * @param machines The list of machines in the system. - * @param task The task that is to be scheduled. - * @return The selected machine or `null` if no machine could be found. - */ - fun select( - scheduler: StageWorkflowSchedulerLogic, - machines: List, - task: StageWorkflowSchedulerLogic.TaskView - ): HostView? -} diff --git a/opendc-workflows/src/main/kotlin/com/atlarge/opendc/model/services/workflows/stages/task/FifoTaskSortingPolicy.kt b/opendc-workflows/src/main/kotlin/com/atlarge/opendc/model/services/workflows/stages/task/FifoTaskSortingPolicy.kt deleted file mode 100644 index 2eb2f6fb..00000000 --- a/opendc-workflows/src/main/kotlin/com/atlarge/opendc/model/services/workflows/stages/task/FifoTaskSortingPolicy.kt +++ /dev/null @@ -1,37 +0,0 @@ -/* - * MIT License - * - * Copyright (c) 2019 atlarge-research - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -package com.atlarge.opendc.model.services.workflows.stages.task - -import com.atlarge.opendc.model.services.workflows.StageWorkflowSchedulerLogic - -/** - * The [FifoTaskSortingPolicy] sorts tasks based on the order of arrival in the queue. - */ -class FifoTaskSortingPolicy : TaskSortingPolicy { - override fun invoke( - scheduler: StageWorkflowSchedulerLogic, - tasks: Collection - ): List = tasks.toList() -} diff --git a/opendc-workflows/src/main/kotlin/com/atlarge/opendc/model/services/workflows/stages/task/FunctionalTaskEligibilityPolicy.kt b/opendc-workflows/src/main/kotlin/com/atlarge/opendc/model/services/workflows/stages/task/FunctionalTaskEligibilityPolicy.kt deleted file mode 100644 index 2e7cc8c1..00000000 --- a/opendc-workflows/src/main/kotlin/com/atlarge/opendc/model/services/workflows/stages/task/FunctionalTaskEligibilityPolicy.kt +++ /dev/null @@ -1,38 +0,0 @@ -/* - * MIT License - * - * Copyright (c) 2019 atlarge-research - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -package com.atlarge.opendc.model.services.workflows.stages.task - -import com.atlarge.opendc.model.resources.compute.scheduling.ProcessState -import com.atlarge.opendc.model.services.workflows.StageWorkflowSchedulerLogic - -/** - * A [TaskEligibilityPolicy] that marks tasks as eligible if they are tasks roots within the job. - */ -class FunctionalTaskEligibilityPolicy : TaskEligibilityPolicy { - override fun isEligible( - scheduler: StageWorkflowSchedulerLogic, - task: StageWorkflowSchedulerLogic.TaskView - ): Boolean = task.state == ProcessState.READY -} diff --git a/opendc-workflows/src/main/kotlin/com/atlarge/opendc/model/services/workflows/stages/task/RandomTaskSortingPolicy.kt b/opendc-workflows/src/main/kotlin/com/atlarge/opendc/model/services/workflows/stages/task/RandomTaskSortingPolicy.kt deleted file mode 100644 index 69462e41..00000000 --- a/opendc-workflows/src/main/kotlin/com/atlarge/opendc/model/services/workflows/stages/task/RandomTaskSortingPolicy.kt +++ /dev/null @@ -1,40 +0,0 @@ -/* - * MIT License - * - * Copyright (c) 2019 atlarge-research - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -package com.atlarge.opendc.model.services.workflows.stages.task - -import com.atlarge.opendc.model.services.workflows.StageWorkflowSchedulerLogic -import kotlin.random.Random - -/** - * The [RandomTaskSortingPolicy] sorts tasks randomly. - * - * @property random The [Random] instance to use when sorting the list of tasks. - */ -class RandomTaskSortingPolicy(private val random: Random = Random.Default) : TaskSortingPolicy { - override fun invoke( - scheduler: StageWorkflowSchedulerLogic, - tasks: Collection - ): List = tasks.shuffled(random) -} diff --git a/opendc-workflows/src/main/kotlin/com/atlarge/opendc/model/services/workflows/stages/task/TaskEligibilityPolicy.kt b/opendc-workflows/src/main/kotlin/com/atlarge/opendc/model/services/workflows/stages/task/TaskEligibilityPolicy.kt deleted file mode 100644 index c3c7e725..00000000 --- a/opendc-workflows/src/main/kotlin/com/atlarge/opendc/model/services/workflows/stages/task/TaskEligibilityPolicy.kt +++ /dev/null @@ -1,48 +0,0 @@ -/* - * MIT License - * - * Copyright (c) 2019 atlarge-research - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -package com.atlarge.opendc.model.services.workflows.stages.task - -import com.atlarge.opendc.model.services.workflows.StageWorkflowSchedulerLogic - -/** - * A policy interface for determining the eligibility of tasks in a scheduling cycle. - */ -interface TaskEligibilityPolicy { - /** - * A method that is invoked at the start of each scheduling cycle. - * - * @param scheduler The scheduler that started the cycle. - */ - fun startCycle(scheduler: StageWorkflowSchedulerLogic) {} - - /** - * Determine whether the specified [StageWorkflowSchedulerLogic.TaskView] is eligible to be scheduled. - * - * @param scheduler The scheduler that is determining whether the task is eligible. - * @param task The task instance to schedule. - * @return `true` if the task eligible to be scheduled, `false` otherwise. - */ - fun isEligible(scheduler: StageWorkflowSchedulerLogic, task: StageWorkflowSchedulerLogic.TaskView): Boolean -} diff --git a/opendc-workflows/src/main/kotlin/com/atlarge/opendc/model/services/workflows/stages/task/TaskSortingPolicy.kt b/opendc-workflows/src/main/kotlin/com/atlarge/opendc/model/services/workflows/stages/task/TaskSortingPolicy.kt deleted file mode 100644 index 3f296d0e..00000000 --- a/opendc-workflows/src/main/kotlin/com/atlarge/opendc/model/services/workflows/stages/task/TaskSortingPolicy.kt +++ /dev/null @@ -1,45 +0,0 @@ -/* - * MIT License - * - * Copyright (c) 2019 atlarge-research - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -package com.atlarge.opendc.model.services.workflows.stages.task - -import com.atlarge.opendc.model.services.workflows.StageWorkflowSchedulerLogic - -/** - * This interface represents the **T2** stage of the Reference Architecture for Datacenter Schedulers and provides the - * scheduler with a sorted list of tasks to schedule. - */ -interface TaskSortingPolicy { - /** - * Sort the given list of tasks on a given criterion. - * - * @param scheduler The scheduler that is sorting the tasks. - * @param tasks The collection of tasks that should be sorted. - * @return The sorted list of tasks. - */ - operator fun invoke( - scheduler: StageWorkflowSchedulerLogic, - tasks: Collection - ): List -} diff --git a/opendc-workflows/src/main/kotlin/com/atlarge/opendc/model/workload/workflow/Job.kt b/opendc-workflows/src/main/kotlin/com/atlarge/opendc/model/workload/workflow/Job.kt deleted file mode 100644 index dd72cf6d..00000000 --- a/opendc-workflows/src/main/kotlin/com/atlarge/opendc/model/workload/workflow/Job.kt +++ /dev/null @@ -1,48 +0,0 @@ -/* - * MIT License - * - * Copyright (c) 2019 atlarge-research - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -package com.atlarge.opendc.model.workload.workflow - -import com.atlarge.opendc.model.User -import com.atlarge.opendc.model.workload.Workload -import java.util.UUID - -/** - * A workload that represents a directed acyclic graph (DAG) of tasks with control and data dependencies between tasks. - * - * @property uid A unique identified of this workflow. - * @property name The name of this workflow. - * @property owner The owner of the workflow. - * @property tasks The tasks that are part of this workflow. - */ -data class Job( - override val uid: UUID, - override val name: String, - override val owner: User, - val tasks: Set -) : Workload { - override fun equals(other: Any?): Boolean = other is Job && uid == other.uid - - override fun hashCode(): Int = uid.hashCode() -} diff --git a/opendc-workflows/src/main/kotlin/com/atlarge/opendc/model/workload/workflow/Task.kt b/opendc-workflows/src/main/kotlin/com/atlarge/opendc/model/workload/workflow/Task.kt deleted file mode 100644 index 0cc3fa0e..00000000 --- a/opendc-workflows/src/main/kotlin/com/atlarge/opendc/model/workload/workflow/Task.kt +++ /dev/null @@ -1,48 +0,0 @@ -/* - * MIT License - * - * Copyright (c) 2019 atlarge-research - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -package com.atlarge.opendc.model.workload.workflow - -import com.atlarge.opendc.model.Identity -import com.atlarge.opendc.model.workload.application.Application -import java.util.UUID - -/** - * A stage of a [Job]. - * - * @property uid A unique identified of this task. - * @property name The name of this task. - * @property application The application to run as part of this workflow task. - * @property dependencies The dependencies of this task in order for it to execute. - */ -data class Task( - override val uid: UUID, - override val name: String, - val application: Application, - val dependencies: Set -) : Identity { - override fun equals(other: Any?): Boolean = other is Task && uid == other.uid - - override fun hashCode(): Int = uid.hashCode() -} diff --git a/settings.gradle.kts b/settings.gradle.kts index 085885ba..5abdd4ab 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -23,13 +23,6 @@ */ rootProject.name = "opendc-simulator" -include(":odcsim-core") -include(":odcsim-engine-tests") -include(":odcsim-engine-omega") -include(":odcsim-testkit") -include(":opendc-core") -include(":opendc-experiments-tpds") -include(":opendc-format") -include(":opendc-format-gwf") -include(":opendc-format-sc18") -include(":opendc-workflows") +include(":odcsim:odcsim-core") +include(":odcsim:odcsim-engine-tests") +include(":odcsim:odcsim-engine-omega") -- cgit v1.2.3