// Time-stamp: <02 Apr 2008 16:09 queinnec@enseeiht.fr> package Synchro; import java.util.Random; import java.util.Set; import java.util.HashSet; import java.util.Iterator; /** Simulateur temporel, avec possibilité de suspendre l'écoulement du temps, * de varier la vitesse du temps, et d'interrompre un sommeil. * * @author Philippe Quéinnec */ public class Simulateur implements Runnable { // Utilisée pour bloquer un processus private class Proc { public int id; public int duree_sommeil; public boolean do_wakeup; public Proc (int _id) { id = _id; duree_sommeil = -1; do_wakeup = false; } } private Set procs; private int lastId = 0; private ThreadLocal procself; static private Random random = new Random(); // one system tick = 5ms // one simulated tick = (1000/timespeed) system tick // sleep(n) = Dormir n simulated ticks. private int timespeed= 1; private int system_ticks = 1; // Le temps s'écoule-t-il ou pas ? private boolean running = true; // private en fait public void run () { while (true) { try { Thread.sleep (1); } catch (InterruptedException e) {/*ignore*/} synchronized (this) { if (running) { if (system_ticks == 0) { //System.err.println ("."); /* Décrémenter et réveiller les processus concernés, */ Iterator it = procs.iterator(); while (it.hasNext()) { Proc p = it.next(); synchronized (p) { if (p.duree_sommeil == 0) { //System.out.println ("wakeup1 "+p.id); it.remove(); p.do_wakeup = true; p.notify(); } //System.out.println (i+"->"+procs[i].duree_sommeil); p.duree_sommeil--; } } system_ticks = 1000 / timespeed; } else { system_ticks--; } } else { // not running try { this.wait(); } catch (InterruptedException e) { // nop } } } // sync } // while } // run /** Initialise le simulateur de temps. */ public Simulateur () { final Simulateur moi = this; this.procs = new HashSet(); this.procself = new ThreadLocal() { protected Proc initialValue() { synchronized (moi) { return new Proc (moi.lastId++); } }}; (new Thread (this)).start(); } /** Deprecated. Pour Compatibilité. */ public Simulateur (int nbprocs) { this(); } /** Renvoie l'identité sous lequel est connu le thread appelant. */ public int getId () { Proc p = procself.get(); return p.id; } /** Suspend l'exécution du processus appelant pour la durée spécifiée. */ public void sleep (int duree) { Proc p = procself.get(); sleepWithId (p.id, duree); } /** Suspend l'exécution du processus appelant, qui s'identifie par * noproc, pour la durée spécifiée. */ public void sleepWithId (int noproc, int duree) { Proc p; synchronized (this) { if (noproc > lastId) { lastId = noproc + 1; // juste au cas où... } p = procself.get(); synchronized (p) { procs.add (p); p.id = noproc; p.duree_sommeil = duree; p.do_wakeup = false; } } synchronized (p) { while (! p.do_wakeup) { try { p.wait(); } catch (InterruptedException e) {/*ignore*/} } } } /** Suspend l'exécution du processus appelant pour une durée aléatoire * comprise entre bi et bs. */ public void sleep (int bi, int bs) { if (bi <= bs) sleep (random.nextInt(bs - bi + 1) + bi); } /** Suspend l'exécution du processus appelant, qui s'identifie par * no, pour une durée aléatoire comprise entre bi et bs. */ public void sleepWithId (int no, int bi, int bs) { if (bi <= bs) sleepWithId (no, random.nextInt(bs - bi + 1) + bi); } /** Compatibilité. */ public void sleep (int no, int bi, int bs) { sleepWithId (no, bi, bs); } /** Interrompt le sommeil du processus noproc. Sans effet si * le processus ne dort pas. */ public void wakeup (int noproc) { synchronized (this) { Iterator it = procs.iterator(); while (it.hasNext()) { Proc p = it.next(); synchronized (p) { if (p.id == noproc) { //System.out.println ("wakeup2 "+p.id); it.remove(); p.do_wakeup = true; p.notify(); } } } } } /** Renvoie la situation courante d'écoulement du temps. */ public boolean getRunning () { synchronized (this) { return running; } } /** Décide de l'écoulement du temps. */ public void setRunning (boolean _running) { synchronized (this) { running = _running; if (running) this.notify(); } } /** Inverse la situation courante d'écoulement du temps. */ public void swapRunning () { synchronized (this) { running = !running; if (running) this.notify(); } } /** Positionne la vitesse d'écoulement du temps. */ public void setTimespeed (int _timespeed) { synchronized (this) { timespeed = _timespeed; system_ticks = 1000 / timespeed; //System.out.println("Timespeed: "+timespeed); } } /** Obtention de la vitesse d'écoulement du temps. */ public int getTimespeed () { synchronized (this) { return timespeed; } } private boolean previous_running; /** Suspend l'écoulement du temps en sauvegardant la situation courante. * Plusieurs appels successifs à suspendTime sans alternance de resumeTime produisent un comportement non spécifié. */ public void suspendTime () { synchronized (this) { previous_running = running; running = false; } } /** Restaure la situation de l'écoulement du temps avant le précédent * suspendTime. */ public void resumeTime () { synchronized (this) { running = previous_running; if (running) this.notify(); } } }