/*
 * Written by Doug Lea with assistance from members of JCP JSR-166
 * Expert Group and released to the public domain, as explained at
 * http://creativecommons.org/licenses/publicdomain
 */
import edu.emory.mathcs.backport.java.util.concurrent.locks.*;

class ReadHoldingWriteLock {

    public static void main(String[] args) throws Exception {
        ReadHoldingWriteLock t = new ReadHoldingWriteLock();
        t.testReadAfterWriteLock();
        t.testReadHoldingWriteLock();
        t.testReadHoldingWriteLock2();
//        t.testReadHoldingWriteLockFair();
//        t.testReadHoldingWriteLockFair2();
    }

    static final long SHORT_DELAY_MS = 50;
    static final long MEDIUM_DELAY_MS = 200;

    void assertTrue(boolean b) {
        if (!b) throw new Error();
    }

    /**
     * Readlocks succeed after a writing thread unlocks
     */
    public void testReadAfterWriteLock() throws Exception {
        final ReentrantReadWriteLock lock = new ReentrantReadWriteLock();
        lock.writeLock().lock();
        Thread t1 = new Thread(new Runnable() {
                public void run() {
                    lock.readLock().lock();
                    lock.readLock().unlock();
                }
            });
        Thread t2 = new Thread(new Runnable() {
                public void run() {
                    lock.readLock().lock();
                    lock.readLock().unlock();
                }
            });

        t1.start();
        t2.start();
        Thread.sleep(SHORT_DELAY_MS);
        lock.writeLock().unlock();
        t1.join(MEDIUM_DELAY_MS);
        t2.join(MEDIUM_DELAY_MS);
        assertTrue(!t1.isAlive());
        assertTrue(!t2.isAlive());

    }

    /**
     * Read trylock succeeds if write locked by current thread
     */
    public void testReadHoldingWriteLock()throws Exception {
        final ReentrantReadWriteLock lock = new ReentrantReadWriteLock();
        lock.writeLock().lock();
        assertTrue(lock.readLock().tryLock());
        lock.readLock().unlock();
        lock.writeLock().unlock();
    }

    /**
     * Read lock succeeds if write locked by current thread even if
     * other threads are waiting
     */
    public void testReadHoldingWriteLock2() throws Exception{
        final ReentrantReadWriteLock lock = new ReentrantReadWriteLock();
        lock.writeLock().lock();
        Thread t1 = new Thread(new Runnable() {
                public void run() {
                    lock.readLock().lock();
                    lock.readLock().unlock();
                }
            });
        Thread t2 = new Thread(new Runnable() {
                public void run() {
                    lock.readLock().lock();
                    lock.readLock().unlock();
                }
            });

        t1.start();
        t2.start();
        lock.readLock().lock();
        lock.readLock().unlock();
        Thread.sleep(SHORT_DELAY_MS);
        lock.readLock().lock();
        lock.readLock().unlock();
        lock.writeLock().unlock();
        t1.join(MEDIUM_DELAY_MS);
        t2.join(MEDIUM_DELAY_MS);
        assertTrue(!t1.isAlive());
        assertTrue(!t2.isAlive());
    }

//    /**
//     * Fair Read trylock succeeds if write locked by current thread
//     */
//    public void testReadHoldingWriteLockFair() throws Exception{
//	final ReentrantReadWriteLock lock = new ReentrantReadWriteLock(true);
//	lock.writeLock().lock();
//        assertTrue(lock.readLock().tryLock());
//        lock.readLock().unlock();
//        lock.writeLock().unlock();
//    }
//
//    /**
//     * Fair Read lock succeeds if write locked by current thread even if
//     * other threads are waiting
//     */
//    public void testReadHoldingWriteLockFair2() throws Exception {
//	final ReentrantReadWriteLock lock = new ReentrantReadWriteLock(true);
//	lock.writeLock().lock();
//	Thread t1 = new Thread(new Runnable() {
//                public void run() {
//                    lock.readLock().lock();
//                    lock.readLock().unlock();
//		}
//	    });
//	Thread t2 = new Thread(new Runnable() {
//                public void run() {
//                    lock.readLock().lock();
//                    lock.readLock().unlock();
//		}
//	    });
//
//        t1.start();
//        t2.start();
//        lock.readLock().lock();
//        lock.readLock().unlock();
//        Thread.sleep(SHORT_DELAY_MS);
//        lock.readLock().lock();
//        lock.readLock().unlock();
//        lock.writeLock().unlock();
//        t1.join(MEDIUM_DELAY_MS);
//        t2.join(MEDIUM_DELAY_MS);
//        assertTrue(!t1.isAlive());
//        assertTrue(!t2.isAlive());
//
//
//    }
}
