From 536a764197fad0cc6e597c4fb7a16aab450a4702 Mon Sep 17 00:00:00 2001 From: Matthew Dailis Date: Tue, 26 Mar 2024 20:18:55 -0700 Subject: [PATCH] Remove set-based implementation --- .../driver/engine/SimulationEngine.java | 178 ++---------------- 1 file changed, 12 insertions(+), 166 deletions(-) diff --git a/merlin-driver/src/main/java/gov/nasa/jpl/aerie/merlin/driver/engine/SimulationEngine.java b/merlin-driver/src/main/java/gov/nasa/jpl/aerie/merlin/driver/engine/SimulationEngine.java index 3c34c958e4..2275f13ff9 100644 --- a/merlin-driver/src/main/java/gov/nasa/jpl/aerie/merlin/driver/engine/SimulationEngine.java +++ b/merlin-driver/src/main/java/gov/nasa/jpl/aerie/merlin/driver/engine/SimulationEngine.java @@ -75,7 +75,6 @@ public final class SimulationEngine implements AutoCloseable { private final Map spans = new HashMap<>(); /** A count of the direct contributors to each span, including child spans and tasks. */ private final Map spanContributorCount = new HashMap<>(); - private final SpanContributors spanContributors = new SpanContributors(); /** A thread pool that modeled tasks can use to keep track of their state between steps. */ private final ExecutorService executor = Executors.newVirtualThreadPerTaskExecutor(); @@ -88,14 +87,10 @@ public SpanId scheduleTask(final Duration startTime, final TaskFactory< this.spans.put(span, new Span(Optional.empty(), startTime, Optional.empty())); final var task = TaskId.generate(); - this.spanContributors.newSpan(span); - this.spanContributors.addContributor(span, task); this.spanContributorCount.put(span, new MutableInt(1)); this.tasks.put(task, new ExecutionState<>(span, 0, Optional.empty(), state.create(this.executor))); this.scheduledJobs.schedule(JobId.forTask(task), SubInstant.Tasks.at(startTime)); - this.spanContributors.checkIntegrity(spanContributorCount); - return span; } @@ -206,44 +201,19 @@ private void stepEffectModel( if (status instanceof TaskStatus.Completed) { // Propagate completion up the span hierarchy. // TERMINATION: The span hierarchy is a finite tree, so eventually we find a parentless span. - { - var span = scheduler.span; - while (true) { - if (this.spanContributorCount.get(span).decrementAndGet() > 0) break; - this.spanContributorCount.remove(span); - - this.spans.compute(span, (_id, $) -> $.close(currentTime)); + var span = scheduler.span; + while (true) { + if (this.spanContributorCount.get(span).decrementAndGet() > 0) break; + this.spanContributorCount.remove(span); - final var span$ = this.spans.get(span).parent; - if (span$.isEmpty()) break; + this.spans.compute(span, (_id, $) -> $.close(currentTime)); - span = span$.get(); - } - } + final var span$ = this.spans.get(span).parent; + if (span$.isEmpty()) break; - { - var span = scheduler.span; - this.spanContributors.removeContributor(span, task); - - if (!this.spanContributors.spanHasContributors(span)) { - this.spanContributors.removeSpan(span); - - if (this.spans.get(span).parent.isPresent()) { - var parentSpan = this.spans.get(span).parent.get(); - while (true) { - this.spanContributors.removeContributor(parentSpan, span); - if (this.spanContributors.spanHasContributors(parentSpan)) break; - this.spanContributors.removeSpan(parentSpan); - span = parentSpan; - final var parentSpan$ = this.spans.get(span).parent; - if (parentSpan$.isEmpty()) break; - parentSpan = parentSpan$.get(); - } - } - } + span = span$.get(); } - this.spanContributors.checkIntegrity(this.spanContributorCount); // Notify any blocked caller of our completion. progress.caller().ifPresent($ -> { @@ -260,14 +230,11 @@ private void stepEffectModel( } else if (status instanceof TaskStatus.CallingTask s) { final var target = TaskId.generate(); SimulationEngine.this.spanContributorCount.get(scheduler.span).increment(); - SimulationEngine.this.spanContributors.addContributor(scheduler.span, target); SimulationEngine.this.tasks.put(target, new ExecutionState<>(scheduler.span, 0, Optional.of(task), s.child().create(this.executor))); SimulationEngine.this.blockedTasks.put(task, new MutableInt(1)); frame.signal(JobId.forTask(target)); this.tasks.put(task, progress.continueWith(scheduler.span, scheduler.shadowedSpans, s.continuation())); - - SimulationEngine.this.spanContributors.checkIntegrity(spanContributorCount); } else if (status instanceof TaskStatus.AwaitingCondition s) { final var condition = ConditionId.generate(); this.conditions.put(condition, s.condition()); @@ -727,13 +694,10 @@ public void emit(final EventType event, final Topic topic @Override public void spawn(final TaskFactory state) { final var task = TaskId.generate(); - SimulationEngine.this.spanContributors.addContributor(this.span, task); SimulationEngine.this.spanContributorCount.get(this.span).increment(); SimulationEngine.this.tasks.put(task, new ExecutionState<>(this.span, 0, this.caller, state.create(SimulationEngine.this.executor))); this.caller.ifPresent($ -> SimulationEngine.this.blockedTasks.get($).increment()); this.frame.signal(JobId.forTask(task)); - - SimulationEngine.this.spanContributors.checkIntegrity(spanContributorCount); } @Override @@ -743,35 +707,22 @@ public void pushSpan() { this.span = SpanId.generate(); SimulationEngine.this.spans.put(this.span, new Span(Optional.of(parentSpan), this.currentTime, Optional.empty())); - SimulationEngine.this.spanContributors.removeContributor(parentSpan, this.taskId); - SimulationEngine.this.spanContributors.addContributor(parentSpan, this.span); - SimulationEngine.this.spanContributors.newSpan(span); - SimulationEngine.this.spanContributors.addContributor(span, this.taskId); SimulationEngine.this.spanContributorCount.put(this.span, new MutableInt(1)); - - SimulationEngine.this.spanContributors.checkIntegrity(spanContributorCount); } @Override public void popSpan() { - SimulationEngine.this.spanContributors.checkIntegrity(spanContributorCount); - // TODO: Do we want to throw an error instead? if (this.shadowedSpans == 0) return; final SpanId parentSpan = SimulationEngine.this.spans.get(this.span).parent().orElseThrow(); - SimulationEngine.this.spanContributors.removeContributor(this.span, this.taskId); - - boolean closedSpan = false; - if (SimulationEngine.this.spanContributorCount.get(this.span).decrementAndGet() == 0) { - closedSpan = true; SimulationEngine.this.spanContributorCount.remove(this.span); SimulationEngine.this.spans.compute(this.span, (_id, $) -> $.close(currentTime)); - } - if (!SimulationEngine.this.spanContributors.spanHasContributors(this.span)) { - SimulationEngine.this.spanContributors.removeContributor(parentSpan, this.span); - SimulationEngine.this.spanContributors.removeSpan(this.span); + // Parent contributor count remains constant, because we are subtracting one (this.span) and adding one (the current task) + } else { + // Parent contributor count grows by one, since the span remains open, and this task begins to contribute to the parent span + SimulationEngine.this.spanContributorCount.get(parentSpan).increment(); } // NOTE: We don't need to propagate completion any further, because the next shadowed span @@ -780,13 +731,6 @@ public void popSpan() { this.shadowedSpans -= 1; this.span = parentSpan; - SimulationEngine.this.spanContributors.addContributor(this.span, this.taskId); - - if (!closedSpan) { - SimulationEngine.this.spanContributorCount.get(parentSpan).increment(); - } - - SimulationEngine.this.spanContributors.checkIntegrity(spanContributorCount); } } @@ -844,102 +788,4 @@ public boolean isComplete() { return this.endOffset.isPresent(); } } - - sealed interface TaskOrSpanId { - record Task(TaskId id) implements TaskOrSpanId {} - record Span(SpanId id) implements TaskOrSpanId {} - - static TaskOrSpanId of(SpanId spanId) { - return new Span(spanId); - } - - static TaskOrSpanId of(TaskId taskId) { - return new Task(taskId); - } - } - - public static class SpanContributors { - private final Map> spanContributors; - private final List operations = new ArrayList<>(); - - public SpanContributors() { - this.spanContributors = new LinkedHashMap<>(); - } - - public void newSpan(SpanId span) { - if (this.spanContributors.containsKey(span)) throw new IllegalStateException("New span clashes with existing span"); - this.spanContributors.put(span, new LinkedHashSet<>()); - operations.add("newSpan " + span); - } - - public void removeContributor(SpanId span, TaskId taskId) { - removeContributor(span, TaskOrSpanId.of(taskId)); - } - - public void removeContributor(SpanId span, SpanId spanId) { - removeContributor(span, TaskOrSpanId.of(spanId)); - } - - private void removeContributor(SpanId span, TaskOrSpanId contributor) { - if (!this.spanContributors.containsKey(span)) throw new IllegalArgumentException("No such span"); - if (!this.spanContributors.get(span).contains(contributor)) throw new IllegalArgumentException("Span did not have this contributor"); - this.spanContributors.get(span).remove(contributor); - operations.add("removeContributor " + span + " " + contributor); - } - - public boolean spanHasContributors(SpanId span) { - if (!this.spanContributors.containsKey(span)) throw new IllegalArgumentException("No such span"); - return !spanContributors.get(span).isEmpty(); - } - - public void removeSpan(SpanId span) { - if (!this.spanContributors.containsKey(span)) throw new IllegalArgumentException("No such span"); - if (!this.spanContributors.get(span).isEmpty()) throw new IllegalStateException("Cannot remove span that still has contributors"); - - this.spanContributors.remove(span); - operations.add("removeSpan " + span); - } - - public void checkIntegrity(final Map spanContributorsCount) { - Set contributors = new LinkedHashSet<>(); - for (final var entry : this.spanContributors.entrySet()) { - if (entry.getValue().isEmpty()) throw new IllegalStateException("Empty contributor not removed"); - for (final var contributor : entry.getValue()) { - if (contributors.contains(contributor)) throw new IllegalStateException("Contributor appears twice"); - contributors.add(contributor); - if (!(contributor instanceof TaskOrSpanId.Span c)) continue; - if (!this.spanContributors.containsKey(c.id())) { - throw new IllegalStateException("Orphaned contributor"); - } - } - } - - for (final var entry : spanContributorsCount.entrySet()) { - if (!this.spanContributors.containsKey(entry.getKey())) throw new IllegalArgumentException("Mismatch!"); - if (!entry.getValue().getValue().equals(this.spanContributors.get(entry.getKey()).size())) { - throw new IllegalArgumentException("Mismatch! " + entry.getValue().getValue() + " != " + this.spanContributors.get(entry.getKey()).size()); - } - } - - for (final var key : this.spanContributors.keySet()) { - if (!spanContributorsCount.containsKey(key)) throw new IllegalArgumentException("Mismatch!"); - } - - operations.clear(); - } - - public void addContributor(SpanId span, TaskId contributor) { - addContributor(span, TaskOrSpanId.of(contributor)); - } - - public void addContributor(SpanId span, SpanId contributor) { - addContributor(span, TaskOrSpanId.of(contributor)); - } - - private void addContributor(SpanId span, TaskOrSpanId contributor) { - if (!this.spanContributors.containsKey(span)) throw new IllegalArgumentException("No such span"); - this.spanContributors.get(span).add(contributor); - operations.add("addContributor " + span + " " + contributor); - } - } }