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; } } } }