package edu.berkeley.thebes.twopl.server; import com.google.common.collect.Lists; import com.google.common.collect.Maps; import edu.berkeley.thebes.common.interfaces.IThebesClient; import edu.berkeley.thebes.common.log4j.Log4JConfig; import edu.berkeley.thebes.twopl.server.TwoPLLocalLockManager.LockType; import junit.framework.AssertionFailedError; import junit.framework.TestCase; import org.apache.thrift.TException; import org.apache.thrift.transport.TTransportException; import javax.naming.ConfigurationException; import java.io.FileNotFoundException; import java.nio.ByteBuffer; import java.util.List; import java.util.Map; import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicInteger; public class TwoPLLocalLockManagerTest extends TestCase { TwoPLLocalLockManager lockManager; private AtomicInteger owners; private List<AssertionFailedError> errors; static { // Log4JConfig.configureLog4J(); } @Override public void setUp() { lockManager = new TwoPLLocalLockManager(); owners = new AtomicInteger(0); errors = Lists.newCopyOnWriteArrayList(); } public void testBasic() { lockManager.lock(LockType.READ, "abc", 123); assertTrue(lockManager.ownsLock(LockType.READ, "abc", 123)); lockManager.unlock("abc", 123); lockManager.lock(LockType.WRITE, "qed", 123); assertTrue(lockManager.ownsLock(LockType.READ, "qed", 123)); assertTrue(lockManager.ownsLock(LockType.WRITE, "qed", 123)); lockManager.unlock("qed", 123); } public void testMultipleReaders() { final int NUM_THREADS = 10; Thread[] threads = new Thread[NUM_THREADS]; for (int i = 0; i < NUM_THREADS; i ++) { final int index = i; threads[i] = new Thread() { @Override public void run() { try { lockManager.lock(LockType.READ, "abc", index); owners.incrementAndGet(); assertTrue(lockManager.ownsLock(LockType.READ, "abc", index)); assertFalse(lockManager.ownsLock(LockType.WRITE, "abc", index)); try { Thread.sleep(1000); } catch (InterruptedException e) { fail(); } assertEquals(NUM_THREADS, owners.get()); try { Thread.sleep(1000); } catch (InterruptedException e) { fail(); } lockManager.unlock("abc", index); owners.decrementAndGet(); } catch (AssertionFailedError e) { errors.add(e); } } }; threads[i].start(); } for (Thread t : threads) { try { t.join(); } catch (InterruptedException e) { fail(); } } for (AssertionFailedError e : errors) { throw e; } } public void testMultipleWriters() { final int NUM_THREADS = 5; Thread[] threads = new Thread[NUM_THREADS]; for (int i = 0; i < NUM_THREADS; i ++) { final int index = i; threads[i] = new Thread() { @Override public void run() { try { lockManager.lock(LockType.WRITE, "abc", index); owners.incrementAndGet(); assertTrue(lockManager.ownsLock(LockType.WRITE, "abc", index)); assertTrue(lockManager.ownsLock(LockType.READ, "abc", index)); try { Thread.sleep(1000); } catch (InterruptedException e) { fail(); } assertEquals(1, owners.get()); try { Thread.sleep(1000); } catch (InterruptedException e) { fail(); } System.out.println("Unlockly!"); lockManager.unlock("abc", index); owners.decrementAndGet(); } catch (AssertionFailedError e) { errors.add(e); } } }; threads[i].start(); } for (Thread t : threads) { try { t.join(); } catch (InterruptedException e) { fail(); } } for (AssertionFailedError e : errors) { throw e; } } private AtomicBoolean writerRan = new AtomicBoolean(false); public void testWritersSupercedeReaders() { final int NUM_THREADS = 5; Thread[] threads = new Thread[NUM_THREADS]; for (int i = 0; i < NUM_THREADS; i ++) { final int index = i; threads[i] = new Thread() { @Override public void run() { try { lockManager.lock(LockType.READ, "abc", index); owners.incrementAndGet(); assertTrue(lockManager.ownsLock(LockType.READ, "abc", index)); assertFalse(lockManager.ownsLock(LockType.WRITE, "abc", index)); try { Thread.sleep(1000); } catch (InterruptedException e) { fail(); } assertEquals(NUM_THREADS, owners.get()); try { Thread.sleep(2000); } catch (InterruptedException e) { fail(); } lockManager.unlock("abc", index); owners.decrementAndGet(); } catch (AssertionFailedError e) { errors.add(e); } } }; threads[i].start(); } try { Thread.sleep(250); } catch (InterruptedException e) { fail(); } // WRITER THREAD queues for lock. Thread writerThread = new Thread() { @Override public void run() { try { int mySessionId = 100; // Assert the readers own the lock. assertEquals(NUM_THREADS, owners.get()); lockManager.lock(LockType.WRITE, "abc", mySessionId); owners.incrementAndGet(); assertTrue(lockManager.ownsLock(LockType.READ, "abc", mySessionId)); assertTrue(lockManager.ownsLock(LockType.WRITE, "abc", mySessionId)); writerRan.set(true); assertEquals(1, owners.get()); try { Thread.sleep(1000); } catch (InterruptedException e) { fail(); } lockManager.unlock("abc", mySessionId); owners.decrementAndGet(); } catch (AssertionFailedError e) { errors.add(e); } } }; writerThread.start(); try { Thread.sleep(250); } catch (InterruptedException e) { fail(); } // READER THREAD should queue for lock (though currently in READ mode). Thread lateReaderThread = new Thread() { @Override public void run() { try { int mySessionId = 101; // Assert the readers still own the lock. assertEquals(NUM_THREADS, owners.get()); lockManager.lock(LockType.READ, "abc", mySessionId); owners.incrementAndGet(); assertTrue(lockManager.ownsLock(LockType.READ, "abc", mySessionId)); assertFalse(lockManager.ownsLock(LockType.WRITE, "abc", mySessionId)); // Assert we're the only one who actually gets the lock. assertEquals(1, owners.get()); assertTrue(writerRan.get()); try { Thread.sleep(1000); } catch (InterruptedException e) { fail(); } lockManager.unlock("abc", mySessionId); owners.decrementAndGet(); } catch (AssertionFailedError e) { errors.add(e); } } }; lateReaderThread.start(); try { for (Thread t : threads) { t.join(); } writerThread.join(); lateReaderThread.join(); } catch (InterruptedException e) { fail(); } for (AssertionFailedError e : errors) { throw e; } } private AtomicBoolean readersRan = new AtomicBoolean(false); public void testReadersSupercedeWriters() { final int NUM_THREADS = 5; Thread writerThread1 = new Thread() { @Override public void run() { try { int mySessionId = 100; assertEquals(0, owners.get()); lockManager.lock(LockType.WRITE, "abc", mySessionId); owners.incrementAndGet(); assertTrue(lockManager.ownsLock(LockType.READ, "abc", mySessionId)); assertTrue(lockManager.ownsLock(LockType.WRITE, "abc", mySessionId)); assertEquals(1, owners.get()); try { Thread.sleep(1000); } catch (InterruptedException e) { fail(); } owners.decrementAndGet(); lockManager.unlock("abc", mySessionId); } catch (AssertionFailedError e) { errors.add(e); } } }; writerThread1.start(); try { Thread.sleep(250); } catch (InterruptedException e) { fail(); } // READER threads queue for lock. Thread[] threads = new Thread[NUM_THREADS]; for (int i = 0; i < NUM_THREADS; i ++) { final int index = i; threads[i] = new Thread() { @Override public void run() { try { assertEquals(1, owners.get()); lockManager.lock(LockType.READ, "abc", index); owners.incrementAndGet(); assertTrue(lockManager.ownsLock(LockType.READ, "abc", index)); assertFalse(lockManager.ownsLock(LockType.WRITE, "abc", index)); try { Thread.sleep(1000); } catch (InterruptedException e) { fail(); } assertEquals(NUM_THREADS, owners.get()); readersRan.set(true); try { Thread.sleep(2000); } catch (InterruptedException e) { fail(); } owners.decrementAndGet(); lockManager.unlock("abc", index); } catch (AssertionFailedError e) { errors.add(e); } } }; threads[i].start(); } try { Thread.sleep(250); } catch (InterruptedException e) { fail(); } // WRITER thread should queue behind all readers. Thread writerThread2 = new Thread() { @Override public void run() { try { int mySessionId = 101; lockManager.lock(LockType.WRITE, "abc", mySessionId); owners.incrementAndGet(); assertTrue(lockManager.ownsLock(LockType.READ, "abc", mySessionId)); assertTrue(lockManager.ownsLock(LockType.WRITE, "abc", mySessionId)); assertEquals(1, owners.get()); assertTrue(readersRan.get()); try { Thread.sleep(1000); } catch (InterruptedException e) { fail(); } owners.decrementAndGet(); lockManager.unlock("abc", mySessionId); } catch (AssertionFailedError e) { errors.add(e); } } }; writerThread2.start(); try { for (Thread t : threads) { t.join(); } writerThread1.join(); writerThread2.join(); } catch (InterruptedException e) { fail(); } for (AssertionFailedError e : errors) { throw e; } } }