001: /*
002: * Copyright 2002 (C) TJDO.
003: * All rights reserved.
004: *
005: * This software is distributed under the terms of the TJDO License version 1.0.
006: * See the terms of the TJDO License in the documentation provided with this software.
007: *
008: * $Id: ReadWriteLockTest.java,v 1.3 2002/11/08 05:06:28 jackknifebarber Exp $
009: */
010:
011: package com.triactive.jdo.util.test;
012:
013: import com.triactive.jdo.util.ReadWriteLock;
014: import java.util.Random;
015: import junit.framework.TestCase;
016:
017: /**
018: * Tests the functionality of a {@link ReadWriteLock}.
019: *
020: * @author <a href="mailto:mmartin5@austin.rr.com">Mike Martin</a>
021: * @version $Revision: 1.3 $
022: */
023:
024: public class ReadWriteLockTest extends TestCase {
025: private static final int MAX_TEST_TIME = 30;
026: private static final int NUM_TEST_THREADS = 10;
027: private static final int NUM_OUTER_ITERATIONS = 500;
028: private static final int NUM_INNER_ITERATIONS = 2500;
029: private static final int YIELD_FREQUENCY = 250;
030:
031: private Throwable threadFailure;
032:
033: /**
034: * Used by the JUnit framework to construct tests. Normally, programmers
035: * would never explicitly use this constructor.
036: *
037: * @param name Name of the <tt>TestCase</tt>.
038: */
039:
040: public ReadWriteLockTest(String name) {
041: super (name);
042: }
043:
044: public void testBasicFunction() throws Throwable {
045: ThreadGroup group = new ThreadGroup("ReadWriteLockTest");
046: Thread[] threads = new Thread[NUM_TEST_THREADS];
047: ReadWriteLock rwl = new ReadWriteLock();
048: Object[] objs = new Object[1];
049:
050: /*
051: * Create and start all the threads running the test.
052: */
053: for (int i = 0; i < NUM_TEST_THREADS; ++i)
054: threads[i] = new Thread(group, new TestThread(
055: new Random(i), rwl, objs), "Test thread #" + i);
056:
057: threadFailure = null;
058:
059: for (int i = 0; i < NUM_TEST_THREADS; ++i)
060: threads[i].start();
061:
062: /*
063: * Wait up to MAX_TEST_TIME seconds for all threads to finish. Any
064: * longer is assumed to be a failure.
065: */
066: long startTime = System.currentTimeMillis();
067: int runningThreads;
068:
069: do {
070: runningThreads = group.enumerate(threads);
071:
072: for (int i = 0; i < runningThreads; ++i) {
073: threads[i].join(250);
074:
075: if (System.currentTimeMillis() - startTime > MAX_TEST_TIME * 1000) {
076: for (int j = 0; j < runningThreads; ++j)
077: threads[j].interrupt();
078:
079: fail("Test has taken more than "
080: + MAX_TEST_TIME
081: + " seconds; it is assumed to be deadlocked");
082: }
083: }
084: } while (runningThreads > 0);
085:
086: /*
087: * If any thread threw an exception, rethrow the last one that
088: * occurred.
089: */
090: if (threadFailure != null)
091: throw threadFailure;
092: }
093:
094: private class TestThread implements Runnable {
095: private final Random rnd;
096: private final ReadWriteLock rwl;
097: private final Object[] objs;
098:
099: public TestThread(Random rnd, ReadWriteLock rwl, Object[] objs) {
100: this .rnd = rnd;
101: this .rwl = rwl;
102: this .objs = objs;
103: }
104:
105: public void run() {
106: try {
107: /*
108: * Test various legal and illegal nested locking patterns
109: * repeatedly in a random order.
110: */
111: final String[] lockPatterns = {
112: /* Legal patterns */
113: "R", "W", "RR", "WR", "WW", "RRR", "WRR", "WWR", "WWW",
114:
115: /* Illegal patterns */
116: "RW", "RRW", "RRRW", "RRRRW" };
117:
118: for (int i = 0; i < NUM_OUTER_ITERATIONS
119: && threadFailure == null; ++i) {
120: String pattern = lockPatterns[rnd
121: .nextInt(lockPatterns.length)];
122: boolean isLegalPattern = pattern.indexOf("RW") == -1;
123:
124: try {
125: tryPattern(pattern);
126:
127: if (!isLegalPattern)
128: fail("Illegal nested lock sequence "
129: + pattern
130: + " should have thrown an IllegalStateException");
131: } catch (IllegalStateException e) {
132: if (isLegalPattern)
133: throw e;
134: }
135: }
136: } catch (Throwable t) {
137: threadFailure = t;
138: }
139: }
140:
141: private void tryPattern(String lockPattern)
142: throws InterruptedException {
143: boolean isWriteLock = lockPattern.charAt(0) == 'W';
144:
145: if (isWriteLock)
146: rwl.writeLock();
147: else
148: rwl.readLock();
149:
150: try {
151: Object obj;
152:
153: /*
154: * If writing, assign a new value to the array, else read the
155: * existing value.
156: */
157: if (isWriteLock) {
158: obj = this ;
159: objs[0] = obj;
160: } else
161: obj = objs[0];
162:
163: /*
164: * Assert that the current value doesn't change over many
165: * successive accesses. Occasionally yield to other threads.
166: */
167: for (int i = 0; i < NUM_INNER_ITERATIONS; ++i) {
168: ReadWriteLockTest.this .assertSame(obj, objs[0]);
169:
170: if (i % YIELD_FREQUENCY == 0)
171: Thread.yield();
172: }
173:
174: /*
175: * If the pattern calls for nested locks, recurse.
176: */
177: if (lockPattern.length() > 1)
178: tryPattern(lockPattern.substring(1));
179: } finally {
180: rwl.unlock();
181: }
182: }
183: }
184: }
|