/** * Copyright (c) 2016 committers of YAKINDU and others. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html * * Contributors: * committers of YAKINDU - initial API and implementation */ package org.yakindu.sct.simulation.core.sexec.interpreter; import java.util.Collection; import java.util.HashMap; import java.util.Map; import java.util.Map.Entry; import java.util.Set; import java.util.concurrent.Executors; import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.ScheduledFuture; import java.util.concurrent.TimeUnit; import org.yakindu.sct.simulation.core.sruntime.ExecutionContext; import com.google.inject.Singleton; /** * Implementation of {@link ITimingService} interface using * {@link ScheduledExecutorService} * * @author andreas muelder * @author thomas kutz * */ @Singleton public class SuspendableTimingService implements ITimingService { protected static class ScheduleEventRunnable implements Runnable { private final ExecutionContext context; private final String eventName; private boolean isPeriodical; private long initialDuration; private long duration; public ScheduleEventRunnable(ExecutionContext context, String eventName, boolean isPeriodical, long duration) { this(context, eventName, isPeriodical, duration, duration); } public ScheduleEventRunnable(ExecutionContext context, String eventName, boolean isPeriodical, long initialDuration, long duration) { this.context = context; this.eventName = eventName; this.isPeriodical = isPeriodical; this.initialDuration = initialDuration; this.duration = duration; } public void run() { context.getEvent(eventName).setScheduled(true); } public ExecutionContext getContext() { return context; } public String getEventName() { return eventName; } public boolean isPeriodical() { return isPeriodical; } public long getDuration() { return duration; } public long getInitialDuration() { return initialDuration; } public void setInitialDuration(long initialDuration) { this.initialDuration = initialDuration; } } private ScheduledExecutorService scheduler; private Map<String, ScheduleEventRunnable> runnables; private Map<String, ScheduledFuture<?>> futures; public SuspendableTimingService() { scheduler = Executors.newScheduledThreadPool(1); runnables = new HashMap<String, ScheduleEventRunnable>(); futures = new HashMap<String, ScheduledFuture<?>>(); } public synchronized void scheduleTimeEvent(ExecutionContext context, String eventName, boolean isPeriodical, long duration) { if (duration <= 0) duration = 1; ScheduleEventRunnable eventRunnable = new ScheduleEventRunnable(context, eventName, isPeriodical, duration); scheduleTimeEvent(eventRunnable); } protected void scheduleTimeEvent(ScheduleEventRunnable eventRunnable) { ScheduledFuture<?> future = null; if (scheduler.isShutdown()) { scheduler = Executors.newScheduledThreadPool(1); } if (eventRunnable.isPeriodical) { future = scheduler.scheduleAtFixedRate(eventRunnable, eventRunnable.initialDuration, eventRunnable.duration, TimeUnit.MILLISECONDS); } else { future = scheduler.schedule(eventRunnable, eventRunnable.initialDuration, TimeUnit.MILLISECONDS); } runnables.put(eventRunnable.getEventName(), eventRunnable); futures.put(eventRunnable.getEventName(), future); } public synchronized void unscheduleTimeEvent(String eventName) { ScheduledFuture<?> future = futures.remove(eventName); if(future != null) future.cancel(false); runnables.remove(eventName); } public synchronized void pause() { Set<Entry<String, ScheduledFuture<?>>> entrySet = futures.entrySet(); for (Entry<String, ScheduledFuture<?>> entry : entrySet) { ScheduledFuture<?> future = entry.getValue(); long delay = future.getDelay(TimeUnit.MILLISECONDS); future.cancel(false); ScheduleEventRunnable runnable = runnables.get(entry.getKey()); runnable.setInitialDuration(delay); } futures.clear(); } public synchronized void resume() { for (ScheduleEventRunnable event : runnables.values()) { scheduleTimeEvent(event); } } public synchronized void stop() { Collection<ScheduledFuture<?>> values = futures.values(); for (ScheduledFuture<?> scheduledFuture : values) { scheduledFuture.cancel(false); } futures.clear(); runnables.clear(); scheduler.shutdownNow(); } }