/*
* Minha.pt: middleware testing platform.
* Copyright (c) 2011-2014, Universidade do Minho.
*
* This program 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 2
* of the License, or (at your option) any later version.
*
* This program 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 this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
package pt.minha.kernel.simulation;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.atomic.AtomicLong;
/**
* Event-driven simulation scheduler. This is the main simulation entry
* point, that includes the simulation main loop.
*/
public class Scheduler implements Runnable {
AtomicLong in = new AtomicLong(), out = new AtomicLong();
AtomicLong lease = new AtomicLong();
private volatile long simulationTime;
private int next = 0;
private long fuzzyness;
private int procs, numlines;
public Scheduler(int procs, int numlines, long fuzzyness) {
this.procs = procs;
this.numlines = numlines;
this.fuzzyness = fuzzyness;
}
private List<Timeline> timelines = new ArrayList<Timeline>();
/**
* Create a new simulation timeline.
* @return the new timeline
*/
public Timeline createTimeline() {
if (numlines<1 || timelines.size()<numlines) {
next++;
Timeline t = new Timeline(this);
timelines.add(t);
return t;
} else
return timelines.get(next++%timelines.size());
}
/**
* Current simulation time. This should be used only when
* the simulation is stopped.
*
* @return current simulation time
*/
public long getTime() {
return lease.get();
}
/**
* Quit the simulation. There might be a short delay before the
* simulation is actually stopped, so some events might be run
* after this method is invoked. This can safely be invoked from
* any timeline or external thread.
*/
public void stop() {
this.simulationTime = 1;
}
private void execute() {
int base = 0;
while(true) {
long min = Long.MAX_VALUE;
long e = out.get();
int i;
for(i=0; i<timelines.size(); i++) {
Timeline t = timelines.get((i+base)%timelines.size());
long l = t.baseline();
if (l<min)
min = l;
if (l<lease.get() && t.run())
break;
}
base = i;
if (min >= simulationTime)
break;
if (i==timelines.size() && in.get() == e) {
long prev = lease.get();
min += fuzzyness;
while(prev < min && !lease.compareAndSet(prev, min))
prev = lease.get();
}
}
}
/**
* Run the simulation until the upper time limit is reached.
* The simulation will also stop when no events remain
* or {@link #stop()} is called.
*
* @param limit maximum simulation time, 0 to run forever
*/
public boolean run(long limit) {
if (limit == 0)
limit = Long.MAX_VALUE-fuzzyness;
simulationTime = limit;
int effprocs = procs;
if (procs>timelines.size()/2) {
effprocs = timelines.size()/2;
if (effprocs == 0) effprocs = 1;
}
Thread[] threads = new Thread[procs];
for(int i = 0; i<threads.length; i++)
threads[i] = new Thread() {
public void run() {
execute();
}
};
for(int i = 0; i<threads.length; i++)
threads[i].start();
for(int i = 0; i<threads.length; i++)
try {
threads[i].join();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
boolean quiescent = true;
for(Timeline t: timelines)
quiescent &= t.isQuiescent();
return !quiescent;
}
/**
* Run the simulation. The simulation stops when no events remain
* or {@link #stop()} is called.
*/
public void run() {
run(0l);
}
}