X-Git-Url: https://mograsim.net/gitweb/?a=blobdiff_plain;f=net.mograsim.logic.core%2Fsrc%2Fnet%2Fmograsim%2Flogic%2Fcore%2Ftimeline%2FTimeline.java;h=9dbc31d4d40d4c0573ca647eb23bfc1388c37223;hb=8bed58cd47f4e53a0a83e066d38864aa6875502f;hp=fb639bed468d535bd42941c2234b83e1fcb63b0f;hpb=07faf07e3acb8b2afdc2bf65a46bc868faaed0f8;p=Mograsim.git diff --git a/net.mograsim.logic.core/src/net/mograsim/logic/core/timeline/Timeline.java b/net.mograsim.logic.core/src/net/mograsim/logic/core/timeline/Timeline.java index fb639bed..9dbc31d4 100644 --- a/net.mograsim.logic.core/src/net/mograsim/logic/core/timeline/Timeline.java +++ b/net.mograsim.logic.core/src/net/mograsim/logic/core/timeline/Timeline.java @@ -18,26 +18,37 @@ public class Timeline private PriorityQueue events; private LongSupplier time; private long lastTimeUpdated = 0; + private long eventCounter = 0; private final List> eventAddedListener; + public final LongSupplier stepByStepExec = () -> lastTimeUpdated; + public final LongSupplier realTimeExec = () -> System.currentTimeMillis(); + + /** + * Constructs a Timeline object. Per default the time function is set to step by step execution. + * + * @param initCapacity The initial capacity of the event queue. + */ public Timeline(int initCapacity) { - events = new PriorityQueue(initCapacity); - + events = new PriorityQueue<>(initCapacity); eventAddedListener = new ArrayList<>(); - time = () -> lastTimeUpdated; + time = stepByStepExec; } /** * @param timestamp exclusive - * @return true if the first event is later than the timestamp + * @return true if the first event in queue is later than the given timestamp */ public BooleanSupplier laterThan(long timestamp) { return () -> timeCmp(events.peek().getTiming(), timestamp) > 0; } + /** + * @return true if there is at least one event enqueued. false otherwise + */ public boolean hasNext() { return !events.isEmpty(); @@ -53,6 +64,9 @@ public class Timeline executeUntil(laterThan(first.getTiming()), -1); } + /** + * Executes all events enqueued in the {@link Timeline}. Use very carefully! Events may generate new events, causing an infinite loop. + */ public void executeAll() { while (hasNext()) @@ -71,7 +85,6 @@ public class Timeline * EXEC_UNTIL_CONDITION if the condition was met * EXEC_UNTIL_EMPTY if events were executed until the {@link Timeline} was empty * @formatter:on - * @author Christian Femers, Fabian Stemmler */ public ExecutionResult executeUntil(BooleanSupplier condition, long stopMillis) { @@ -81,32 +94,49 @@ public class Timeline return ExecutionResult.NOTHING_DONE; } int checkStop = 0; - InnerEvent first = events.peek(); while (hasNext() && !condition.getAsBoolean()) { - events.remove(); - lastTimeUpdated = first.getTiming(); - first.run(); + InnerEvent event; + synchronized (events) + { + event = events.remove(); + } + lastTimeUpdated = event.getTiming(); + event.run(); // Don't check after every run checkStop = (checkStop + 1) % 10; if (checkStop == 0 && System.currentTimeMillis() >= stopMillis) return ExecutionResult.EXEC_OUT_OF_TIME; - first = events.peek(); } lastTimeUpdated = getSimulationTime(); return hasNext() ? ExecutionResult.EXEC_UNTIL_EMPTY : ExecutionResult.EXEC_UNTIL_CONDITION; } + /** + * Sets the function, which defines the current simulation time at any time. + * + * @param time The return value of calling this function is the current simulation time. + */ public void setTimeFunction(LongSupplier time) { this.time = time; } + /** + * Calculates the current simulation time. + * + * @return The simulation time as defined by the time function. + */ public long getSimulationTime() { return time.getAsLong(); } + /** + * Retrieves the timestamp of the next event. + * + * @return The timestamp of the next enqueued event, if the {@link Timeline} is not empty, -1 otherwise. + */ public long nextEventTime() { if (!hasNext()) @@ -114,17 +144,29 @@ public class Timeline return events.peek().getTiming(); } + /** + * Clears the {@link Timeline} of enqueued events. + */ public void reset() { - events.clear(); + synchronized (events) + { + events.clear(); + } lastTimeUpdated = 0; } + /** + * Adds a listener, that is called when a {@link TimelineEvent} is added. + */ public void addEventAddedListener(Consumer listener) { eventAddedListener.add(listener); } + /** + * Removes the listener, if possible. It will no longer be called when a {@link TimelineEvent} is added. + */ public void removeEventAddedListener(Consumer listener) { eventAddedListener.remove(listener); @@ -140,7 +182,10 @@ public class Timeline { long timing = getSimulationTime() + relativeTiming; TimelineEvent event = new TimelineEvent(timing); - events.add(new InnerEvent(function, event)); + synchronized (events) + { + events.add(new InnerEvent(function, event, eventCounter++)); + } eventAddedListener.forEach(l -> l.accept(event)); } @@ -148,6 +193,7 @@ public class Timeline { private final TimelineEventHandler function; private final TimelineEvent event; + private final long id; /** * Creates an {@link InnerEvent} @@ -155,10 +201,11 @@ public class Timeline * @param function {@link TimelineEventHandler} to be executed when the {@link InnerEvent} occurs * @param timing Point in the MI simulation {@link Timeline}, at which the {@link InnerEvent} is executed; */ - InnerEvent(TimelineEventHandler function, TimelineEvent event) + InnerEvent(TimelineEventHandler function, TimelineEvent event, long id) { this.function = function; this.event = event; + this.id = id; } public long getTiming() @@ -181,7 +228,8 @@ public class Timeline @Override public int compareTo(InnerEvent o) { - return timeCmp(getTiming(), o.getTiming()); + int c1; + return (c1 = timeCmp(getTiming(), o.getTiming())) == 0 ? timeCmp(id, o.id) : c1; } } @@ -193,7 +241,12 @@ public class Timeline @Override public String toString() { - return String.format("Simulation time: %s, Last update: %d, Events: %s", getSimulationTime(), lastTimeUpdated, events.toString()); + String eventsString; + synchronized (events) + { + eventsString = events.toString(); + } + return String.format("Simulation time: %s, Last update: %d, Events: %s", getSimulationTime(), lastTimeUpdated, eventsString); } public enum ExecutionResult