blob: 7fe2b60c1e4e11f76e441ae1d6545875a65d9619 [file] [log] [blame]
Alex Lightce568642017-09-05 16:54:25 -07001/*
2 * Copyright (C) 2017 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package art;
18
Alex Light77fee872017-09-05 14:51:49 -070019import java.lang.reflect.Method;
20import java.util.concurrent.atomic.*;
Alex Lightce568642017-09-05 16:54:25 -070021import java.util.function.Function;
22import java.util.stream.Stream;
Alex Light77fee872017-09-05 14:51:49 -070023import java.util.Arrays;
24import java.util.Objects;
Alex Lightce568642017-09-05 16:54:25 -070025
26public class Monitors {
Alex Light77fee872017-09-05 14:51:49 -070027 public native static void setupMonitorEvents(
28 Class<?> method_klass,
29 Method monitor_contended_enter_event,
30 Method monitor_contended_entered_event,
31 Method monitor_wait_event,
32 Method monitor_waited_event,
33 Class<?> lock_klass,
34 Thread thr);
35 public native static void stopMonitorEvents();
36
Alex Lightce568642017-09-05 16:54:25 -070037 public static class NamedLock {
38 public final String name;
Alex Lightbeae5ac2017-09-21 13:21:51 -070039 private volatile int calledNotify;
Alex Lightce568642017-09-05 16:54:25 -070040 public NamedLock(String name) {
41 this.name = name;
Alex Lightbeae5ac2017-09-21 13:21:51 -070042 calledNotify = 0;
Alex Lightce568642017-09-05 16:54:25 -070043 }
Alex Lightbeae5ac2017-09-21 13:21:51 -070044
Alex Lightce568642017-09-05 16:54:25 -070045 public String toString() {
46 return String.format("NamedLock[%s]", name);
47 }
Alex Lightbeae5ac2017-09-21 13:21:51 -070048
49 public final void DoWait() throws Exception {
50 final int v = calledNotify;
51 while (v == calledNotify) {
52 wait();
53 }
54 }
55
56 public final void DoWait(long t) throws Exception {
57 final int v = calledNotify;
58 final long target = System.currentTimeMillis() + (t / 2);
59 while (v == calledNotify && (t < 0 || System.currentTimeMillis() < target)) {
60 wait(t);
61 }
62 }
63
64 public final void DoNotifyAll() throws Exception {
65 calledNotify++;
66 notifyAll();
67 }
68
69 public final void DoNotify() throws Exception {
70 calledNotify++;
71 notify();
72 }
Alex Lightce568642017-09-05 16:54:25 -070073 }
74
75 public static final class MonitorUsage {
76 public final Object monitor;
77 public final Thread owner;
78 public final int entryCount;
79 public final Thread[] waiters;
80 public final Thread[] notifyWaiters;
81
82 public MonitorUsage(
83 Object monitor,
84 Thread owner,
85 int entryCount,
86 Thread[] waiters,
87 Thread[] notifyWaiters) {
88 this.monitor = monitor;
89 this.entryCount = entryCount;
90 this.owner = owner;
91 this.waiters = waiters;
92 this.notifyWaiters = notifyWaiters;
93 }
94
95 private static String toNameList(Thread[] ts) {
96 return Arrays.toString(Arrays.stream(ts).map((Thread t) -> t.getName()).toArray());
97 }
98
99 public String toString() {
100 return String.format(
101 "MonitorUsage{ monitor: %s, owner: %s, entryCount: %d, waiters: %s, notify_waiters: %s }",
102 monitor,
103 (owner != null) ? owner.getName() : "<NULL>",
104 entryCount,
105 toNameList(waiters),
106 toNameList(notifyWaiters));
107 }
108 }
109
110 public static native MonitorUsage getObjectMonitorUsage(Object monitor);
Alex Light41006c62017-09-14 09:51:14 -0700111 public static native Object getCurrentContendedMonitor(Thread thr);
Alex Light77fee872017-09-05 14:51:49 -0700112
113 public static class TestException extends Error {
114 public TestException() { super(); }
115 public TestException(String s) { super(s); }
116 public TestException(String s, Throwable c) { super(s, c); }
117 }
118
119 public static class LockController {
120 private static enum Action { HOLD, RELEASE, NOTIFY, NOTIFY_ALL, WAIT, TIMED_WAIT }
121
Alex Lightbeae5ac2017-09-21 13:21:51 -0700122 public final NamedLock lock;
Alex Light77fee872017-09-05 14:51:49 -0700123 public final long timeout;
124 private final AtomicStampedReference<Action> action;
125 private volatile Thread runner = null;
126 private volatile boolean started = false;
127 private volatile boolean held = false;
128 private static final AtomicInteger cnt = new AtomicInteger(0);
129 private volatile Throwable exe;
130
Alex Lightbeae5ac2017-09-21 13:21:51 -0700131 public LockController(NamedLock lock) {
Alex Light77fee872017-09-05 14:51:49 -0700132 this(lock, 10 * 1000);
133 }
Alex Lightbeae5ac2017-09-21 13:21:51 -0700134 public LockController(NamedLock lock, long timeout) {
Alex Light77fee872017-09-05 14:51:49 -0700135 this.lock = lock;
136 this.timeout = timeout;
137 this.action = new AtomicStampedReference(Action.HOLD, 0);
138 this.exe = null;
139 }
140
141 public boolean IsWorkerThread(Thread thd) {
142 return Objects.equals(runner, thd);
143 }
144
145 public boolean IsLocked() {
146 checkException();
147 return held;
148 }
149
150 public void checkException() {
151 if (exe != null) {
152 throw new TestException("Exception thrown by other thread!", exe);
153 }
154 }
155
156 private void setAction(Action a) {
157 int stamp = action.getStamp();
158 // Wait for it to be HOLD before updating.
159 while (!action.compareAndSet(Action.HOLD, a, stamp, stamp + 1)) {
160 stamp = action.getStamp();
161 }
162 }
163
Alex Light41006c62017-09-14 09:51:14 -0700164 public synchronized void suspendWorker() throws Exception {
165 checkException();
166 if (runner == null) {
167 throw new TestException("We don't have any runner holding " + lock);
168 }
169 Suspension.suspend(runner);
170 }
171
172 public Object getWorkerContendedMonitor() throws Exception {
173 checkException();
174 if (runner == null) {
175 return null;
176 }
177 return getCurrentContendedMonitor(runner);
178 }
179
Alex Light77fee872017-09-05 14:51:49 -0700180 public synchronized void DoLock() {
181 if (IsLocked()) {
182 throw new Error("lock is already acquired or being acquired.");
183 }
184 if (runner != null) {
185 throw new Error("Already have thread!");
186 }
187 runner = new Thread(() -> {
188 started = true;
189 try {
190 synchronized (lock) {
191 held = true;
192 int[] stamp_h = new int[] { -1 };
193 Action cur_action = Action.HOLD;
194 try {
195 while (true) {
196 cur_action = action.get(stamp_h);
197 int stamp = stamp_h[0];
198 if (cur_action == Action.RELEASE) {
199 // The other thread will deal with reseting action.
200 break;
201 }
202 try {
203 switch (cur_action) {
204 case HOLD:
205 Thread.yield();
206 break;
207 case NOTIFY:
Alex Lightbeae5ac2017-09-21 13:21:51 -0700208 lock.DoNotify();
Alex Light77fee872017-09-05 14:51:49 -0700209 break;
210 case NOTIFY_ALL:
Alex Lightbeae5ac2017-09-21 13:21:51 -0700211 lock.DoNotifyAll();
Alex Light77fee872017-09-05 14:51:49 -0700212 break;
213 case TIMED_WAIT:
Alex Lightbeae5ac2017-09-21 13:21:51 -0700214 lock.DoWait(timeout);
Alex Light77fee872017-09-05 14:51:49 -0700215 break;
216 case WAIT:
Alex Lightbeae5ac2017-09-21 13:21:51 -0700217 lock.DoWait();
Alex Light77fee872017-09-05 14:51:49 -0700218 break;
219 default:
220 throw new Error("Unknown action " + action);
221 }
222 } finally {
223 // reset action back to hold if it isn't something else.
224 action.compareAndSet(cur_action, Action.HOLD, stamp, stamp+1);
225 }
226 }
227 } catch (Exception e) {
228 throw new TestException("Got an error while performing action " + cur_action, e);
229 }
230 }
231 } finally {
232 held = false;
233 started = false;
234 }
235 }, "Locker thread " + cnt.getAndIncrement() + " for " + lock);
236 // Make sure we can get any exceptions this throws.
237 runner.setUncaughtExceptionHandler((t, e) -> { exe = e; });
238 runner.start();
239 }
240
241 public void waitForLockToBeHeld() throws Exception {
242 while (true) {
243 if (IsLocked() && Objects.equals(runner, Monitors.getObjectMonitorUsage(lock).owner)) {
244 return;
245 }
246 }
247 }
248
249 public synchronized void waitForNotifySleep() throws Exception {
250 if (runner == null) {
251 throw new Error("No thread trying to lock!");
252 }
253 do {
254 checkException();
255 } while (!started ||
256 !Arrays.asList(Monitors.getObjectMonitorUsage(lock).notifyWaiters).contains(runner));
257 }
258
259 public synchronized void waitForContendedSleep() throws Exception {
260 if (runner == null) {
261 throw new Error("No thread trying to lock!");
262 }
263 do {
264 checkException();
265 } while (!started ||
266 runner.getState() != Thread.State.BLOCKED ||
267 !Arrays.asList(Monitors.getObjectMonitorUsage(lock).waiters).contains(runner));
268 }
269
270 public synchronized void DoNotify() {
271 if (!IsLocked()) {
272 throw new Error("Not locked");
273 }
274 setAction(Action.NOTIFY);
275 }
276
277 public synchronized void DoNotifyAll() {
278 if (!IsLocked()) {
279 throw new Error("Not locked");
280 }
281 setAction(Action.NOTIFY_ALL);
282 }
283
284 public synchronized void DoTimedWait() throws Exception {
285 if (!IsLocked()) {
286 throw new Error("Not locked");
287 }
288 setAction(Action.TIMED_WAIT);
289 }
290
291 public synchronized void DoWait() throws Exception {
292 if (!IsLocked()) {
293 throw new Error("Not locked");
294 }
295 setAction(Action.WAIT);
296 }
297
298 public synchronized void interruptWorker() throws Exception {
299 if (!IsLocked()) {
300 throw new Error("Not locked");
301 }
302 runner.interrupt();
303 }
304
305 public synchronized void waitForActionToFinish() throws Exception {
306 checkException();
307 while (action.getReference() != Action.HOLD) { checkException(); }
308 }
309
310 public synchronized void DoUnlock() throws Exception {
311 Error throwing = null;
312 if (!IsLocked()) {
313 // We might just be racing some exception that was thrown by the worker thread. Cache the
314 // exception, we will throw one from the worker before this one.
315 throwing = new Error("Not locked!");
316 }
317 setAction(Action.RELEASE);
318 Thread run = runner;
319 runner = null;
320 while (held) {}
321 run.join();
322 action.set(Action.HOLD, 0);
323 // Make sure to throw any exception that occurred since it might not have unlocked due to our
324 // request.
325 checkException();
326 DoCleanup();
327 if (throwing != null) {
328 throw throwing;
329 }
330 }
331
332 public synchronized void DoCleanup() throws Exception {
333 if (runner != null) {
334 Thread run = runner;
335 runner = null;
336 while (held) {}
337 run.join();
338 }
339 action.set(Action.HOLD, 0);
340 exe = null;
341 }
342 }
Alex Lightce568642017-09-05 16:54:25 -0700343}
344