blob: cfbd2ff14490af782c39a315559ac81549e9e69b [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you 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 org.openide.util;
import java.io.IOException;
import java.lang.reflect.InvocationTargetException;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.logging.Level;
import java.util.logging.Logger;
import org.netbeans.junit.Log;
import org.netbeans.junit.NbTestCase;
import org.netbeans.junit.RandomlyFails;
import org.netbeans.modules.openide.util.DefaultMutexImplementation;
public class ReadWriteAccessTest extends NbTestCase {
Mutex.Privileged p;
Mutex m;
public ReadWriteAccessTest(java.lang.String testName) {
super(testName);
}
/** Sets up the test.
*/
@Override
protected void setUp () {
p = new Mutex.Privileged ();
m = new Mutex (p);
DefaultMutexImplementation.beStrict = true;
}
@Override
protected Level logLevel() {
return Level.FINEST;
}
public void testReadWriteRead() throws Exception {
final Object lock = new Object();
final Mutex.Privileged mPriv = new Mutex.Privileged();
final Mutex tmpMutex = new Mutex( mPriv );
synchronized ( lock ) {
mPriv.enterReadAccess();
new Thread() {
@Override
public void run () {
synchronized( lock ) {
lock.notifyAll();
}
mPriv.enterWriteAccess();
synchronized ( lock ) {
lock.notifyAll();
mPriv.exitWriteAccess();
}
}
}.start();
lock.wait();
}
Thread.sleep (100);
mPriv.enterReadAccess();
mPriv.exitReadAccess();
synchronized ( lock ) {
mPriv.exitReadAccess();
lock.wait();
}
}
/** Simple test to execute read access and write access imediatelly.
*/
public void testPostImmediatelly () {
State s = new State ();
m.postReadRequest(s);
if (s.state != 1) {
fail ("Read request not started immediatelly");
}
m.postWriteRequest (s);
if (s.state != 2) {
fail ("Write request not started immediately");
}
}
/** Behaviour of postWriteRequest is defined by this test.
*/
public void testPostWriteRequest () {
State s = new State ();
// first enter
p.enterWriteAccess ();
p.enterReadAccess ();
m.postWriteRequest(s);
if (s.state != 0) {
fail ("Write request started when we are in read access");
}
p.exitReadAccess ();
if (s.state != 1) {
fail ("Write request not run when leaving read access: " + s.state);
}
// exiting
p.exitWriteAccess ();
if (s.state != 1) {
fail ("Run more times?: " + s.state);
}
}
/** Behaviour of postReadRequest is defined by this test.
*/
public void testPostReadRequest () {
State s = new State ();
// first enter
p.enterWriteAccess ();
m.postReadRequest(s);
if (s.state != 0) {
fail ("Read request started when we are in write access");
}
p.exitWriteAccess ();
if (s.state != 1) {
fail ("Read request not run when leaving write access: " + s.state);
}
if (s.state != 1) {
fail ("Run more times?: " + s.state);
}
}
/** Test enter from S mode to X mode *
public void testXtoS() {
State s = new State ();
p.enterReadAccess ();
p.enterWriteAccess ();
s.run();
p.exitWriteAccess();
p.exitReadAccess();
if (s.state != 1) {
fail ("Run more times?: " + s.state);
}
}
*/
/** Tests posting write and read requests while the SimpleMutex is held
* in X mode and was entered in S mode as well
*/
public void testPostWriteReadRequests() {
State s = new State ();
// first enter
p.enterWriteAccess ();
p.enterReadAccess ();
m.postWriteRequest(s);
if (s.state != 0) {
fail ("Write request started when we are in read access");
}
m.postReadRequest(s);
if (s.state != 0) {
fail ("Read request started when we are in write access");
}
p.exitReadAccess ();
if (s.state != 1) {
fail ("Write request not run when leaving read access: " + s.state);
}
// exiting
p.exitWriteAccess ();
if (s.state != 2) {
fail ("Read request not run when leaving write access: " + s.state);
}
consistencyCheck();
}
/** Tests simple postWriteRequest */
public void testSimplePostWriteRequest() {
State s = new State ();
m.postWriteRequest(s);
if (s.state != 1) {
fail ("Write request not run: " + s.state);
}
consistencyCheck();
}
/** Tests simple postReadRequest */
public void testSimplePostReadRequest() {
State s = new State ();
m.postReadRequest(s);
if (s.state != 1) {
fail ("Read request not run: " + s.state);
}
consistencyCheck();
}
// starts a new thread, after return the thread will hold lock "p" in
// mode X for timeout milliseconds
private static void asyncEnter(final Mutex.Privileged p, final boolean X, final long timeout) throws InterruptedException {
asyncEnter(p, X, timeout, null);
}
// starts a new thread, after return the thread will hold lock "p" in
// mode X for timeout milliseconds, the new thread execs "run" first
private static void asyncEnter(final Mutex.Privileged p, final boolean X, final long timeout, final Runnable run) throws InterruptedException {
final Object lock = new Object();
synchronized (lock) {
new Thread(new Runnable() {
public void run() {
if (X) {
p.enterWriteAccess();
} else {
p.enterReadAccess();
}
synchronized (lock) {
lock.notify();
}
if (run != null) {
run.run();
}
try {
Thread.sleep(timeout);
} catch (InterruptedException e) {
e.printStackTrace();
}
if (X) {
p.exitWriteAccess();
} else {
p.exitReadAccess();
}
}
}).start();
lock.wait();
}
}
/** Tests enterWriteAccess while the SimpleMutex is contended in X mode by
* another thread
*/
public void testXContendedX() throws InterruptedException {
asyncEnter(p, true, 2000);
// first enter
p.enterWriteAccess();
p.exitWriteAccess();
consistencyCheck();
}
/** Tests enterReadAccess while the SimpleMutex is contended in X mode by
* another thread
*/
public void testXContendedS() throws InterruptedException {
asyncEnter(p, true, 2000);
// first enter
p.enterReadAccess();
p.exitReadAccess();
consistencyCheck();
}
/** Tests enterWriteAccess while the SimpleMutex is contended in S mode by
* another thread
*/
public void testSContendedX() throws InterruptedException {
asyncEnter(p, false, 2000);
// first enter
p.enterWriteAccess();
p.exitWriteAccess();
consistencyCheck();
}
/** Tests enterReadAccess while the SimpleMutex is contended in S mode by
* another thread
*/
public void testSContendedS() throws InterruptedException {
asyncEnter(p, false, 2000);
// first enter
p.enterReadAccess();
p.exitReadAccess();
consistencyCheck();
}
/** Tests postWriteRequest while the SimpleMutex is contended in X mode by
* another thread
*/
public void testXContendedPx() throws InterruptedException {
asyncEnter(p, true, 2000);
State s = new State ();
m.postWriteRequest(s);
if (s.state != 1) {
fail ("Write request not run: " + s.state);
}
consistencyCheck();
}
/** Tests postReadRequest while the SimpleMutex is contended in X mode by
* another thread
*/
public void testXContendedPs() throws InterruptedException {
asyncEnter(p, true, 2000);
State s = new State ();
m.postReadRequest(s);
if (s.state != 1) {
fail ("Read request not run: " + s.state);
}
consistencyCheck();
}
/** Tests postWriteRequest while the SimpleMutex is contended in S mode by
* another thread
*/
public void testSContendedPx() throws InterruptedException {
asyncEnter(p, false, 2000);
State s = new State ();
m.postWriteRequest(s);
if (s.state != 1) {
fail ("Write request not run: " + s.state);
}
consistencyCheck();
}
/** Tests postReadRequest while the SimpleMutex is contended in S mode by
* another thread
*/
public void testSContendedPs() throws InterruptedException {
asyncEnter(p, false, 2000);
State s = new State ();
m.postReadRequest(s);
if (s.state != 1) {
fail ("Write request not run: " + s.state);
}
consistencyCheck();
}
/** Tests postWriteRequest and postReadRequest while the SimpleMutex is contended in S mode by
* another thread as well as this thread.
*/
public void testSContendedSPsPx() throws InterruptedException {
asyncEnter(p, false, 2000);
State s = new State ();
p.enterReadAccess();
m.postReadRequest(s);
if (s.state != 1) {
fail ("Read request not run: " + s.state);
}
m.postWriteRequest(s);
if (s.state != 1) {
fail ("Write request run: " + s.state);
}
p.exitReadAccess();
if (s.state != 2) {
fail ("Write request not run: " + s.state);
}
consistencyCheck();
}
/** The SimpleMutex is held in S mode by a thread which also posted a
* write request. Another thread tries enterWriteAccess.
*/
public void testSPxContendedX() throws Exception {
final State s = new State ();
asyncEnter(p, false, 2000, new Runnable() {
public void run() {
try {
Thread.sleep(1000);
} catch (Exception e) {
e.printStackTrace();
}
m.postWriteRequest(s);
if (s.state == 1) {
fail ("Write request run: " + s.state);
}
}
});
p.enterWriteAccess();
if (s.state != 1) {
fail ("Write request not run: " + s.state);
}
p.exitWriteAccess();
consistencyCheck();
}
/**
* Test case for #16577. Grab X,S and post X request,
* the second thread waits for X, causing the mutex to be
* in CHAINED.
*/
public void testXSPxContendedX() throws Exception {
final State s = new State ();
asyncEnter(p, true, 2000, new Runnable() {
public void run() {
p.enterReadAccess();
try {
Thread.sleep(1000);
} catch (Exception e) {
e.printStackTrace();
}
m.postWriteRequest(s);
p.exitReadAccess();
if (s.state != 1) {
fail ("Write request not run: " + s.state);
}
}
});
p.enterWriteAccess();
p.exitWriteAccess();
consistencyCheck();
}
/**
* The scenario:
* Cast:
* Thread A: M.reader [X] trying to lock(L)
* Thread B: L owner [X] trying to enter M.read
* Thread C: M.reader trying to M.writeReenter on leave
*
* Actions:
* - first let A and B reach point [X]
* - unfuse A so it block on lock(L)
* - start C, it will reach exitReadAccess and block on reenter as writer
* - unfuse B so it should perform its legitimate read access
*
* What should happen then (if SimpleMutex works OK):
* - B unlocks L and die
* - A locks/unlocks L, leave readAccess and die
* - C performs its write and die
*/
public void testStarvation68106() throws Exception {
final Mutex.Privileged PR = new Mutex.Privileged();
final Mutex M = new Mutex(PR);
final Object L = new Object();
final boolean[] done = new boolean[3];
final Ticker tickX1 = new Ticker();
final Ticker tickX2 = new Ticker();
final Ticker tickX3 = new Ticker();
Thread A = new Thread("A") { public @Override void run() {
PR.enterReadAccess();
tickX1.tick();
tickX2.waitOn();
synchronized(L) {
done[0] = true;
}
PR.exitReadAccess();
}};
Thread B = new Thread("B") { public @Override void run() {
synchronized(L) {
tickX2.tick();
tickX3.tick();
tickX1.waitOn();
PR.enterReadAccess();
done[1] = true;
PR.exitReadAccess();
}
}};
Thread C = new Thread("C") { public @Override void run() {
PR.enterReadAccess();
M.postWriteRequest(new Runnable() {public void run() {
done[2] = true;
}});
PR.exitReadAccess();
}};
A.start();
tickX1.waitOn();
// A reached point X
B.start();
tickX3.waitOn();
// B reached point X, unlocked A so in would block on lock(L)
C.start();
Thread.sleep(100); // wait for C to perform exitReadAccess (can't
// tick as it will block in case of failure...)
tickX1.tick();
// push B, everything should finish after this
// wait for them for a while
A.join(2000);
B.join(2000);
C.join(2000);
if (!done[0] || !done[1] || !done[2]) {
StringBuffer sb = new StringBuffer();
sb.append("A: "); sb.append(done[0]);
sb.append(" B: "); sb.append(done[1]);
sb.append(" C: "); sb.append(done[2]);
sb.append("\n");
dumpStrackTrace(A, sb);
dumpStrackTrace(B, sb);
dumpStrackTrace(C, sb);
fail(sb.toString());
}
}
/**
* The scenario:
* - Have 3 threads, A, B and C
* - writeLock mutex1 in A
* - writeLock mutex2 in B
* - postReadLock mutex2 in B
* - writeLock mutex1 in B
* - writeLock mutex2 in C
* - readLock mutex2 in A
* - leaveWriteLock mutex2 in B
*
*/
public void testStarvation49466() throws Exception {
final Mutex.Privileged pr1 = new Mutex.Privileged();
final Mutex mutex1 = new Mutex(pr1);
final Mutex.Privileged pr2 = new Mutex.Privileged();
final Mutex mutex2 = new Mutex(pr2);
final boolean[] done = new boolean[3];
final Ticker tick0 = new Ticker();
final Ticker tick1 = new Ticker();
final Ticker tick2 = new Ticker();
final Ticker tick3 = new Ticker();
Thread A = new Thread() {
public @Override void run() {
pr1.enterWriteAccess();
tick0.tick();
tick1.waitOn();
pr2.enterReadAccess();
done[0] = true;
pr2.exitReadAccess();
pr1.exitWriteAccess();
}
};
// writeLock mutex1 in A
A.start();
tick0.waitOn();
Thread B = new Thread() {
public @Override void run() {
pr2.enterWriteAccess();
mutex2.postReadRequest(new Runnable() {
public void run() {
tick0.tick();
pr1.enterWriteAccess();
done[1] = true;
pr1.exitWriteAccess();
}
});
tick0.tick();
tick2.waitOn();
pr2.exitWriteAccess();
}
};
// writeLock mutex2 in B
B.start();
tick0.waitOn();
/*
* The test fails even when using only first two threads.
*
Thread C = new Thread() {
public void run() {
tick0.tick(); // have to tick in advance and wait :-(
pr2.enterWriteAccess();
done[2] = true;
pr2.exitWriteAccess();
}
};
// writeLock mutex2 in C
C.start();
tick0.waitOn();
Thread.sleep(1000); // between tick and C enqueued ...
*/
// readLock mutex2 in A
tick1.tick(); // enqueues A in mutex2's queue
Thread.sleep(1000); // between tick and A enqueued ...
// leaveWriteLock mutex2 in B
tick2.tick();
// postReadLock mutex2 in B
tick0.waitOn();
// System.err.println("Do a thread dump now!");
Thread.sleep(2000); // give them some time ...
assertTrue("Thread A finished", done[0]);
assertTrue("Thread B finished", done[1]);
// assertTrue("Thread C succeed", done[2]);
}
public void testReadEnterAfterPostWriteWasContended87932() throws Exception {
final Logger LOG = Logger.getLogger("org.openide.util.test");//testReadEnterAfterPostWriteWasContended87932");
final Mutex.Privileged pr = new Mutex.Privileged();
final Mutex mutex = new Mutex(pr);
final Ticker tick = new Ticker();
class WR implements Runnable {
boolean inWrite;
public void run() {
inWrite = true;
// just keep the write lock for a while
ReadWriteAccessTest.sleep(1000);
inWrite = false;
}
}
WR wr = new WR();
class T extends Thread {
public T() {
super("testReadEnterAfterPostWriteWasContended87932-reader");
}
@Override
public void run() {
pr.enterReadAccess();
tick.tick();
// wait for exploitable place in SimpleMutex
LOG.log(Level.FINE, "wait for exploitable place in SimpleMutex");
pr.exitReadAccess();
//Let the othe thread continue
LOG.log(Level.FINE, "Let the other thread continue");
// the writer gets in now, lets' give him some time.
ReadWriteAccessTest.sleep(50);
pr.enterReadAccess();
// if (inWrite.get()) fail("Another reader inside while writer keeps lock");
pr.exitReadAccess();
}
}
Thread t = new T();
String str = "THREAD:testReadEnterAfterPostWriteWasContended87932-reader MSG:wait for exploitable place in SimpleMutex" +
"THREAD:main MSG:.*Processing posted requests: 2" +
"THREAD:testReadEnterAfterPostWriteWasContended87932-reader MSG:Let the other thread continue";
Log.controlFlow(Logger.getLogger("org.openide.util"), null, str, 100);
pr.enterReadAccess();
t.start();
tick.waitOn();
mutex.postWriteRequest(wr);
pr.exitReadAccess();
t.join(10000);
}
private static class Ticker {
boolean state;
public void waitOn() {
synchronized(this) {
while (!state) {
try {
wait();
} catch (InterruptedException e) {
throw new InternalError();
}
}
state = false; // reusable
}
}
public void tick() {
synchronized(this) {
state = true;
notifyAll();
}
}
}
/**
* Grab X and post S request,
* the second thread waits for X, causing the mutex to be
* in CHAINED.
*/
public void testXPsContendedX() throws Exception {
final State s = new State ();
asyncEnter(p, true, 2000, new Runnable() {
public void run() {
try {
Thread.sleep(1000);
} catch (Exception e) {
e.printStackTrace();
}
m.postReadRequest(s);
if (s.state == 1) {
fail ("Read request run: " + s.state);
}
}
});
p.enterWriteAccess();
Thread.sleep(4000);
p.exitWriteAccess();
consistencyCheck();
}
/** Checks the SimpleMutex is in the consistent state, i.e. enterWriteAccess must pass */
private void consistencyCheck() {
p.enterWriteAccess();
p.exitWriteAccess();
}
public void testNoWayToDoReadAndThenWrite () {
class R implements Runnable {
public void run () {
m.writeAccess (this);
}
}
try {
m.readAccess (new R ());
fail ("This is supposed to throw an IllegalStateException");
} catch (IllegalStateException ex) {
// ok, this is expected
}
}
public void testNoWayToDoWriteThenReadAndThenWrite () {
class R implements Runnable {
public boolean second;
public boolean end;
public boolean ending;
public void run () {
if (end) {
ending = true;
return;
}
if (second) {
end = true;
m.writeAccess (this);
} else {
second = true;
m.readAccess (this);
}
}
}
R r = new R ();
try {
m.writeAccess (r);
fail ("This is supposed to throw an IllegalStateException");
} catch (IllegalStateException ex) {
// ok, this is expected
assertTrue ("We were in the write access section", r.second);
assertTrue ("We were before the writeAcess(this)", r.end);
assertFalse ("We never reached ending", r.ending);
}
}
public void testIsOrIsNotInReadOrWriteAccess () {
new ReadWriteChecking ("No r/w", Boolean.FALSE, Boolean.FALSE).run ();
m.readAccess (new ReadWriteChecking ("r but no w", Boolean.TRUE, Boolean.FALSE));
m.writeAccess (new ReadWriteChecking ("w but no r", Boolean.FALSE, Boolean.TRUE));
m.readAccess (new Runnable () {
public void run () {
m.postReadRequest (new ReadWriteChecking ("+r -w", Boolean.TRUE, Boolean.FALSE));
}
});
m.readAccess (new Runnable () {
public void run () {
m.postWriteRequest (new ReadWriteChecking ("-r +w", Boolean.FALSE, Boolean.TRUE));
}
});
m.writeAccess (new Runnable () {
public void run () {
m.postReadRequest (new ReadWriteChecking ("+r -w", Boolean.TRUE, Boolean.FALSE));
}
});
m.writeAccess (new Runnable () {
public void run () {
m.postWriteRequest (new ReadWriteChecking ("-r +w", Boolean.FALSE, Boolean.TRUE));
}
});
// write->read->test (downgrade from write to read)
m.writeAccess (new Runnable () {
public boolean second;
public void run () {
if (!second) {
second = true;
m.readAccess (this);
return;
}
class P implements Runnable {
public boolean exec;
public void run () {
exec = true;
}
}
P r = new P ();
P w = new P ();
m.postWriteRequest (w);
m.postReadRequest (r);
assertFalse ("Writer not executed", w.exec);
assertFalse ("Reader not executed", r.exec);
m.readAccess (new ReadWriteChecking ("+r +w", Boolean.TRUE, Boolean.TRUE));
}
});
new ReadWriteChecking ("None at the end", Boolean.FALSE, Boolean.FALSE).run ();
}
// [pnejedly:] There was an attempt to fix Starvation68106 by allowing read
// enter while SimpleMutex is currently in CHAIN mode, but that's wrong, as it can
// be CHAIN/W (write granted, readers waiting). Let's cover this with a test.
@RandomlyFails // NB-Core-Build #8070: B finished after unblocking M
public void testReaderCannotEnterWriteChainedMutex() throws Exception {
final Mutex.Privileged PR = new Mutex.Privileged();
final Mutex M = new Mutex(PR);
final boolean[] done = new boolean[2];
final Ticker tickX1 = new Ticker();
final Ticker tickX2 = new Ticker();
final Ticker tickX3 = new Ticker();
PR.enterWriteAccess();
Thread A = new Thread("A") { public @Override void run() {
PR.enterReadAccess();
done[0] = true;
PR.exitReadAccess();
}};
Thread B = new Thread("B") { public @Override void run() {
PR.enterReadAccess();
done[1] = true;
PR.exitReadAccess();
}};
A.start();
Thread.sleep(100); // wait for A to chain in M
B.start();
Thread.sleep(100); // B should chain as well
assertFalse ("B should chain-wait", done[1]);
// final cleanup and consistency check:
PR.exitWriteAccess();
A.join(1000);
B.join(1000);
assertTrue("A finished after unblocking M", done[0]);
assertTrue("B finished after unblocking M", done[1]);
}
private void exceptionsReporting(final Throwable t) throws Exception {
final IOException e1 = new IOException();
final Mutex mm = m;
final Runnable secondRequest = new Runnable() {
public void run() {
if (t instanceof RuntimeException) {
throw (RuntimeException)t;
} else {
throw (Error)t;
}
}
};
Mutex.ExceptionAction<Object> firstRequest = new Mutex.ExceptionAction<Object>() {
public Object run () throws Exception {
mm.postWriteRequest(secondRequest);
throw e1;
}
};
try {
m.readAccess(firstRequest);
} catch (MutexException mu) {
Exception e = mu.getException();
assertEquals("IOException correctly reported", e, e1);
return;
} catch (Throwable e) {
fail("a problem in postWriteRequest() should not swallow any " +
"exception thrown in readAccess() because that might be " +
"the cause of the problem. "+e.toString());
}
fail("should never get here");
}
public void testThrowingAssertionErrorInSpecialCase() throws Exception {
exceptionsReporting(new AssertionError());
}
public void testThrowingRuntimeExceptionInSpecialCase() throws Exception {
exceptionsReporting(new RuntimeException());
}
private void dumpStrackTrace(Thread thread, StringBuffer sb) throws IllegalAccessException, InvocationTargetException {
sb.append("StackTrace for thread: " + thread.getName() + "\n");
StackTraceElement[] arr = thread.getStackTrace();
for (int i = 0; i < arr.length; i++) {
sb.append(arr[i].toString());
sb.append("\n");
}
}
private class ReadWriteChecking implements Runnable {
public Boolean read;
public Boolean write;
public String msg;
public ReadWriteChecking (String msg, Boolean read, Boolean write) {
assertNotNull ("Msg cannot be null", msg);
this.msg = msg;
this.read = read;
this.write = write;
}
@Override
protected void finalize () {
assertNull ("Run method was not called!", msg);
}
public void run () {
if (write != null) assertEquals (msg, write.booleanValue (), m.isWriteAccess ());
if (read != null) assertEquals (msg, read.booleanValue (), m.isReadAccess ());
msg = null;
}
}
private static class State implements Runnable {
public int state;
public void run () {
state++;
}
} // end of State
private static final void sleep(long ms) {
try {
Thread.sleep(ms);
} catch (InterruptedException ex) {
ex.printStackTrace();
}
}
/** Test that interrupted flag of thread entering SimpleMutex is not eaten in
* SimpleMutex.QueueCell.sleep.
* - enter read access in main thread
* - start another thread which tries to aquire write access
* - interrupt that thread
* - wait until thread is stopped at wait() in SimpleMutex.QueueCell.sleep
* - exit read access to release waiting thread
* - check interrupted flag is set
*/
public void testInterruptedException129003() throws InterruptedException {
final AtomicBoolean interrupted = new AtomicBoolean(false);
p.enterReadAccess();
Thread enteringThread = new Thread("Entering thread") {
@Override
public void run() {
p.enterWriteAccess();
interrupted.set(Thread.interrupted());
p.exitWriteAccess();
}
};
enteringThread.start();
enteringThread.interrupt();
// let enteringThread reach wait() in SimpleMutex.QueueCell.sleep
while(!enteringThread.getState().equals(Thread.State.WAITING)) {
Thread.sleep(100);
}
p.exitReadAccess();
enteringThread.join();
assertTrue("Interrupted thread entering SimpleMutex should be set as interrupted.", interrupted.get());
}
}