/*
* 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.models.fake.java.util.concurrent.locks;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import pt.minha.kernel.simulation.Event;
import pt.minha.models.local.lang.SimulationThread;
public class ReentrantLock implements Lock {
private List<Event> waitingOnLock = new ArrayList<Event>();
private int busy;
private SimulationThread holder;
public ReentrantLock() {
}
public ReentrantLock(boolean fair) {
// currently ignored
}
@Override
public void lock() {
if (busy!=0) {
SimulationThread current = SimulationThread.currentSimulationThread();
if (holder == current) {
busy++;
return;
}
try {
SimulationThread.stopTime(0);
while(holder!=null) {
waitingOnLock.add(current.getWakeup());
current.pause(false, false);
}
holder=current;
busy++;
} finally {
SimulationThread.startTime(0);
}
} else {
holder=SimulationThread.currentSimulationThread();
busy++;
}
}
@Override
public void unlock() {
busy--;
if (busy>0)
return;
holder=null;
if (waitingOnLock.isEmpty())
return;
try {
SimulationThread.stopTime(0);
if (!waitingOnLock.isEmpty())
waitingOnLock.remove(0).schedule(0);
} finally {
SimulationThread.startTime(0);
}
}
@Override
public void lockInterruptibly() throws InterruptedException {
if (busy!=0) {
SimulationThread current = SimulationThread.currentSimulationThread();
if (holder == current) {
busy++;
return;
}
try {
SimulationThread.stopTime(0);
boolean interrupted = current.getInterruptedStatus(true);
while(!interrupted && holder!=null) {
waitingOnLock.add(current.getWakeup());
interrupted = current.pause(true,true);
}
if (interrupted) {
waitingOnLock.remove(current.getWakeup());
throw new InterruptedException();
}
holder=current;
busy++;
} finally {
SimulationThread.startTime(0);
}
} else {
holder=SimulationThread.currentSimulationThread();
busy++;
}
}
@Override
public boolean tryLock() {
if (busy!=0 && !isHeldByCurrentThread())
return false;
lock();
return true;
}
@Override
public boolean tryLock(long time, TimeUnit unit) throws InterruptedException {
if (busy!=0) {
SimulationThread current = SimulationThread.currentSimulationThread();
if (holder == current) {
busy++;
return true;
}
try {
SimulationThread.stopTime(0);
long nanosTimeout = unit.toNanos(time);
long before = current.getTimeline().getTime();
boolean interrupted = current.getInterruptedStatus(true);
while(!interrupted && holder!=null && nanosTimeout>0) {
waitingOnLock.add(current.getWakeup());
current.getWakeup().schedule(nanosTimeout);
interrupted = current.pause(true,true);
long after = current.getTimeline().getTime();
nanosTimeout = nanosTimeout - (after - before);
if (nanosTimeout < 0) nanosTimeout = 0;
before = after;
}
if (interrupted) {
waitingOnLock.remove(current.getWakeup());
throw new InterruptedException();
}
if (holder!=null) {
waitingOnLock.remove(current.getWakeup());
return false;
}
holder=current;
busy++;
return true;
} finally {
SimulationThread.startTime(0);
}
} else {
holder=SimulationThread.currentSimulationThread();
busy++;
return true;
}
}
public boolean isHeldByCurrentThread() {
return holder == SimulationThread.currentSimulationThread();
}
public boolean isLocked() {
return holder != null;
}
@Override
public Condition newCondition() {
return new ConditionImpl();
}
private class ConditionImpl implements Condition {
private List<Event> waitingOnCond = new ArrayList<Event>();
@Override
public void await() throws InterruptedException {
awaitNanos(0);
}
@Override
public void awaitUninterruptibly() {
awaitNanosUnint(0);
}
private long awaitNanosUnint(long nanosTimeout) {
if (!isHeldByCurrentThread())
throw new IllegalMonitorStateException();
long before=0, after=0;
try {
SimulationThread.stopTime(0);
SimulationThread current = SimulationThread.currentSimulationThread();
int storedBusy = busy;
before = current.getTimeline().getTime();
// unlock
holder=null;
busy=0;
if (!waitingOnLock.isEmpty())
waitingOnLock.remove(0).schedule(0);
// sleep
waitingOnCond.add(current.getWakeup());
if (nanosTimeout>0) current.getWakeup().schedule(nanosTimeout);
current.pause(false, false);
// cleanup
waitingOnCond.remove(current.getWakeup());
// lock
while(holder!=null) {
waitingOnLock.add(current.getWakeup());
current.pause(false, false);
}
// wakeup
holder=current;
busy=storedBusy;
after = current.getTimeline().getTime();
} finally {
SimulationThread.startTime(0);
}
return nanosTimeout-(after-before);
}
@Override
public void signal() {
if (!isHeldByCurrentThread())
throw new IllegalMonitorStateException();
try {
SimulationThread.stopTime(0);
if (!waitingOnCond.isEmpty())
waitingOnCond.remove(0).schedule(0);
} finally {
SimulationThread.startTime(0);
}
}
@Override
public void signalAll() {
if (!isHeldByCurrentThread())
throw new IllegalMonitorStateException();
try {
SimulationThread.stopTime(0);
for (Event e: waitingOnCond)
e.schedule(0);
waitingOnCond.clear();
} finally {
SimulationThread.startTime(0);
}
}
@Override
public long awaitNanos(long nanosTimeout) throws InterruptedException {
if (!isHeldByCurrentThread())
throw new IllegalMonitorStateException();
long before=0, after=0;
try {
SimulationThread.stopTime(0);
SimulationThread current = SimulationThread.currentSimulationThread();
int storedBusy = busy;
before = current.getTimeline().getTime();
if (current.getInterruptedStatus(true))
throw new InterruptedException();
// unlock
holder=null;
busy=0;
if (!waitingOnLock.isEmpty())
waitingOnLock.remove(0).schedule(0);
// sleep
waitingOnCond.add(current.getWakeup());
if (nanosTimeout>0) current.getWakeup().schedule(nanosTimeout);
current.pause(true,false);
// cleanup
waitingOnCond.remove(current.getWakeup());
// lock
while(holder!=null) {
waitingOnLock.add(current.getWakeup());
current.pause(false, false);
}
// wakeup
holder=current;
busy=storedBusy;
after = current.getTimeline().getTime();
if (current.getInterruptedStatus(true)) {
/* Make sure signal is not lost, if done concurrently
* with the interrupt. This may result in a spurious
* wakeup, but that is ok too.
*/
if (!waitingOnCond.isEmpty())
waitingOnCond.remove(0).schedule(0);
throw new InterruptedException();
}
} finally {
SimulationThread.startTime(0);
}
return nanosTimeout-(after-before);
}
@Override
public boolean await(long time, TimeUnit unit) throws InterruptedException {
return awaitNanos(unit.toNanos(time)) > 0;
}
@Override
public boolean awaitUntil(Date deadline) throws InterruptedException {
throw new RuntimeException("not implemented");
}
}
}