package org.jboss.cache.lock;

import EDU.oswego.cs.dl.util.concurrent.Sync;
import junit.framework.Test;
import junit.framework.TestCase;
import junit.framework.TestSuite;
import org.jboss.cache.misc.TestingUtil;

/**
 * Tests ReentrantWriterPreferenceReadWriteLock
 * @author Bela Ban
 * @version $Id: ReentrantWriterPreferenceReadWriteLockTest.java 1838 2006-05-05 12:06:59Z msurtani $
 */
public class ReentrantWriterPreferenceReadWriteLockTest extends TestCase {
   // ReentrantWriterPreferenceReadWriteLock lock;
   SimpleReadWriteLock lock;
   Sync rl, wl;
   Exception thread_ex=null;

   protected void setUp() throws Exception {
      super.setUp();
      // lock=new ReentrantWriterPreferenceReadWriteLock();
      lock=new SimpleReadWriteLock();
      rl=lock.readLock();
      wl=lock.writeLock();
      thread_ex=null;
   }

   protected void tearDown() throws Exception {
      super.tearDown();
      lock=null;
      if(thread_ex != null)
         throw thread_ex;
   }

   public void testMultipleReadLockAcquisitions() throws InterruptedException {
      rl.acquire();
      rl.acquire();
   }

   public void testInterruptedLockAcquisition() {
      Thread.currentThread().interrupt();
      try {
         rl.acquire();
         fail("thread should be in interrupted status");
      }
      catch(InterruptedException e) {
      }
      finally {
         try {
            rl.release();
            fail("release() should throw an IllegalStateException");
         }
         catch(IllegalStateException illegalStateEx) {
            assertTrue(true);
         }
      }
   }

   public void testMultipleWriteLockAcquisitions() throws InterruptedException {
      wl.acquire();
      wl.acquire();
   }

   public void testMultipleReadLockReleases() throws InterruptedException {
      rl.acquire();
      rl.release();
      try {
         rl.release();
         fail("we should not get here, cannot acquire RL once but release twice");
      }
      catch(IllegalStateException illegalState) {
         // this is as expected
      }
   }


   public void acquireReadAndWriteLocks() throws InterruptedException {
      rl.acquire();
      rl.acquire();
      boolean fl=wl.attempt(4000);
      assertTrue(fl);
   }


   public void acquireWriteThenReadLock() throws InterruptedException {
      wl.acquire();
      rl.acquire();
      wl.release();
      rl.release();
   }

   public void testMultipleWriteLockReleases() throws InterruptedException {
      wl.acquire();
      wl.release();
      wl.release();
   }

   public void testAcquireWriteLockAfterReadLock() throws InterruptedException {
      rl.acquire();
      wl.acquire();
   }


   public void testAcquiringReadLockedLockWithRead() throws InterruptedException {
      new Thread() {
         public void run() {
            try {rl.acquire();}
            catch(InterruptedException e) {}
         }
      }.start();

      TestingUtil.sleepThread(500);

      // now we have a RL by another thread

      boolean flag=rl.attempt(3000);
      assertTrue(flag);
      flag=wl.attempt(3000);
      assertFalse(flag);
   }

   public void testAcquiringReadLockedLock() throws InterruptedException {
      new Thread() {
         public void run() {
            try {rl.acquire();}
            catch(InterruptedException e) {}
         }
      }.start();

      TestingUtil.sleepThread(500);

      // now we have a RL by another thread
      boolean flag=wl.attempt(3000);
      assertFalse(flag);
   }

   public void testWriteThenReadByDifferentTx() throws InterruptedException {
      Writer writer=new Writer("Writer");
      Reader reader=new Reader("Reader");
      writer.start();
      TestingUtil.sleepThread(500);
      reader.start();
      TestingUtil.sleepThread(1000);

      synchronized(writer) {
         log("terminating Writer");
         writer.notify();
      }
      TestingUtil.sleepThread(500);
      synchronized(reader) {
         reader.notify();
      }
      writer.join();
      reader.join();
   }

   public void testReadThenWriteByDifferentTx() throws InterruptedException {
      Writer writer=new Writer("Writer");
      Reader reader=new Reader("Reader");

      reader.start();
      TestingUtil.sleepThread(500);
      writer.start();
      TestingUtil.sleepThread(1000);

      synchronized(reader) {
         log("terminating Reader");
         reader.notify();
      }

      TestingUtil.sleepThread(500);
      synchronized(writer) {
         writer.notify();
      }
      writer.join();
      reader.join();
   }



   private static void log(String msg) {
      System.out.println(System.currentTimeMillis() + "  " + Thread.currentThread() +
                         " [" + Thread.currentThread().getName() + "]: " + msg);
   }

   class Reader extends Thread {

      public Reader(String name) {
         super(name);
      }

      public void run() {
         try {
            log("acquiring RL");
            rl.acquire();
            log("acquired RL");
            synchronized(this) {
               this.wait();
            }
            log("releasing RL");
            rl.release();
            log("released RL");
         }
         catch(InterruptedException e) {
            ;
         }
      }
   }


   class Writer extends Thread {

      public Writer(String name) {
         super(name);
      }

      public void run() {
         try {
            log("acquiring WL");
            wl.acquire();
            log("acquired WL");
            synchronized(this) {
               this.wait();
            }
            log("releasing WL");
            wl.release();
            log("released WL");
         }
         catch(InterruptedException e) {
            ;
         }
      }
   }


   class Upgrader extends Thread {
      boolean upgradeSuccessful=false;
      public Upgrader(String name) {
         super(name);
      }

      public boolean wasUpgradeSuccessful() {
         return upgradeSuccessful;
      }


      public void run() {
         try {
            log("acquiring RL");
            rl.acquire();
            log("acquired RL");
            synchronized(this) {
               this.wait();
            }
            log("attempting to acquire WL");
            // rl.release();
            wl.acquire();
            upgradeSuccessful=true;
            log("acquired WL");
            log("releasing WL/RL");
            wl.release();
            log("released WL/RL");
         }
         catch(InterruptedException e) {
            ;
         }
      }
   }



   public static Test suite() {
      return new TestSuite(ReentrantWriterPreferenceReadWriteLockTest.class);
   }

   public static void main(String[] args) {
      junit.textui.TestRunner.run(suite());
   }

}
