You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@jackrabbit.apache.org by ma...@apache.org on 2009/01/26 15:06:34 UTC

svn commit: r737695 - /jackrabbit/trunk/jackrabbit-core/src/test/java/org/apache/jackrabbit/core/state/AbstractISMLockingTest.java

Author: martijnh
Date: Mon Jan 26 14:06:33 2009
New Revision: 737695

URL: http://svn.apache.org/viewvc?rev=737695&view=rev
Log:
JCR-1948 Let the AbstractISMLockingTest tests fail properly

Applied proposed patch.

Modified:
    jackrabbit/trunk/jackrabbit-core/src/test/java/org/apache/jackrabbit/core/state/AbstractISMLockingTest.java

Modified: jackrabbit/trunk/jackrabbit-core/src/test/java/org/apache/jackrabbit/core/state/AbstractISMLockingTest.java
URL: http://svn.apache.org/viewvc/jackrabbit/trunk/jackrabbit-core/src/test/java/org/apache/jackrabbit/core/state/AbstractISMLockingTest.java?rev=737695&r1=737694&r2=737695&view=diff
==============================================================================
--- jackrabbit/trunk/jackrabbit-core/src/test/java/org/apache/jackrabbit/core/state/AbstractISMLockingTest.java (original)
+++ jackrabbit/trunk/jackrabbit-core/src/test/java/org/apache/jackrabbit/core/state/AbstractISMLockingTest.java Mon Jan 26 14:06:33 2009
@@ -19,6 +19,8 @@
 import junit.framework.TestCase;
 import org.apache.jackrabbit.core.NodeId;
 import org.apache.jackrabbit.core.ItemId;
+import org.apache.jackrabbit.core.state.ISMLocking.ReadLock;
+import org.apache.jackrabbit.core.state.ISMLocking.WriteLock;
 import org.apache.jackrabbit.uuid.UUID;
 import org.apache.jackrabbit.spi.commons.name.NameConstants;
 
@@ -27,11 +29,13 @@
 import java.util.Iterator;
 
 /**
- * <code>AbstractISMLockingTest</code> contains test cases for the ISMLocking
- * requirements.
+ * <code>AbstractISMLockingTest</code> contains test cases for the ISMLocking requirements.
  */
 public abstract class AbstractISMLockingTest extends TestCase {
 
+    /**
+     * The {@link ISMLocking} instance under test.
+     */
     protected ISMLocking locking;
 
     /**
@@ -71,190 +75,175 @@
     }
 
     /**
-     * Checks the following requirement:
-     * <p/>
-     * <i>While a read lock is held for a given item with id <code>I</code> an
-     * implementation must ensure that no write lock is issued for a change log
-     * that contains a reference to an item with id <code>I</code>. </i>
+     * @return an {@link ISMLocking} instance which is exercised by the tests
+     */
+    public abstract ISMLocking createISMLocking();
+
+    /**
+     * Checks the following requirement: <p/> <i>While a read lock is held for a given item with id
+     * <code>I</code> an implementation must ensure that no write lock is issued for a change log that
+     * contains a reference to an item with id <code>I</code>. </i>
+     * 
+     * @throws InterruptedException on interruption; this will err the test
      */
     public void testReadBlocksWrite() throws InterruptedException {
-        ISMLocking.ReadLock rLock = locking.acquireReadLock(state.getId());
-        try {
-            for (Iterator it = logs.iterator(); it.hasNext(); ) {
-                final ChangeLog log = (ChangeLog) it.next();
-                Thread t = new Thread(new Runnable() {
-                    public void run() {
-                        try {
-                            checkBlocking(log);
-                        } catch (InterruptedException e) {
-                            fail(e.toString());
-                        }
-                    }
-                });
-                t.start();
-                t.join();
-            }
-        } finally {
-            rLock.release();
+        ReadLock rLock = locking.acquireReadLock(state.getId());
+        for (Iterator it = logs.iterator(); it.hasNext();) {
+            ChangeLog changeLog = (ChangeLog) it.next();
+            verifyBlocked(startWriterThread(locking, changeLog));
         }
+        rLock.release();
     }
 
     /**
-     * Checks the following requirement:
-     * <p/>
-     * <i>While a write lock is held for a given change log <code>C</code> an
-     * implementation must ensure that no read lock is issued for an item that
-     * is contained in <code>C</code>, unless the current thread is the owner of
-     * the write lock!</i>
+     * Checks the following requirement: <p/> <i>While a write lock is held for a given change log
+     * <code>C</code> an implementation must ensure that no read lock is issued for an item that is contained
+     * in <code>C</code>, unless the current thread is the owner of the write lock!</i> <p/> The "unless"
+     * clause is tested by {@link #testWriteBlocksRead_notIfSameThread()} test.
+     * 
+     * @throws InterruptedException on interruption; this will err the test
      */
     public void testWriteBlocksRead() throws InterruptedException {
-        for (Iterator it = logs.iterator(); it.hasNext(); ) {
-            ISMLocking.WriteLock wLock
-                    = locking.acquireWriteLock((ChangeLog) it.next());
-            try {
-                checkNonBlocking(state.getId());
-                Thread t = new Thread(new Runnable() {
-                    public void run() {
-                        try {
-                            checkBlocking(state.getId());
-                        } catch (InterruptedException e) {
-                            fail(e.toString());
-                        }
+        for (Iterator it = logs.iterator(); it.hasNext();) {
+            ChangeLog changeLog = (ChangeLog) it.next();
+            WriteLock wLock = locking.acquireWriteLock(changeLog);
+            verifyBlocked(startReaderThread(locking, state.getId()));
+            wLock.release();
+        }
+    }
+
+    public void testWriteBlocksRead_notIfSameThread() throws InterruptedException {
+        for (Iterator it = logs.iterator(); it.hasNext();) {
+            final ChangeLog changeLog = (ChangeLog) it.next();
+            Thread t = new Thread(new Runnable() {
+
+                public void run() {
+                    try {
+                        WriteLock wLock = locking.acquireWriteLock(changeLog);
+                        locking.acquireReadLock(state.getId()).release();
+                        wLock.release();
+                    } catch (InterruptedException e) {
                     }
-                });
-                t.start();
-                t.join();
-            } finally {
-                wLock.release();
-            }
+                }
+            });
+            t.start();
+            verifyNotBlocked(t);
         }
     }
 
     /**
-     * Checks the following requirement:
-     * <p/>
-     * While a write lock is held for a given change log <code>C</code> an
-     * implementation must ensure that no write lock is issued for a change log
-     * <code>C'</code> that intersects with <code>C</code>. That is both change
-     * logs contain a reference to the same item. Please note that an
-     * implementation is free to block requests entirely for additional write
-     * lock while a write lock is active. It is not a requirement to support
-     * concurrent write locks.
+     * Checks the following requirement: <p/> While a write lock is held for a given change log <code>C</code>
+     * an implementation must ensure that no write lock is issued for a change log <code>C'</code> that intersects
+     * with <code>C</code>. That is both change logs contain a reference to the same item. Please note that an
+     * implementation is free to block requests entirely for additional write lock while a write lock is
+     * active. It is not a requirement to support concurrent write locks.
+     * 
+     * @throws InterruptedException on interruption; this will err the test
      */
     public void testIntersectingWrites() throws InterruptedException {
         ChangeLog cl = new ChangeLog();
         cl.added(state);
-        ISMLocking.WriteLock wLock = locking.acquireWriteLock(cl);
-        try {
-            for (Iterator it = logs.iterator(); it.hasNext(); ) {
-                final ChangeLog log = (ChangeLog) it.next();
-                Thread t = new Thread(new Runnable() {
-                    public void run() {
-                        try {
-                            checkBlocking(log);
-                        } catch (InterruptedException e) {
-                            fail(e.toString());
-                        }
-                    }
-                });
-                t.start();
-                t.join();
-            }
-        } finally {
-            wLock.release();
+        WriteLock wLock = locking.acquireWriteLock(cl);
+        for (Iterator it = logs.iterator(); it.hasNext();) {
+            ChangeLog changeLog = (ChangeLog) it.next();
+            verifyBlocked(startWriterThread(locking, changeLog));
         }
+        wLock.release();
     }
 
     /**
      * Checks if a downgraded write lock allows other threads to read again.
+     * 
+     * @throws InterruptedException on interruption; this will err the test
      */
     public void testDowngrade() throws InterruptedException {
-        for (Iterator it = logs.iterator(); it.hasNext(); ) {
-            ISMLocking.ReadLock rLock = locking.acquireWriteLock(
-                    (ChangeLog) it.next()).downgrade();
-            try {
-                Thread t = new Thread(new Runnable() {
-                    public void run() {
-                        try {
-                            checkNonBlocking(state.getId());
-                        } catch (InterruptedException e) {
-                            fail(e.toString());
-                        }
-                    }
-                });
-                t.start();
-                t.join();
-            } finally {
-                rLock.release();
-            }
-        }
-    }
-
-    //------------------------------< utilities >-------------------------------
-
-    protected void checkBlocking(ChangeLog log)
-            throws InterruptedException {
-        final Thread t = Thread.currentThread();
-        TimeBomb tb = new TimeBomb(100) {
-            public void explode() {
-                t.interrupt();
-            }
-        };
-        tb.arm();
-        try {
-            locking.acquireWriteLock(log).release();
-        } catch (InterruptedException e) {
-            // success
-            return;
+        for (Iterator it = logs.iterator(); it.hasNext();) {
+            ChangeLog changeLog = (ChangeLog) it.next();
+            WriteLock wLock = locking.acquireWriteLock(changeLog);
+            verifyBlocked(startReaderThread(locking, state.getId()));
+            ReadLock rLock = wLock.downgrade();
+            verifyNotBlocked(startReaderThread(locking, state.getId()));
+            rLock.release();
         }
-        tb.disarm();
-        // make sure interrupted status is cleared
-        // bomb may blow off right before we disarm it
-        Thread.interrupted();
-        fail("acquireWriteLock must block");
     }
 
-    protected void checkBlocking(ItemId id)
-            throws InterruptedException {
-        final Thread t = Thread.currentThread();
-        TimeBomb tb = new TimeBomb(100) {
-            public void explode() {
-                t.interrupt();
-            }
-        };
-        tb.arm();
-        try {
-            locking.acquireReadLock(id).release();
-        } catch (InterruptedException e) {
-            // success
-            return;
-        }
-        tb.disarm();
-        // make sure interrupted status is cleared
-        // bomb may blow off right before we disarm it
-        Thread.interrupted();
-        fail("acquireReadLock must block");
-    }
+    // ------------------------------< utilities >-------------------------------
 
-    protected void checkNonBlocking(ItemId id)
-            throws InterruptedException {
-        final Thread t = Thread.currentThread();
-        TimeBomb tb = new TimeBomb(100) {
-            public void explode() {
-                t.interrupt();
-            }
-        };
-        tb.arm();
-        try {
-            locking.acquireReadLock(id).release();
-        } catch (InterruptedException e) {
-            fail("acquireReadLock must not block");
-        }
-        tb.disarm();
-        // make sure interrupted status is cleared
-        // bomb may blow off right before we disarm it
-        Thread.interrupted();
+    /**
+     * Creates and starts a thread that acquires and releases the write lock of the given
+     * <code>ISMLocking</code> for the given changelog. The thread's interrupted status is set if it was
+     * interrupted during the acquire-release sequence and could therefore not finish it.
+     * 
+     * @param lock the <code>ISMLocking</code> to use
+     * @param changeLog the <code>ChangeLog</code> to use
+     * @return a thread that has been started
+     */
+    protected final Thread startWriterThread(final ISMLocking lock, final ChangeLog changeLog) {
+        Thread t = new Thread(new Runnable() {
+
+            public void run() {
+                try {
+                    lock.acquireWriteLock(changeLog).release();
+                } catch (InterruptedException e) {
+                    Thread.currentThread().interrupt();
+                }
+            }
+        });
+        t.start();
+        return t;
+    }
+
+    /**
+     * Creates and starts an thread that acquires and releases the read lock of the given
+     * <code>ISMLocking</code> for the given id. The thread's interrupted status is set if it was interrupted
+     * during the acquire-release sequence and could therefore not finish it.
+     * 
+     * @param lock the <code>ISMLocking</code> to use
+     * @param id the id to use
+     * @return a thread that has been started
+     */
+    protected final Thread startReaderThread(final ISMLocking lock, final ItemId id) {
+        Thread t = new Thread(new Runnable() {
+
+            public void run() {
+                try {
+                    lock.acquireReadLock(id).release();
+                } catch (InterruptedException e) {
+                    Thread.currentThread().interrupt();
+                }
+            }
+        });
+        t.start();
+        return t;
+    }
+
+    /**
+     * Verifies that the given thread is blocked. Then it interrupts the thread and waits a certain amount of
+     * time for it to complete. (If it doesn't complete within that time then the test that calls this method
+     * fails).
+     * 
+     * @param other a started thread
+     * @throws InterruptedException on interruption
+     */
+    protected final void verifyBlocked(final Thread other) throws InterruptedException {
+        Thread.sleep(100);
+        assertTrue(other.isAlive());
+        other.interrupt();
+        other.join(100);
+        assertFalse(other.isAlive());
+    }
+
+    /**
+     * Verifies that the given thread is not blocked and runs to completion within a certain amount of time
+     * and without interruption. (If it doesn't complete within that time without interruption then the test
+     * that calls this method fails).
+     * 
+     * @param other a started thread
+     * @throws InterruptedException on interruption
+     */
+    protected final void verifyNotBlocked(final Thread other) throws InterruptedException {
+        other.join(1000);
+        assertFalse(other.isInterrupted());
+        assertFalse(other.isAlive());
     }
-
-    public abstract ISMLocking createISMLocking();
 }