package nl.tudelft.simulation.language.concurrent;
import java.util.ArrayList;
import java.util.List;
/**
* Monitor class. In the Java programming language there is a lock associated with every object. The language does not
* provide a way to perform separate lock and unlock operations; instead, they are implicitly performed by high-level
* constructs that always arrange to pair such operations correctly. This Monitor class, however, provides separate
* monitorenter and monitorexit instructions that implement the lock and unlock operations. The class is final for now,
* as it is not the idea that the class should be extended. It has only static methods.
*
* Copyright (c) 2002-2019 Delft University of Technology, Jaffalaan 5, 2628 BX Delft, the Netherlands. All rights
* reserved. See for project information
* https://simulation.tudelft.nl. The DSOL project is distributed under a three-clause BSD-style license, which can
* be found at
* https://simulation.tudelft.nl/dsol/3.0/license.html.
*
* @author Peter H.M. Jacobs
* @author Alexander Verbraeck
*/
public final class Monitor
{
/** the locks held. */
private static List locks = new ArrayList();
/**
* constructs a new Monitor.
*/
private Monitor()
{
// unreachable code
}
/**
* locks an object for the current thread.
* @param object Object; the object to lock
*/
public static void lock(final Object object)
{
Monitor.lock(object, Thread.currentThread());
}
/**
* locks an object for the given requestor.
* @param object Object; the object to lock.
* @param requestor Thread; the requesting thread.
*/
public static void lock(final Object object, final Thread requestor)
{
synchronized (Monitor.locks)
{
if (Monitor.get(object) == null)
{
Monitor.locks.add(new Entry(object, new MonitorThread(requestor, object)));
}
else
{
MonitorThread thread = Monitor.get(object);
if (thread.getOwner().equals(requestor))
{
thread.increaseCounter();
}
else
{
synchronized (object)
{
// We wait until we gained access to the monitor
Monitor.locks.add(new Entry(object, new MonitorThread(requestor, object)));
}
}
}
}
}
/**
* unlocks an object locked by the current Thread.
* @param object Object; the object to unlock
*/
public static void unlock(final Object object)
{
Monitor.unlock(object, Thread.currentThread());
}
/**
* unlocks an object locked by owner.
* @param object Object; the object to unlock.
* @param owner Thread; the owning thread.
*/
public static void unlock(final Object object, final Thread owner)
{
synchronized (Monitor.locks)
{
MonitorThread thread = Monitor.get(object);
if (thread == null)
{
throw new IllegalMonitorStateException("object(" + object + ") is not locked");
}
if (!thread.getOwner().equals(owner))
{
throw new IllegalMonitorStateException(
owner + " cannot" + " unlock object owned by " + thread.getOwner());
}
thread.decreaseCounter();
if (thread.getCounter() == 0)
{
thread.interrupt();
Monitor.locks.remove(object);
}
}
}
/**
* returns the MonitorThread for a specific key.
* @param key Object; the key to resolve
* @return the MonitorThread
*/
private static MonitorThread get(final Object key)
{
for (Entry next : Monitor.locks)
{
if (next.getKey().equals(key))
{
return next.getThread();
}
}
return null;
}
/**
* The Entry specifies entries in the set.
*/
private static final class Entry
{
/** the key to use. */
private Object key = null;
/** the monitorThread. */
private MonitorThread thread = null;
/**
* constructs a new Entry.
* @param key Object; the key that locked the thread
* @param thread MonitorThread; the thread to be locked
*/
public Entry(final Object key, final MonitorThread thread)
{
super();
this.key = key;
this.thread = thread;
}
/**
* @return the key that is locked by a thread
*/
public Object getKey()
{
return this.key;
}
/**
* @return the thread that locked the key
*/
public MonitorThread getThread()
{
return this.thread;
}
}
/**
* A MonitorThread is used to lock an object.
*/
private static class MonitorThread extends Thread
{
/** the monitor to use. */
private Object object = null;
/** the owning thread. */
private Thread owner = null;
/** the counter. */
private int counter = 0;
/**
* constructs a new MonitorThread.
* @param owner Thread; the owning thread
* @param object Object; the object
*/
public MonitorThread(final Thread owner, final Object object)
{
super("MonitorThread on " + object.getClass());
this.setDaemon(true);
this.owner = owner;
synchronized (object)
{
this.object = object;
this.counter++;
this.start();
}
synchronized (owner)
{
try
{
this.owner.wait();
}
catch (InterruptedException exception)
{
exception = null;
/*
* This interrupted exception is thrown because this monitor thread has started and interrupted its
* constructor. We now know object is locked and may therefore return.
*/
}
}
}
/**
* @return Returns the counter.
*/
public synchronized int getCounter()
{
return this.counter;
}
/**
* decreases the counter with one.
*/
public synchronized void decreaseCounter()
{
this.counter = Math.max(0, this.counter - 1);
}
/**
* increases the counter of this thread with one.
*/
public synchronized void increaseCounter()
{
this.counter++;
}
/**
* @return Returns the owning thread.
*/
public Thread getOwner()
{
return this.owner;
}
/** {@inheritDoc} */
@Override
public void run()
{
try
{
// We lock the object
synchronized (this.object)
{
// Since we have locked the object, we can now return
// the constructor
this.owner.interrupt();
// We join
this.join();
}
}
catch (Exception exception)
{
// This is OK.. We use this construction in the
// MonitorTest.unlock to release a lock
exception = null;
}
}
}
}