package abstrasy;
import abstrasy.interpreter.InterpreterException;
import abstrasy.interpreter.StdErrors;
import java.util.Vector;
/**
* Abstrasy Interpreter
*
* Copyright : Copyright (c) 2006-2012, Luc Bruninx.
*
* Concédée sous licence EUPL, version 1.1 uniquement (la «Licence»).
*
* Vous ne pouvez utiliser la présente oeuvre que conformément à la Licence.
* Vous pouvez obtenir une copie de la Licence à l’adresse suivante:
*
* http://www.osor.eu/eupl
*
* Sauf obligation légale ou contractuelle écrite, le logiciel distribué sous
* la Licence est distribué "en l’état", SANS GARANTIES OU CONDITIONS QUELLES
* QU’ELLES SOIENT, expresses ou implicites.
*
* Consultez la Licence pour les autorisations et les restrictions
* linguistiques spécifiques relevant de la Licence.
*
*
* @author Luc Bruninx
* @version 1.0
*/
public class InterpreterSemaphore {
public InterpreterSemaphore() {
}
/*
* Délai d'attente passive (en ms).
*
* Ce délai permet de vérifier périodiquement l'état de l'interpréteur et les éventuelles
* interaction de l'utilisateur.
*
*/
private final static int PASSIVE_WAITING_TIME = 50;
/*
* Drapeau mutex pour verrouillage exclusif global.
* Un seul mutex peut fonctionner au même moement.
*/
private static boolean mutex_lock = false;
private static boolean signalBreak = false;
private static boolean signalEnd = false;
private Vector<Interpreter> threads = new Vector<Interpreter>();
/**
* Peut-on continuer l'exécution ou y a t-il un signal d'arrêt brutal ?
*
* @return
*/
final private boolean canContinue() {
return !(signalBreak || signalEnd);
}
/**
* Object de synchronisation mutex global:
* ---------------------------------------
* Plutot que de verrouiller tout le InterpreterSemaphore, permet d'affiner la granularité du mutex.
*/
private static Object mutex_object = new Object();
/**
* Verrouillage mutex...
*
* Utilisé par l'opérateur mutex pour introduire une section critique (mutual exclude).
*
* @throws Exception
*/
final public void lockMutex() throws Exception {
Interpreter myself = (Interpreter) Thread.currentThread();
synchronized (mutex_object) {
while (mutex_lock && threads.size() > 1 && canContinue() && (!myself.isThreadRaising())) { // && thisThread!=byThread) {
mutex_object.wait(PASSIVE_WAITING_TIME); // avec passive_waiting_time, on peut tester l'état de l'interpréteur (sinon, on le bloque).
}
mutex_lock = true;
}
myself.actor_LOCKMUTEX();
}
/**
* Déverrouillage mutex...
*
* Utilisé par l'opérateur mutex pour libérer une section critique.
*/
final public void unlockMutex() {
Interpreter.mySelf().actor_UNLOCKMUTEX();
synchronized (mutex_object) {
mutex_lock = false;
mutex_object.notify();
}
}
/**
* Verrouillage (lock node{...})...
*
* Utilisé par l'opérateur lock pour introduire une section critique.
*
* @throws Exception
*/
final public void lockNode(Node node) throws Exception {
Interpreter myself = (Interpreter) Thread.currentThread();
Exception exc = null;
synchronized (node) {
myself.threadlock_R_add(node);
try {
while (node.tryLock(myself) && threads.size() > 1 && canContinue() && (!myself.isThreadRaising())) { // && thisThread!=byThread) {
myself.thowsDeadlock(node.getlock_by());
node.wait(PASSIVE_WAITING_TIME); // avec passive_waiting_time, on peut tester l'état de l'interpréteur (sinon, on le bloque).
}
}
catch (Exception e) {
// en cas de problème, récupérer l'exception...
exc = e;
}
myself.threadlock_R_remove(node);
}
if (exc != null) {
// si exception, alors répercuter celle-ci...
throw exc;
}
}
/**
* Déverrouillage (lock node{...})...
*
* Utilisé par l'opérateur lock pour libérer une section critique.
*/
final public void unlockNode(Node node) throws Exception {
Interpreter interpreter=Interpreter.mySelf();
synchronized (node) {
node.unlock(interpreter);
node.notify();
}
}
/**
* Réveille l'acteur thread...
*
* Depuis REV6251(31/01/2011): Ne fragmente plus le verrouillage du semaphore.
* resume n'est pas bloquant. Il peut donc être utilisé dans une section critique.
*
* @param thread
* @throws Exception
*/
public void resume(Interpreter thread) throws Exception {
//thread.actor_RESUME(); // appliqué lors du réveil par l'acteur de destination...
thread.actor_SIGNAL();
Object obj = thread.thread_getSuspendLock();
synchronized (obj) {
obj.notify();
}
}
/**
* L'acteur en cours suspend son activité et attend qu'on le réveille
*
* Depuis REV6251(31/01/2011): Ne fragmente plus le verrouillage du semaphore.
* suspend est un opérateur bloquant. Il ne peut pas être utilisé dans une section
* atomique (section critique).
*
* @throws Exception
*/
public void suspendAndWaitResume() throws Exception {
Interpreter myself = (Interpreter) Thread.currentThread();
Object obj = myself.thread_getSuspendLock();
while (!myself.actor_CONSUMESIGNAL() && !myself.isThreadRaising() && canContinue()) {
synchronized (obj) {
myself.actor_SUSPEND();
//myself.yield(); //(optimisation 30/01/2011)
if (!myself.actor_HASSIGNALS()) { // dans le cas où un signal serait arrivé dans l'interval
obj.wait(PASSIVE_WAITING_TIME);
}
}
}
myself.actor_RESUME();
}
/**
* L'acteur en cours suspend son activité et attend qu'on le réveille comme suspendAndWaitResume().
* Toutefois, ici, un délai d'attente maximum est déterminé. Si ce délai est dépassé, retourne true.
*
* On notera aussi que cette méthode est sûr. Si le signal n'est pas capturé avant le dépassement
* de temps, le signal reste dans la file d'attente. Il pourra être récupéré lors d'un prochain
* suspend.
*
* @throws Exception
*/
public boolean suspendAndWaitResume(long millis) throws Exception {
Interpreter myself = (Interpreter) Thread.currentThread();
Object obj = myself.thread_getSuspendLock();
long base = System.currentTimeMillis();
long now = 0;
long delay;
while (((delay = millis - now) > 0) && !myself.actor_CONSUMESIGNAL() && !myself.isThreadRaising() && canContinue()) {
synchronized (obj) {
myself.actor_SUSPEND();
//myself.yield(); //(optimisation 30/01/2011)
if (!myself.actor_HASSIGNALS()) { // dans le cas où un signal serait arrivé dans l'interval
obj.wait(Math.min(delay, PASSIVE_WAITING_TIME));
}
}
now = System.currentTimeMillis() - base;
}
myself.actor_RESUME();
return (delay <= 0); // retourne true en cas de dépassement...
}
/**
* Réception d'un message
*
* Depuis REV6251(31/01/2011): Ne fragmente plus le verrouillage du semaphore.
* receive est un opérateur bloquant. Il ne peut donc pas être utilisé dans une section critique.
*
* @return
* @throws Exception
*/
public Node receive_MSG() throws Exception {
Interpreter myself = (Interpreter) Thread.currentThread();
Node msg = null;
Object obj = myself.thread_getSuspendLock();
while ((msg = myself.actor_GETMSG()) == null && (!myself.isThreadRaising()) && canContinue()) {
synchronized (obj) {
myself.actor_SUSPEND();
// myself.yield(); //(optimisation 30/01/2011)
if (!myself.actor_HASMSG()) { // dans le cas où un message serait arrivé dans l'interval...
obj.wait(PASSIVE_WAITING_TIME);
}
}
}
// si un message valide, alors on se réveille...
myself.actor_RESUME();
return msg;
}
/**
* Réception d'un message avec délai d'expiration de l'attente.
*
* Notez que cette méthode est transactionnelle. Si le délai est écoulé et qu'un message arrive juste à ce moment
* là, celui-ci reste dans la file d'attente et pourra être récupéré ultérieurement par le prochain receive.
*
* @return
* @throws Exception
*/
public Node receive_MSG(long millis) throws Exception {
Interpreter myself = (Interpreter) Thread.currentThread();
Node msg = null;
Object obj = myself.thread_getSuspendLock();
long base = System.currentTimeMillis();
long now = 0;
long delay;
while (((delay = millis - now) > 0) && (msg = myself.actor_GETMSG()) == null && (!myself.isThreadRaising()) && canContinue()) {
/*
* On notera que le message éventuel est extrait uniquement si le délai n'est pas dépassé!!!
*
* Si le délai est dépassé (donc delay<=0), la partie (msg=myself.actor_GETMSG()) n'est pas évaluée (caractéristique d'évaluation
* paresseuse de Java).
*
*/
synchronized (obj) {
myself.actor_SUSPEND();
// myself.yield(); //(optimisation 30/01/2011)
if (!myself.actor_HASMSG()) { // dans le cas où un message serait arrivé dans l'interval...
obj.wait(Math.min(delay, PASSIVE_WAITING_TIME));
}
}
now = System.currentTimeMillis() - base;
}
/*
* S'il n'y a pas de message et que le délai est dépassé, alors signaler un dépassement de temps...
*/
if (msg == null && delay <= 0) {
myself.setTimeOutRaising(true);
/*
* Ceci répercutera une exception...
*
* Attention, cette manière de procéder est différente de celle de suspendAndWaitResume(long millis) parce qu'ici
* nous devons renvoyer le message. Si on renvoie null, normalement, nous devons nous trouver en situation
* de time-out!!! Sinon, il s'agit d'une erreur interne.
*/
}
// si un message valide, alors on se réveille...
myself.actor_RESUME();
return msg;
}
/**
* Envoyer un le message 'msg' à l'acteur 'thread'.
*
* Depuis REV6251(31/01/2011): Ne fragmente plus le verrouillage du semaphore.
* send n'est pas bloquant. Il peut donc être utilisé dans une section critique.
*
* @param thread
* @param msg
* @throws Exception
*/
public void send_MSG(Interpreter thread, Node msg) throws Exception {
thread.actor_PUTMSG(msg);
// thread.actor_RESUME(); // appliqué par l'acteur de destination à son réveille...
Object obj = thread.thread_getSuspendLock();
synchronized (obj) {
obj.notify();
}
}
/**
* Suspendre son activité pendant 'millis' ms.
*
* Depuis REV6251(31/01/2011): Ne fragmente plus le verrouillage du semaphore.
* sleep est un opérateur bloquant. Il ne peut pas être utilisé dans une section critique.
*
* @param millis
* @throws Exception
*/
public void sleep(long millis) throws Exception {
Interpreter myself = (Interpreter) Thread.currentThread();
long base = System.currentTimeMillis();
long now = 0;
long delay;
Object obj = myself.thread_getSuspendLock();
while (((delay = millis - now) > 0) && (!myself.isThreadRaising()) && canContinue()) {
synchronized (obj) {
myself.actor_SUSPEND();
// myself.yield(); //(optimisation 30/01/2011)
obj.wait(Math.min(delay, PASSIVE_WAITING_TIME));
}
now = System.currentTimeMillis() - base;
}
// on repart...
myself.actor_RESUME();
}
/**
* Tuer l'acteur 'thread'...
*
* @param thread
* @throws Exception
*/
public synchronized void kill(Interpreter thread) throws Exception {
if (thread != null) {
/*
* Le processus doit exister...
*/
thread.actor_STOP();
thread.actor_SIGNAL();
Interpreter myself = (Interpreter) Thread.currentThread();
long base = System.currentTimeMillis();
long now = 0;
long millis = 100;
int i = 30;
if (thread == myself) {
throw new InterpreterException(StdErrors.extend(StdErrors.Thread_error, "a thread can not kill himself"));
}
Object obj = myself.thread_getSuspendLock();
while (((millis - now) > 0) && (!myself.isThreadRaising()) && canContinue() && isThreadsExists(thread)) {
synchronized (obj) {
notifyAll();
wait(1);
}
now = System.currentTimeMillis() - base;
}
/*
* lorsque le processus s'est arrêté, il doit normalement s'avoir désenregistré lui-même...
*/
}
}
public synchronized void reset() { // du sémaphore...
endAllCoroutines();
signalBreak = false;
signalEnd = false;
mutex_lock = false;
}
public synchronized void setSignalBreak(boolean signalBreak) {
InterpreterSemaphore.signalBreak = signalBreak;
}
boolean isSignalBreak() {
return signalBreak;
}
public synchronized void setSignalEnd(boolean signalEnd) {
InterpreterSemaphore.signalEnd = signalEnd;
}
boolean isSignalEnd() {
return signalEnd;
}
public void registerThread(Interpreter corout) {
synchronized (threads) {
boolean fnd = false;
for (int i = 0; i < threads.size(); ) {
if (fnd = (corout == threads.elementAt(i++))) {
i = threads.size();
}
}
if (!fnd) {
// pas de doublon...
threads.addElement(corout);
}
}
}
public void unregisterThread(Interpreter corout) {
Interpreter myself = Interpreter.mySelf();
synchronized (threads) {
/*
* Si je suis mutex, je libère celui-ci...
*/
if (myself.actor_ISMUTEX()) {
this.unlockMutex();
}
/*
* je m'enlève de la liste des acteurs moi-même...
*/
threads.remove(corout);
}
// myself.yield(); //(optimisation 30/01/2011)
}
private Interpreter getRegistered() {
synchronized (threads) {
Interpreter myself = Interpreter.mySelf();
Interpreter inter = null;
for (int i = 0; i < threads.size(); i++) {
inter = threads.elementAt(i);
if (inter == myself) {
// ne pas trouver le thread superviseur...
inter = null;
}
else {
i = threads.size();
}
}
return inter;
}
}
public synchronized String getUniqueThreadName() {
String res = null;
while (res == null) {
res = "actor/" + java.util.UUID.randomUUID().toString();
if (isThreadsExists(res)) {
res = null;
}
}
return res;
}
public Node getACTORS() throws Exception {
synchronized (threads) {
Node res = Node.createCList();
Interpreter inter = null;
for (int i = 0; i < threads.size(); i++) {
inter = threads.elementAt(i);
if (inter.isInterThread()) {
// n'inclure que les threads fils... pas le thread superviseur
res.addElement(new Node(inter.getName()));
}
}
return res;
}
}
public synchronized Vector<String> getThreadsNames() {
Vector<String> res = new Vector<String>();
Interpreter inter = null;
for (int i = 0; i < threads.size(); i++) {
inter = threads.elementAt(i);
if (inter.isInterThread()) {
// n'inclure que les threads fils... pas le thread superviseur
res.addElement(inter.getName());
}
}
return res;
}
public synchronized Interpreter getThread(String name) {
Interpreter res = null;
Interpreter inter = null;
for (int i = 0; i < threads.size(); i++) {
inter = threads.elementAt(i);
/**
* ATTENTION TROUVE TOUS LES THREADS... MEME LE THREAD SUPERVISEUR !!!
*/
if (inter.getName().equals(name)) {
res = inter;
i = threads.size();
}
}
return res;
}
public synchronized boolean isThreadsExists(String name) {
Interpreter inter = null;
boolean res = false;
for (int i = 0; i < threads.size(); i++) {
inter = threads.elementAt(i);
if (inter.isInterThread()) {
// n'inclure que les threads fils...
if (res = (inter.getName().equals(name))) {
i = threads.size();
}
}
}
//if(!res){
// System.out.println("Thread not exists " +name);
//}
return res;
}
public synchronized boolean isThreadsExists(Interpreter thread) {
Interpreter inter = null;
boolean res = false;
for (int i = 0; i < threads.size(); i++) {
inter = threads.elementAt(i);
if (inter.isInterThread()) {
// n'inclure que les threads fils...
if (res = (inter == thread)) {
i = threads.size();
}
}
}
return res;
}
public static long interThreadUID = 0;
public boolean hasOtherRegistered() {
// synchronized (threads) {
return threads.size() > 1;
// }
}
public boolean hasRegistered() {
// synchronized (threads) {
return threads.size() > 0;
// }
}
public void endAllCoroutines() {
// interdit à partir d'une coroutine...
Interpreter myself = Interpreter.mySelf();
if (!myself.isInterThread()) {
setSignalEnd(true);
Interpreter corout = null;
while ((corout = getRegistered()) != null) {
Interpreter.Log("Asking thread interruption [" + corout.getName() + " : " + corout.getId() + "] by " + myself.getName() + "<" + myself.getId() +
">...");
corout.actor_STOP();
corout.interrupt();
try {
Thread.sleep(100);
}
catch (Exception ex) {
if (Interpreter.isDebugMode()) {
ex.printStackTrace();
}
}
if (corout.isInterrupted()) {
Interpreter.Log(" -> thread [" + corout.getName() + " : " + corout.getId() + "] Interrupted.");
unregisterThread(corout);
}
if (!corout.isAlive()) {
unregisterThread(corout);
Interpreter.Log(" -> thread [" + corout.getName() + " : " + corout.getId() + "] Died.");
}
}
}
else {
Interpreter.Log("Asking thread interruption from InterThread !!!...");
}
}
}