/******************************************************************************* * * Overture. * * Author: Kenneth Lausdahl * * This file is part of VDMJ. * * VDMJ is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * VDMJ is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with VDMJ. If not, see <http://www.gnu.org/licenses/>. * ******************************************************************************/ package org.overture.interpreter.scheduler; import java.io.Serializable; import java.util.concurrent.BlockingQueue; import java.util.concurrent.RejectedExecutionException; import java.util.concurrent.RejectedExecutionHandler; import java.util.concurrent.SynchronousQueue; import java.util.concurrent.ThreadPoolExecutor; import java.util.concurrent.TimeUnit; import org.overture.ast.intf.lex.ILexLocation; import org.overture.ast.lex.Dialect; import org.overture.ast.messages.InternalException; import org.overture.config.Settings; import org.overture.interpreter.messages.rtlog.RTExtendedTextMessage; import org.overture.interpreter.messages.rtlog.RTLogger; import org.overture.interpreter.runtime.Context; import org.overture.interpreter.values.ObjectValue; import org.overture.parser.config.Properties; public abstract class SchedulablePoolThread implements Serializable, Runnable, ISchedulableThread { /** * VdmThreadPoolExecutor used to set the Thread instance which will run a SchedulablePoolThread just before * execution. It also reports a reject error if the pool no longer can expand to handle the requested number of * threads * * @author kela */ public static class VdmThreadPoolExecutor extends ThreadPoolExecutor { public VdmThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue<Runnable> workQueue) { super(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue, new VdmjRejectedExecutionHandler()); } @Override protected void beforeExecute(Thread t, Runnable r) { super.beforeExecute(t, r); if (r instanceof SchedulablePoolThread) { SchedulablePoolThread spt = (SchedulablePoolThread) r; spt.setThread(t); t.setName(spt.getName()); } } @Override protected void afterExecute(Runnable r, Throwable t) { if (r instanceof SchedulablePoolThread) { SchedulablePoolThread spt = (SchedulablePoolThread) r; spt.setThread(null); } } /** * Prints an error message if a execution is rejected * * @author kela */ private static class VdmjRejectedExecutionHandler implements RejectedExecutionHandler { public void rejectedExecution(Runnable r, ThreadPoolExecutor executor) { System.err.println("Thread pool rejected thread: " + ((ISchedulableThread) r).getName() + " pool size " + executor.getActiveCount()); throw new RejectedExecutionException(); } } } private static final long serialVersionUID = 1L; protected final Resource resource; protected final ObjectValue object; private final boolean periodic; private final boolean virtual; protected RunState state; private Signal signal; private long timeslice; private long steps; private long timestep; private long durationEnd; private long alarmWakeTime; private long swapInBy; private boolean inOuterTimeStep; private Thread executingThread; private char name[]; private long tid = 0; protected boolean stopCalled; /** * Thread pool used by SchedulablePoolThread. It is a none blocking queue with an upper limit set to * Integer.MAX_VALUE allowing it to freely expand. The thread pool will most likely make the Java VM throw * OutOfMemoryError before Integer.MAX_VALUE is reached do the the native thread creation requiring 2 MB for each * thread. <a href="http://java.sun.com/j2se/1.5.0/docs/api/java/util/concurrent/ThreadPoolExecutor.html">link</a> */ public final static VdmThreadPoolExecutor pool = new VdmThreadPoolExecutor(200, Integer.MAX_VALUE, 60L, TimeUnit.SECONDS, new SynchronousQueue<Runnable>());// LinkedBlockingQueue public SchedulablePoolThread(Resource resource, ObjectValue object, long priority, boolean periodic, long swapInBy) { this.tid = BasicSchedulableThread.nextThreadID(); this.resource = resource; this.object = object; this.periodic = periodic; this.virtual = resource.isVirtual(); this.setSwapInBy(swapInBy); state = RunState.CREATED; signal = null; timeslice = 0; steps = 0; timestep = Long.MAX_VALUE; durationEnd = 0; alarmWakeTime = Long.MAX_VALUE; inOuterTimeStep = false; stopCalled = false; resource.register(this, priority); BasicSchedulableThread.add(this); } private void setThread(Thread t) { executingThread = t; } public Thread getThread() { return this.executingThread; } /* * (non-Javadoc) * @see org.overture.vdmj.scheduler.ISchedulableThread#equals(java.lang.Object) */ @Override public boolean equals(Object other) { if (other instanceof ISchedulableThread) { ISchedulableThread to = (ISchedulableThread) other; return getId() == to.getId(); } return false; } /* * (non-Javadoc) * @see org.overture.vdmj.scheduler.ISchedulableThread#hashCode() */ @Override public int hashCode() { return new Long(getId()).hashCode(); } /* * (non-Javadoc) * @see org.overture.vdmj.scheduler.ISchedulableThread#toString() */ @Override public String toString() { return getName() + " (" + (stopCalled ? "STOPPING" : state) + ")"; } /* * (non-Javadoc) * @see org.overture.vdmj.scheduler.ISchedulableThread#start() */ public synchronized void start() { pool.execute(this); while (state == RunState.CREATED) { sleep(null, null); } // Log the creation here so that it is deterministic... if (resource instanceof CPUResource) { CPUResource cpu = (CPUResource) resource; cpu.createThread(this); } } /* * (non-Javadoc) * @see org.overture.vdmj.scheduler.ISchedulableThread#run() */ public void run() { try { reschedule(null, null); body(); } finally { setState(RunState.COMPLETE); resource.unregister(this); BasicSchedulableThread.remove(this); getThread().setName("pool-" + getThread().getId() + "-thread-"); } } abstract protected void body(); /* * (non-Javadoc) * @see org.overture.vdmj.scheduler.ISchedulableThread#step(org.overture.vdmj.runtime.Context, * org.overture.vdmj.lex.ILexLocation) */ public void step(Context ctxt, ILexLocation location) { if (Settings.dialect == Dialect.VDM_RT) { if (!virtual) { duration(getObject().getCPU().getDuration(Properties.rt_cycle_default), ctxt, location); } } else { SystemClock.advance(Properties.rt_duration_default); } // Note that we don't reschedule if we are in an outer cycles/duration if (++steps >= timeslice && !inOuterTimeStep) { reschedule(ctxt, location); steps = 0; } } /* * (non-Javadoc) * @see org.overture.vdmj.scheduler.ISchedulableThread#getRunState() */ public synchronized RunState getRunState() { return state; } /* * (non-Javadoc) * @see org.overture.vdmj.scheduler.ISchedulableThread#setState(org.overture.vdmj.scheduler.RunState) */ public synchronized void setState(RunState newstate) { state = newstate; notifyAll(); } public synchronized void reschedule(Context ctxt, ILexLocation location) { // Yield control but remain runnable - called by thread waitUntilState(RunState.RUNNABLE, RunState.RUNNING, ctxt, location); } /* * (non-Javadoc) * @see org.overture.vdmj.scheduler.ISchedulableThread#waiting(org.overture.vdmj.runtime.Context, * org.overture.vdmj.lex.ILexLocation) */ public synchronized void waiting(Context ctxt, ILexLocation location) { // Enter a waiting state - called by thread waitUntilState(RunState.WAITING, RunState.RUNNING, ctxt, location); } /* * (non-Javadoc) * @see org.overture.vdmj.scheduler.ISchedulableThread#locking(org.overture.vdmj.runtime.Context, * org.overture.vdmj.lex.ILexLocation) */ public synchronized void locking(Context ctxt, ILexLocation location) { // Enter a locking state - called by thread waitUntilState(RunState.LOCKING, RunState.RUNNING, ctxt, location); } /* * (non-Javadoc) * @see org.overture.vdmj.scheduler.ISchedulableThread#alarming(long, Context, ILexLocation) */ public synchronized void alarming(long expected) { // Enter an alarming state - called by another thread, does not block. alarmWakeTime = expected; setState(RunState.ALARM); } /* * (non-Javadoc) * @see org.overture.vdmj.scheduler.ISchedulableThread#runslice(long) */ public synchronized void runslice(long slice) { // Run one time slice - called by Scheduler timeslice = slice; waitWhileState(RunState.RUNNING, RunState.RUNNING, null, null); } /* * (non-Javadoc) * @see org.overture.vdmj.scheduler.ISchedulableThread#duration(long, org.overture.vdmj.runtime.Context, * org.overture.vdmj.lex.ILexLocation) */ public synchronized void duration(long pause, Context ctxt, ILexLocation location) { // Wait until pause has passed - called by thread if (!inOuterTimeStep) { setTimestep(pause); durationEnd = SystemClock.getWallTime() + pause; do { if (Properties.diags_timestep) { RTLogger.log(new RTExtendedTextMessage(String.format("-- %s Waiting to move time by %d", this, timestep))); } waitUntilState(RunState.TIMESTEP, RunState.RUNNING, ctxt, location); setTimestep(durationEnd - SystemClock.getWallTime()); } while (getTimestep() > 0); setTimestep(Long.MAX_VALUE); // Finished } } private synchronized void waitWhileState(RunState newstate, RunState whilestate, Context ctxt, ILexLocation location) { setState(newstate); while (state == whilestate) { sleep(ctxt, location); } } private synchronized void waitUntilState(RunState newstate, RunState until, Context ctxt, ILexLocation location) { setState(newstate); while (state != until) { sleep(ctxt, location); } } private synchronized void sleep(Context ctxt, ILexLocation location) { while (true) { try { wait(); if (stopCalled && state == RunState.RUNNING) { // stopThread made us RUNNABLE, now we're running, so die throw new ThreadDeath(); } return; } catch (InterruptedException e) { if (signal != null) { handleSignal(signal, ctxt, location); } } } } protected void handleSignal(Signal sig, Context ctxt, ILexLocation location) { BasicSchedulableThread.handleSignal(sig, ctxt, location); } /* * (non-Javadoc) * @see org.overture.vdmj.scheduler.ISchedulableThread#suspendOthers() */ public void suspendOthers() { BasicSchedulableThread.suspendOthers(this); } public void setExceptionOthers() { BasicSchedulableThread.setExceptionOthers(this); } public static void signalAll(Signal sig) { BasicSchedulableThread.signalAll(sig); } public synchronized boolean stopThread() { if (!stopCalled) { stopCalled = true; timestep = Long.MAX_VALUE; // Don't take part in time step if (Thread.currentThread() != this.getThread()) { setState(RunState.RUNNABLE); // So that thread is rescheduled } return true; } else { return false; } } public synchronized void setSignal(Signal sig) { signal = sig; interrupt(); } private void interrupt() { if (getThread() != null) { getThread().interrupt(); } else { Thread.currentThread().interrupt(); } } /* * (non-Javadoc) * @see org.overture.vdmj.scheduler.ISchedulableThread#getObject() */ public ObjectValue getObject() { return object; } /* * (non-Javadoc) * @see org.overture.vdmj.scheduler.ISchedulableThread#setSwapInBy(long) */ public synchronized void setSwapInBy(long swapInBy) { this.swapInBy = swapInBy; } /* * (non-Javadoc) * @see org.overture.vdmj.scheduler.ISchedulableThread#getSwapInBy() */ public synchronized long getSwapInBy() { return swapInBy; } /* * (non-Javadoc) * @see org.overture.vdmj.scheduler.ISchedulableThread#isPeriodic() */ public boolean isPeriodic() { return periodic; } /* * (non-Javadoc) * @see org.overture.vdmj.scheduler.ISchedulableThread#isActive() */ public boolean isActive() { return state == RunState.TIMESTEP || state == RunState.WAITING; } /* * (non-Javadoc) * @see org.overture.vdmj.scheduler.ISchedulableThread#isVirtual() */ public boolean isVirtual() { return virtual; } /* * (non-Javadoc) * @see org.overture.vdmj.scheduler.ISchedulableThread#setTimestep(long) */ public synchronized void setTimestep(long step) { timestep = step; } /* * (non-Javadoc) * @see org.overture.vdmj.scheduler.ISchedulableThread#getTimestep() */ public synchronized long getTimestep() { return timestep; } /* * (non-Javadoc) * @see org.overture.vdmj.scheduler.ISchedulableThread#getDurationEnd() */ public synchronized long getDurationEnd() { return durationEnd; } /* * (non-Javadoc) * @see org.overture.vdmj.scheduler.ISchedulableThread#getAlarmWakeTime() */ public synchronized long getAlarmWakeTime() { return alarmWakeTime; } /* * (non-Javadoc) * @see org.overture.vdmj.scheduler.ISchedulableThread#clearAlarm() */ public void clearAlarm() { alarmWakeTime = Long.MAX_VALUE; } /* * (non-Javadoc) * @see org.overture.vdmj.scheduler.ISchedulableThread#inOuterTimestep(boolean) */ public synchronized void inOuterTimestep(boolean b) { inOuterTimeStep = b; } /* * (non-Javadoc) * @see org.overture.vdmj.scheduler.ISchedulableThread#inOuterTimestep() */ public synchronized boolean inOuterTimestep() { return inOuterTimeStep; } /* * (non-Javadoc) * @see org.overture.vdmj.scheduler.ISchedulableThread#getCPUResource() */ public CPUResource getCPUResource() { if (resource instanceof CPUResource) { return (CPUResource) resource; } else { throw new InternalException(66, "Thread is not running on a CPU"); } } public long getId() { return tid; } public final String getName() { return String.valueOf(name); } public final void setName(String name) { this.name = name.toCharArray(); } public boolean isAlive() { if (getThread() != null) { return getThread().isAlive(); } return false; } }