package abstrasy;
/**
* 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
*/
import abstrasy.interpreter.InterpreterException;
import abstrasy.interpreter.StdErrors;
class AtomicLock {
/*
* Gestion du réentrant (dans la section critique - donc en ayant déjà le verrou)
*/
private int reentrant = 0;
/*
* Attention!!, volatile nécessaire...
*/
volatile private Interpreter lock = null;
private synchronized boolean tryReentrant_cas_(Interpreter expected) {
if(lock==expected){
reentrant++;
return true;
}
else
return false;
}
synchronized void extractReentrantOf(AtomicLock other) {
synchronized (other) {
reentrant += other.reentrant;
other.reentrant = 0;
}
}
synchronized boolean isExclusive() {
return lock == Interpreter.SHARED_INTERPRETER ? false: true;
}
private synchronized Interpreter getAndSet(Interpreter lock) {
Interpreter old = this.lock;
this.lock = lock;
return old;
}
Interpreter get() {
return lock;
}
private synchronized boolean tryLock_cas_(Interpreter newlock,Node node) {
if (lock == null) {
lock = newlock;
if(reentrant++ == 0)newlock.threadlock_A_add(node);
return false;
}
else {
return true;
}
}
/**
* Tentative d'obtenir un verrou dans une boucle d'attente passive.
* Cette méthode est basée sur le principe du compare-and-set (voir
* méthode private tryLock_cas_(Interpreter newlock,Node node) ci-desus).
*
* Le verrou ne peut être obtenu que s'il est libre.
*
* Si ce n'est pas le cas, la méthode retourne true (ce qui est plus facile
* pour une implémentation dans une boucle while(tryLock){wait}...
*
* @param newlock
* @param node
* @return
*/
boolean tryLock(Interpreter newlock, Node node){
return tryLock_cas_(newlock,node);
}
/**
* Acquisition d'un verroux via une attente active.
*
* Est utilisé par (sync v {...}) et (share v {...}):
*
* - sync demande un verrou exclusif:
* ---------------------------------
* Cela signifie qu'une exception doit être déclenchée si un autre processus essaye d'accéder
* à la données sans demander de verrou au préalable.
*
* - share demande un verrou non exclusif:
* ------------------------------------
* Ce la signifie qu'aucune exception ne sera déclenchée si on accède à la donnée (pour la consulter)
* sans avoir demandé de verrou au préalable.
*
*
* S'arrête en cas d'exception ou de threadRaising.
*
* @param interpreter qui demande le verrou
* @param node à verrouiller
* @throws Exception
*/
void acquirelock(Interpreter interpreter,Node node) throws Exception {
//
// si on a déjà le verrou, on incrémente simplement le réentrant
//
if(tryReentrant_cas_(interpreter))
return;
//
// on a pas le verrou...
//
// on va donc le chercher activement...
//
boolean unraising = true;
Exception exc = null;
interpreter.threadlock_R_add(node);
try {
while (tryLock_cas_(interpreter,node)) {
interpreter.thowsDeadlock(lock);
unraising = !interpreter.isThreadRaising();
}
}
catch (InterpreterException e) {
exc = e;
}
interpreter.threadlock_R_remove(node);
if (exc != null)
throw exc;
return;
}
/**
* Déverrouillage du node sans attente passive.
*
* Est utilisé pas atomic et lock. Toutefois, c'est cette méthoque que
* l'opérateur atomic appelle directement à la fin de la section (atomic n {...})
*
*/
synchronized void unlock(Interpreter interpreter,Node node) throws InterpreterException {
// normalement, je suis tout seul à travailler sur ce node...
// si le compteur réentrant est 0, alors...
--reentrant;
if (reentrant == 0){
Interpreter r=getAndSet(null);
interpreter.threadlock_A_remove(node);
if (r != interpreter){ // on libère le verrou...
//System.out.println(r);
//System.out.println(interpreter);
//System.out.println(Interpreter.SHARED_INTERPRETER);
throw new InterpreterException(StdErrors.extend(StdErrors.Internal_error, "Node already unlocked"));
}
}
else if(reentrant<0){
throw new InterpreterException(StdErrors.extend(StdErrors.Internal_error, "Negative reentrant"));
}
}
/**
* Le node est-il verrouillé par mon acteur (moi-même).
*
* @param interpreter
* @return
*/
boolean isLockedBy(Interpreter interpreter) {
return lock == interpreter; // attention!!! est-ce verrouillé par l'interpréter fourni en argument !!!
}
}