blob: 0995cf04881faf52d9bc85746763c85738b59c01 [file] [log] [blame]
/*
* Copyright (C) 2017 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package art;
import java.util.concurrent.CountDownLatch;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
public class Test923 {
public static void run() throws Exception {
doTest();
}
private static void doTest() throws Exception {
// Start a watchdog, to make sure on deadlocks etc the test dies.
startWatchdog();
sharedId = createRawMonitor();
output = new ArrayList<String>(100);
simpleTests(sharedId);
for (String s : output) {
System.out.println(s);
}
output.clear();
threadTests(sharedId);
destroyRawMonitor(sharedId);
}
private static void simpleTests(long id) {
unlock(id); // Should fail.
lock(id);
unlock(id);
unlock(id); // Should fail.
lock(id);
lock(id);
unlock(id);
unlock(id);
unlock(id); // Should fail.
rawWait(id, 0); // Should fail.
rawWait(id, -1); // Should fail.
rawWait(id, 1); // Should fail.
lock(id);
rawWait(id, 50);
unlock(id);
unlock(id); // Should fail.
rawNotify(id); // Should fail.
lock(id);
rawNotify(id);
unlock(id);
unlock(id); // Should fail.
rawNotifyAll(id); // Should fail.
lock(id);
rawNotifyAll(id);
unlock(id);
unlock(id); // Should fail.
}
private static void threadTests(final long id) throws Exception {
final int N = 10;
final CountDownLatch waitLatch = new CountDownLatch(N);
final CountDownLatch wait2Latch = new CountDownLatch(1);
Runnable r = new Runnable() {
@Override
public void run() {
lock(id);
waitLatch.countDown();
rawWait(id, 0);
firstAwakened = Thread.currentThread();
appendToLog("Awakened");
unlock(id);
wait2Latch.countDown();
}
};
List<Thread> threads = new ArrayList<Thread>();
for (int i = 0; i < N; i++) {
Thread t = new Thread(r);
threads.add(t);
t.start();
}
// Wait till all threads have been started.
waitLatch.await();
// Hopefully enough time for all the threads to progress into wait.
Thread.yield();
Thread.sleep(500);
// Wake up one.
lock(id);
rawNotify(id);
unlock(id);
wait2Latch.await();
// Wait a little bit more to see stragglers. This is flaky - spurious wakeups could
// make the test fail.
Thread.yield();
Thread.sleep(500);
if (firstAwakened != null) {
firstAwakened.join();
}
// Wake up everyone else.
lock(id);
rawNotifyAll(id);
unlock(id);
// Wait for everyone to die.
for (Thread t : threads) {
t.join();
}
// Check threaded output.
Iterator<String> it = output.iterator();
// 1) Start with N locks and Waits.
{
int locks = 0;
int waits = 0;
for (int i = 0; i < 2*N; i++) {
String s = it.next();
if (s.equals("Lock")) {
locks++;
} else if (s.equals("Wait")) {
if (locks <= waits) {
System.out.println(output);
throw new RuntimeException("Wait before Lock");
}
waits++;
} else {
System.out.println(output);
throw new RuntimeException("Unexpected operation: " + s);
}
}
}
// 2) Expect Lock + Notify + Unlock.
expect("Lock", it, output);
expect("Notify", it, output);
expect("Unlock", it, output);
// 3) A single thread wakes up, runs, and dies.
expect("Awakened", it, output);
expect("Unlock", it, output);
// 4) Expect Lock + NotifyAll + Unlock.
expect("Lock", it, output);
expect("NotifyAll", it, output);
expect("Unlock", it, output);
// 5) N-1 threads wake up, run, and die.
{
int expectedUnlocks = 0;
int ops = 2 * (N-1);
for (int i = 0; i < ops; i++) {
String s = it.next();
if (s.equals("Awakened")) {
expectedUnlocks++;
} else if (s.equals("Unlock")) {
expectedUnlocks--;
if (expectedUnlocks < 0) {
System.out.println(output);
throw new RuntimeException("Unexpected unlock");
}
}
}
}
// 6) That should be it.
if (it.hasNext()) {
System.out.println(output);
throw new RuntimeException("Unexpected trailing output, starting with " + it.next());
}
output.clear();
System.out.println("Done");
}
private static void expect(String s, Iterator<String> it, List<String> output) {
String t = it.next();
if (!s.equals(t)) {
System.out.println(output);
throw new RuntimeException("Expected " + s + " but got " + t);
}
}
private static void lock(long id) {
appendToLog("Lock");
rawMonitorEnter(id);
}
private static void unlock(long id) {
appendToLog("Unlock");
try {
rawMonitorExit(id);
} catch (RuntimeException e) {
appendToLog(e.getMessage());
}
}
private static void rawWait(long id, long millis) {
appendToLog("Wait");
try {
rawMonitorWait(id, millis);
} catch (RuntimeException e) {
appendToLog(e.getMessage());
}
}
private static void rawNotify(long id) {
appendToLog("Notify");
try {
rawMonitorNotify(id);
} catch (RuntimeException e) {
appendToLog(e.getMessage());
}
}
private static void rawNotifyAll(long id) {
appendToLog("NotifyAll");
try {
rawMonitorNotifyAll(id);
} catch (RuntimeException e) {
appendToLog(e.getMessage());
}
}
private static synchronized void appendToLog(String s) {
output.add(s);
}
private static void startWatchdog() {
Runnable r = new Runnable() {
@Override
public void run() {
long start = System.currentTimeMillis();
// Give it a minute.
long end = 60 * 1000 + start;
for (;;) {
long delta = end - System.currentTimeMillis();
if (delta <= 0) {
break;
}
try {
Thread.currentThread().sleep(delta);
} catch (Exception e) {
}
}
System.out.println("TIMEOUT!");
System.exit(1);
}
};
Thread t = new Thread(r);
t.setDaemon(true);
t.start();
}
static volatile long sharedId;
static List<String> output;
static Thread firstAwakened;
private static native long createRawMonitor();
private static native void destroyRawMonitor(long id);
private static native void rawMonitorEnter(long id);
private static native void rawMonitorExit(long id);
private static native void rawMonitorWait(long id, long millis);
private static native void rawMonitorNotify(long id);
private static native void rawMonitorNotifyAll(long id);
}