Index: openide/test/unit/src/org/openide/util/MutexTest.java =================================================================== RCS file: /cvs/openide/test/unit/src/org/openide/util/MutexTest.java,v retrieving revision 1.7 diff -u -r1.7 MutexTest.java --- openide/test/unit/src/org/openide/util/MutexTest.java 27 Feb 2003 23:41:26 -0000 1.7 +++ openide/test/unit/src/org/openide/util/MutexTest.java 31 Mar 2003 23:14:57 -0000 @@ -13,12 +13,18 @@ package org.openide.util; +import java.awt.EventQueue; +import java.io.IOException; import java.lang.ref.*; import java.util.*; import org.openide.ErrorManager; import junit.framework.*; import org.netbeans.junit.*; +/** + * Test behavior of the read-write mutex. + * @author Jaroslav Tulach, Ales Novak, Petr Hrebejk, Jesse Glick + */ public class MutexTest extends NbTestCase { private Mutex.Privileged p; private Mutex m; @@ -133,7 +139,8 @@ /** Behaviour of postReadRequest is defined by this test. */ - public void testPostReadRequest () { + // Cannot go S -> X + public void DONOTtestPostReadRequest () { State s = new State (); @@ -162,7 +169,8 @@ } /** Test enter from S mode to X mode */ - public void testXtoS() { + // You cannot go from S -> X + public void DONOTtestXtoS() { State s = new State (); p.enterReadAccess (); @@ -193,20 +201,26 @@ m.postReadRequest(s); - if (s.state != 0) { - fail ("Read request started when we are in write access"); + if (s.state != 1) { + fail ("Read request not run when we are in read access inside write access"); } p.exitReadAccess (); - if (s.state != 1) { + if (s.state != 2) { fail ("Write request not run when leaving read access: " + s.state); } + m.postReadRequest(s); + + if (s.state != 2) { + fail ("Read request started when we are in write access"); + } + // exiting p.exitWriteAccess (); - if (s.state != 2) { + if (s.state != 3) { fail ("Read request not run when leaving write access: " + s.state); } @@ -540,4 +554,474 @@ } } // end of State + + // --- TESTS ADDED BY JGLICK --- + + public void testEventAccess() throws Exception { + assertTrue("Not starting in AWT", !EventQueue.isDispatchThread()); + // test canRead, canWrite, correct thread used by synch methods + assertTrue(!Mutex.EVENT.canRead()); + assertTrue(!Mutex.EVENT.canWrite()); + assertEquals(Boolean.TRUE, Mutex.EVENT.readAccess(new Mutex.Action() { + public Object run() { + return new Boolean(EventQueue.isDispatchThread() && + Mutex.EVENT.canRead() && + Mutex.EVENT.canWrite()); + } + })); + assertEquals(Boolean.TRUE, Mutex.EVENT.readAccess(new Mutex.ExceptionAction() { + public Object run() throws Exception { + return new Boolean(EventQueue.isDispatchThread() && + Mutex.EVENT.canRead() && + Mutex.EVENT.canWrite()); + } + })); + assertEquals(Boolean.TRUE, Mutex.EVENT.writeAccess(new Mutex.Action() { + public Object run() { + return new Boolean(EventQueue.isDispatchThread() && + Mutex.EVENT.canRead() && + Mutex.EVENT.canWrite()); + } + })); + assertEquals(Boolean.TRUE, Mutex.EVENT.writeAccess(new Mutex.ExceptionAction() { + public Object run() throws Exception { + return new Boolean(EventQueue.isDispatchThread() && + Mutex.EVENT.canRead() && + Mutex.EVENT.canWrite()); + } + })); + // test that r/wA(Runnable) runs in AWT eventually + final boolean[] b = new boolean[1]; + // first, that r/wA will run (even asynch) + Mutex.EVENT.readAccess(new Runnable() { + public void run() { + synchronized (b) { + b[0] = EventQueue.isDispatchThread(); + b.notify(); + } + } + }); + synchronized (b) { + if (!b[0]) b.wait(9999); + } + assertTrue(b[0]); + Mutex.EVENT.writeAccess(new Runnable() { + public void run() { + synchronized (b) { + b[0] = !EventQueue.isDispatchThread(); + b.notify(); + } + } + }); + synchronized (b) { + if (b[0]) b.wait(9999); + } + assertTrue(!b[0]); + // now that r/wA runs synch in event thread + EventQueue.invokeAndWait(new Runnable() { + public void run() { + Mutex.EVENT.readAccess(new Runnable() { + public void run() { + b[0] = EventQueue.isDispatchThread(); + } + }); + } + }); + assertTrue(b[0]); + EventQueue.invokeAndWait(new Runnable() { + public void run() { + Mutex.EVENT.writeAccess(new Runnable() { + public void run() { + b[0] = !EventQueue.isDispatchThread(); + } + }); + } + }); + // and that pR/WR runs synch outside AWT + assertTrue(!b[0]); + Mutex.EVENT.postReadRequest(new Runnable() { + public void run() { + b[0] = EventQueue.isDispatchThread(); + } + }); + assertTrue(b[0]); + Mutex.EVENT.postWriteRequest(new Runnable() { + public void run() { + b[0] = !EventQueue.isDispatchThread(); + } + }); + assertTrue(!b[0]); + } + + public void testEventExceptions() throws Exception { + assertTrue("Not starting in AWT", !EventQueue.isDispatchThread()); + // test that checked excs from M.EA throw correct ME + try { + Mutex.EVENT.readAccess(new Mutex.ExceptionAction() { + public Object run() throws Exception { + throw new IOException(); + } + }); + fail(); + } catch (MutexException e) { + assertEquals(IOException.class, e.getException().getClass()); + } + // but that unchecked excs are passed thru + try { + Mutex.EVENT.readAccess(new Mutex.ExceptionAction() { + public Object run() throws Exception { + throw new IllegalArgumentException(); + } + }); + fail(); + } catch (IllegalArgumentException e) { + // OK + } catch (Exception e) { + fail(e.toString()); + } + // similarly for unchecked excs from M.A + try { + Mutex.EVENT.readAccess(new Mutex.Action() { + public Object run() { + throw new IllegalArgumentException(); + } + }); + fail(); + } catch (IllegalArgumentException e) { + // OK + } catch (RuntimeException e) { + fail(e.toString()); + } + // and blocking runnables + try { + Mutex.EVENT.postReadRequest(new Runnable() { + public void run() { + throw new IllegalArgumentException(); + } + }); + fail(); + } catch (IllegalArgumentException e) { + // OK. + } + try { + Mutex.EVENT.postWriteRequest(new Runnable() { + public void run() { + throw new IllegalArgumentException(); + } + }); + fail(); + } catch (IllegalArgumentException e) { + // OK. + } + } + + public void testLockOrdering() throws Exception { + Mutex.Privileged p1 = new Mutex.Privileged(); + Mutex m1 = new Mutex("one", p1, 1); + Mutex.Privileged p2 = new Mutex.Privileged(); + Mutex m2 = new Mutex("two", p2, 2); + Mutex.Privileged p2a = new Mutex.Privileged(); + Mutex m2a = new Mutex("two-a", p2a, 2); + Mutex.Privileged px = new Mutex.Privileged(); + Mutex mx = new Mutex(px); + Mutex.Privileged pxa = new Mutex.Privileged(); + Mutex mxa = new Mutex(pxa); + // test that higher -> lower is OK, and can entered unordered too + m2.enterReadAccess(); + try { + m1.enterReadAccess(); + try { + mx.enterReadAccess(); + mx.exitReadAccess(); + } finally { + m1.exitReadAccess(); + } + } finally { + m2.exitReadAccess(); + } + // check that lower -> higher is blocked + m1.enterReadAccess(); + try { + boolean ok = true; + try { + m2.enterReadAccess(); + ok = false; + m2.exitReadAccess(); + fail(); + } catch (IllegalStateException e) { + assertTrue(ok); + } + mx.enterReadAccess(); + mx.exitReadAccess(); + } finally { + m1.exitReadAccess(); + } + // check that from unordered, can enter anything + mx.enterReadAccess(); + try { + m1.enterReadAccess(); + m1.exitReadAccess(); + m2.enterReadAccess(); + m2.exitReadAccess(); + } finally { + mx.exitReadAccess(); + } + // even higher -> lower -> higher is blocked + m2.enterReadAccess(); + try { + m1.enterReadAccess(); + try { + boolean ok = true; + try { + m2.enterReadAccess(); + ok = false; + m2.exitReadAccess(); + fail(); + } catch (IllegalStateException e) { + assertTrue(ok); + } + } finally { + m1.exitReadAccess(); + } + } finally { + m2.exitReadAccess(); + } + // high -> equiv. is blocked too + m2.enterReadAccess(); + try { + boolean ok = true; + try { + m2a.enterReadAccess(); + ok = false; + m2a.exitReadAccess(); + fail(); + } catch (IllegalStateException e) { + assertTrue(ok); + } + } finally { + m2.exitReadAccess(); + } + // but can reenter yourself + m2.enterReadAccess(); + try { + m2.enterReadAccess(); + m2.exitReadAccess(); + } finally { + m2.exitReadAccess(); + } + // undef level mutexes do not block transitions + mx.enterReadAccess(); + try { + mxa.enterReadAccess(); + mxa.exitReadAccess(); + } finally { + mx.exitReadAccess(); + } + // higher -> lower is OK in W/R, R/W, and W/W combos too + m2.enterWriteAccess(); + try { + m1.enterReadAccess(); + m1.exitReadAccess(); + } finally { + m2.exitWriteAccess(); + } + m2.enterReadAccess(); + try { + m1.enterWriteAccess(); + m1.exitWriteAccess(); + } finally { + m2.exitReadAccess(); + } + m2.enterWriteAccess(); + try { + m1.enterWriteAccess(); + m1.exitWriteAccess(); + } finally { + m2.exitWriteAccess(); + } + } + + public void testEventOrdering() throws Exception { + Mutex.Privileged p0 = new Mutex.Privileged(); + final Mutex m0 = new Mutex("zero", p0, 0); + Mutex.Privileged px = new Mutex.Privileged(); + final Mutex mx = new Mutex(px); + // can go EVENT -> ordered + Mutex.EVENT.postReadRequest(new Runnable() { + public void run() { + m0.enterReadAccess(); + m0.exitReadAccess(); + } + }); + // but not ordered -> EVENT + m0.enterReadAccess(); + try { + try { + Mutex.EVENT.postReadRequest(new Runnable() { + public void run() { + fail(); + } + }); + } catch (IllegalStateException e) { + // OK. + } + } finally { + m0.exitReadAccess(); + } + // however EVENT -> unordered is OK + Mutex.EVENT.postReadRequest(new Runnable() { + public void run() { + mx.enterReadAccess(); + mx.exitReadAccess(); + } + }); + // as is unordered -> EVENT + mx.enterReadAccess(); + try { + final boolean[] b = new boolean[1]; + Mutex.EVENT.postReadRequest(new Runnable() { + public void run() { + b[0] = true; + } + }); + assertTrue(b[0]); + } finally { + mx.exitReadAccess(); + } + } + + public void testMisorderedMutexes() throws Exception { + Mutex.Privileged p1 = new Mutex.Privileged(); + Mutex m1 = new Mutex("one", p1, 1); + Mutex.Privileged p2 = new Mutex.Privileged(); + Mutex m2 = new Mutex("two", p2, 2); + // Cannot exit wrong read access when ordered + m2.enterReadAccess(); + try { + m1.enterReadAccess(); + try { + m2.exitReadAccess(); + fail(); + } catch (IllegalStateException e) { + // OK. + } finally { + m1.exitReadAccess(); + } + } finally { + try { + m2.exitReadAccess(); + } catch (IllegalStateException e) { + fail(); + } + } + // but for compatibility, unordered mutexes can be intermixed + Mutex.Privileged px1 = new Mutex.Privileged(); + Mutex mx1 = new Mutex(px1); + Mutex.Privileged px2 = new Mutex.Privileged(); + Mutex mx2 = new Mutex(px2); + boolean exited2 = false; + mx2.enterReadAccess(); + try { + mx1.enterReadAccess(); + mx2.exitReadAccess(); + exited2 = true; + } finally { + mx1.exitReadAccess(); + if (!exited2) { + // in case test fails, still clean up + mx2.exitReadAccess(); + } + } + } + + public void testNestedEntries() throws Exception { + Mutex.Privileged p = new Mutex.Privileged(); + Mutex m = new Mutex(p); + // can go write -> read + m.enterWriteAccess(); + try { + m.enterReadAccess(); + m.exitReadAccess(); + } finally { + m.exitWriteAccess(); + } + // and write -> write -> read + m.enterWriteAccess(); + try { + m.enterWriteAccess(); + try { + m.enterReadAccess(); + m.exitReadAccess(); + } finally { + m.exitWriteAccess(); + } + } finally { + m.exitWriteAccess(); + } + // and write -> read -> read + m.enterWriteAccess(); + try { + m.enterReadAccess(); + try { + m.enterReadAccess(); + m.exitReadAccess(); + } finally { + m.exitReadAccess(); + } + } finally { + m.exitWriteAccess(); + } + // and even write -> write -> read -> read + m.enterWriteAccess(); + try { + m.enterWriteAccess(); + try { + m.enterReadAccess(); + try { + m.enterReadAccess(); + m.exitReadAccess(); + } finally { + m.exitReadAccess(); + } + } finally { + m.exitWriteAccess(); + } + } finally { + m.exitWriteAccess(); + } + // but read -> write is forbidden + m.enterReadAccess(); + try { + boolean ok = true; + try { + m.enterWriteAccess(); + ok = false; + m.exitWriteAccess(); + } catch (IllegalStateException e) { + assertTrue(ok); + } + } finally { + m.exitReadAccess(); + } + // so is write -> read -> write! + m.enterWriteAccess(); + try { + m.enterReadAccess(); + try { + boolean ok = true; + try { + m.enterWriteAccess(); + ok = false; + m.exitWriteAccess(); + } catch (IllegalStateException e) { + assertTrue(ok); + } + } finally { + m.exitReadAccess(); + } + } finally { + m.exitWriteAccess(); + } + } + }