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