1 package nl.tudelft.simulation.language.concurrent;
2
3 import java.util.LinkedHashMap;
4 import java.util.Map;
5
6 /**
7 * Monitor class. In the Java programming language there is a lock associated with every object. The language does not provide a
8 * way to perform separate lock and unlock operations; instead, they are implicitly performed by high-level constructs that
9 * always arrange to pair such operations correctly. This Monitor class, however, provides separate monitorenter and monitorexit
10 * instructions that implement the lock and unlock operations. The class is final for now, as it is not the idea that the class
11 * should be extended. It has only static methods.
12 * <p>
13 * Copyright (c) 2002-2023 Delft University of Technology, Jaffalaan 5, 2628 BX Delft, the Netherlands. All rights reserved. See
14 * for project information <a href="https://simulation.tudelft.nl/" target="_blank"> https://simulation.tudelft.nl</a>. The DSOL
15 * project is distributed under a three-clause BSD-style license, which can be found at
16 * <a href="https://https://simulation.tudelft.nl/dsol/docs/latest/license.html" target="_blank">
17 * https://https://simulation.tudelft.nl/dsol/docs/latest/license.html</a>.
18 * </p>
19 * @author <a href="mailto:phmjacobs@hotmail.com">Peter H.M. Jacobs</a>
20 * @author <a href="http://tudelft.nl/averbraeck">Alexander Verbraeck</a>
21 */
22 public final class Monitor
23 {
24 /** the locks held. */
25 private static Map<Object, MonitorThread> locks = new LinkedHashMap<>();
26
27 /**
28 * constructs a new Monitor.
29 */
30 private Monitor()
31 {
32 // unreachable code
33 }
34
35 /**
36 * locks an object for the current thread.
37 * @param object Object; the object to lock
38 */
39 public static void lock(final Object object)
40 {
41 Monitor.lock(object, Thread.currentThread());
42 }
43
44 /**
45 * locks an object for the given requestor.
46 * @param object Object; the object to lock.
47 * @param requestor Thread; the requesting thread.
48 */
49 public static void lock(final Object object, final Thread requestor)
50 {
51 synchronized (Monitor.locks)
52 {
53 if (Monitor.get(object) == null)
54 {
55 Monitor.locks.put(object, new MonitorThread(requestor, object));
56 }
57 else
58 {
59 MonitorThread thread = Monitor.get(object);
60 if (thread.getOwner().equals(requestor))
61 {
62 thread.increaseCounter();
63 }
64 else
65 {
66 synchronized (object)
67 {
68 // We wait until we gained access to the monitor
69 Monitor.locks.put(object, new MonitorThread(requestor, object));
70 }
71 }
72 }
73 }
74 }
75
76 /**
77 * unlocks an object locked by the current Thread.
78 * @param object Object; the object to unlock
79 */
80 public static void unlock(final Object object)
81 {
82 Monitor.unlock(object, Thread.currentThread());
83 }
84
85 /**
86 * unlocks an object locked by owner.
87 * @param object Object; the object to unlock.
88 * @param owner Thread; the owning thread.
89 */
90 public static void unlock(final Object object, final Thread owner)
91 {
92 synchronized (Monitor.locks)
93 {
94 MonitorThread thread = Monitor.get(object);
95 if (thread == null)
96 {
97 throw new IllegalMonitorStateException("object(" + object + ") is not locked");
98 }
99 if (!thread.getOwner().equals(owner))
100 {
101 throw new IllegalMonitorStateException(owner + " cannot" + " unlock object owned by " + thread.getOwner());
102 }
103 thread.decreaseCounter();
104 if (thread.getCounter() == 0)
105 {
106 thread.interrupt();
107 Monitor.locks.remove(object);
108 }
109 }
110 }
111
112 /**
113 * returns the MonitorThread for a specific key.
114 * @param key Object; the key to resolve
115 * @return the MonitorThread
116 */
117 private static MonitorThread get(final Object key)
118 {
119 return locks.get(key);
120 }
121
122 /**
123 * A MonitorThread is used to lock an object.
124 */
125 private static class MonitorThread extends Thread
126 {
127 /** the monitor to use. */
128 private Object object = null;
129
130 /** the owning thread. */
131 private Thread owner = null;
132
133 /** the counter. */
134 private int counter = 0;
135
136 /**
137 * constructs a new MonitorThread.
138 * @param owner Thread; the owning thread
139 * @param object Object; the object
140 */
141 MonitorThread(final Thread owner, final Object object)
142 {
143 super("MonitorThread on " + object.getClass());
144 this.setDaemon(true);
145 this.owner = owner;
146 synchronized (object)
147 {
148 this.object = object;
149 increaseCounter();
150 this.start();
151 }
152 synchronized (owner)
153 {
154 try
155 {
156 this.owner.wait();
157 }
158 catch (InterruptedException exception)
159 {
160 exception = null;
161 /*
162 * This interrupted exception is thrown because this monitor thread has started and interrupted its
163 * constructor. We now know object is locked and may therefore return.
164 */
165 }
166 }
167 }
168
169 /**
170 * @return Returns the counter.
171 */
172 public synchronized int getCounter()
173 {
174 return this.counter;
175 }
176
177 /**
178 * decreases the counter with one.
179 */
180 public synchronized void decreaseCounter()
181 {
182 this.counter = Math.max(0, this.counter - 1);
183 }
184
185 /**
186 * increases the counter of this thread with one.
187 */
188 public synchronized void increaseCounter()
189 {
190 this.counter++;
191 }
192
193 /**
194 * @return Returns the owning thread.
195 */
196 public Thread getOwner()
197 {
198 return this.owner;
199 }
200
201 /** {@inheritDoc} */
202 @Override
203 public void run()
204 {
205 try
206 {
207 // We lock the object
208 synchronized (this.object)
209 {
210 // Since we have locked the object, we can now return
211 // the constructor
212 this.owner.interrupt();
213
214 // We join
215 this.join();
216 }
217 }
218 catch (Exception exception)
219 {
220 // This is OK.. We use this construction in the
221 // MonitorTest.unlock to release a lock
222 exception = null;
223 }
224 }
225 }
226 }