pjs/grendel/calypso/util/SelfTestRWLock.java

344 строки
8.3 KiB
Java

/* -*- Mode: Java; tab-width: 2; indent-tabs-mode: nil -*-
*
* The contents of this file are subject to the Mozilla Public License
* Version 1.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.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS IS"
* basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
* the License for the specific language governing rights and limitations
* under the License.
*
* The Original Code is the Grendel mail/news client.
*
* The Initial Developer of the Original Code is Netscape Communications
* Corporation. Portions created by Netscape are Copyright (C) 1997
* Netscape Communications Corporation. All Rights Reserved.
*/
package calypso.util;
class SelfTestRWLock {
static void simpleReadTest(RWLock aLock)
throws InterruptedException
{
for (int i = 0; i < 5; i++) {
aLock.enterReadLock();
}
for (int i = 0; i < 5; i++) {
aLock.exitReadLock();
}
}
static void simpleReadWriteTest(RWLock aLock)
throws InterruptedException
{
for (int i = 0; i < 5; i++) {
aLock.enterReadLock();
}
for (int i = 0; i < 5; i++) {
aLock.enterWriteLock();
}
for (int i = 0; i < 5; i++) {
aLock.exitWriteLock();
}
for (int i = 0; i < 5; i++) {
aLock.exitReadLock();
}
}
static void simpleInvariantReadTest(RWLock aLock)
throws InterruptedException
{
for (int i = 0; i < 5; i++) {
aLock.enterInvariantReadLock();
}
for (int i = 0; i < 5; i++) {
aLock.exitInvariantReadLock();
}
}
static void simpleInvariantReadWriteTest(RWLock aLock)
throws InterruptedException
{
for (int i = 0; i < 5; i++) {
aLock.enterInvariantReadLock();
}
for (int i = 0; i < 5; i++) {
aLock.enterWriteLock();
}
for (int i = 0; i < 5; i++) {
aLock.exitWriteLock();
}
for (int i = 0; i < 5; i++) {
aLock.exitInvariantReadLock();
}
}
// Verify that an out of order exit throws an error
static void outOfOrderExit0(RWLock aLock)
throws InterruptedException, SelfTestException
{
aLock.enterReadLock();
try {
aLock.exitWriteLock();
} catch (Error e) {
return;
}
throw new SelfTestException("whoops");
}
// Verify that an out of order exit throws an error
static void outOfOrderExit1(RWLock aLock)
throws InterruptedException, SelfTestException
{
aLock.enterReadLock();
try {
aLock.exitInvariantReadLock();
} catch (Error e) {
return;
}
throw new SelfTestException("whoops");
}
static void outOfOrderExit2(RWLock aLock)
throws InterruptedException, SelfTestException
{
aLock.enterInvariantReadLock();
try {
aLock.exitReadLock();
} catch (Error e) {
return;
}
throw new SelfTestException("whoops");
}
static void outOfOrderExit3(RWLock aLock)
throws InterruptedException, SelfTestException
{
aLock.enterInvariantReadLock();
try {
aLock.exitWriteLock();
} catch (Error e) {
return;
}
throw new SelfTestException("whoops");
}
static void outOfOrderExit4(RWLock aLock)
throws InterruptedException, SelfTestException
{
aLock.enterWriteLock();
try {
aLock.exitReadLock();
} catch (Error e) {
return;
}
throw new SelfTestException("whoops");
}
static void outOfOrderExit5(RWLock aLock)
throws InterruptedException, SelfTestException
{
aLock.enterWriteLock();
try {
aLock.exitInvariantReadLock();
} catch (Error e) {
return;
}
throw new SelfTestException("whoops");
}
public static void run()
throws SelfTestException
{
// Perform simple tests that have no contention with another thread
try {
simpleReadTest(new RWLock());
simpleReadWriteTest(new RWLock());
simpleInvariantReadTest(new RWLock());
simpleInvariantReadWriteTest(new RWLock());
outOfOrderExit0(new RWLock());
outOfOrderExit1(new RWLock());
outOfOrderExit2(new RWLock());
outOfOrderExit3(new RWLock());
outOfOrderExit4(new RWLock());
outOfOrderExit5(new RWLock());
verifyBlocking(new RWLock(), kRead, kWrite);
verifyBlocking(new RWLock(), kInvariantRead, kWrite);
verifyBlocking(new RWLock(), kWrite, kRead);
verifyBlocking(new RWLock(), kWrite, kInvariantRead);
stressTestContention(new RWLock());
} catch (InterruptedException e) {
// Test was interrupted
e.printStackTrace();
}
}
static final int kRead = 0;
static final int kWrite = 1;
static final int kInvariantRead = 2;
static String[] kStateToString = {
"read-lock", "write-lock", "invariant-read-lock"
};
// Verify that blocking is occuring by using the RWLock to
// protect the helper.fValue and guarantee a correct ordering
// of evaluation.
static void verifyBlocking(RWLock aLock, int aMe, int aHelper)
throws SelfTestException, InterruptedException
{
Enter(aLock, aMe);
RWTestHelper helper = new RWTestHelper(aLock, aHelper, 1, 1, 0);
Thread t1 = new Thread(helper);
t1.start();
// Make sure RWTestHelper is waiting to get the write-lock
Thread.sleep(500);
if (helper.fValue != 1) {
throw new SelfTestException("write-lock didn't block");
}
// Give RWTestHelper the lock; set value to 2 before it gets
// the lock
helper.fValue = 2;
Exit(aLock, aMe);
// make sure RWTestHelper is done
Thread.sleep(500);
if (helper.fValue != 0) {
throw new SelfTestException("helper didn't finish");
}
}
static void stressTestContention(RWLock aLock)
throws SelfTestException, InterruptedException
{
int count = 500000;
int delay = 123;
long start = System.currentTimeMillis();
// Fire up a bunch of threads
for (int i = 0; i < 3*100; i++) {
RWTestHelper helper = new RWTestHelper(aLock, i % 3, 1, count, delay);
Thread t = new Thread(helper);
t.start();
ThreadEnter();
}
ThreadWait();
long end = System.currentTimeMillis();
System.out.println("Stress test: duration=" + (end - start) + "ms");
}
static Object gThreadCounterLock = new Object();
static int gThreadCounter;
static void ThreadEnter()
throws InterruptedException
{
synchronized (gThreadCounterLock) {
gThreadCounter++;
}
}
static void ThreadExit()
throws InterruptedException
{
synchronized (gThreadCounterLock) {
if (--gThreadCounter == 0) {
gThreadCounterLock.notify();
}
}
}
static void ThreadWait()
throws InterruptedException
{
synchronized (gThreadCounterLock) {
while (gThreadCounter > 0) {
gThreadCounterLock.wait();
}
}
}
static final boolean noisy = false;
static void Enter(RWLock aLock, int aHow)
throws InterruptedException
{
if (noisy) {
System.out.println(Thread.currentThread() + ": enter " +
kStateToString[aHow]);
}
switch (aHow) {
case kRead:
aLock.enterReadLock();
break;
case kWrite:
aLock.enterWriteLock();
break;
case kInvariantRead:
aLock.enterInvariantReadLock();
break;
}
}
static void Exit(RWLock aLock, int aHow)
throws InterruptedException
{
if (noisy) {
System.out.println(Thread.currentThread() + ": exit " +
kStateToString[aHow]);
}
switch (aHow) {
case kRead:
aLock.exitReadLock();
break;
case kWrite:
aLock.exitWriteLock();
break;
case kInvariantRead:
aLock.exitInvariantReadLock();
break;
}
}
static class RWTestHelper implements Runnable {
RWLock fLock;
int fHow;
int fValue;
int fCount;
int fDelay;
RWTestHelper(RWLock aLock, int aHow, int aValue, int aCount, int aDelay) {
fLock = aLock;
fHow = aHow;
fValue = aValue;
fCount = aCount;
fDelay = aDelay;
}
public void run() {
try {
for (int i = 0; i < fCount; i++) {
SelfTestRWLock.Enter(fLock, fHow);
fValue /= 4;
SelfTestRWLock.Exit(fLock, fHow);
if ((i % 1000) == 0) {
Thread.sleep(fDelay);
}
}
SelfTestRWLock.ThreadExit();
} catch (InterruptedException e) {
}
}
}
}