package org.jboss.cache.lock;

import static org.testng.AssertJUnit.fail;

import java.util.concurrent.locks.ReadWriteLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;

import org.testng.annotations.AfterMethod;
import org.testng.annotations.BeforeMethod;
import org.testng.annotations.Test;

/**
 * Tests the various ReadWriteLock implementations
 * @author Bela Ban
 * @version $Id: ReadWriteLockTest.java 4444 2007-08-28 14:40:21Z jason.greene@jboss.com $
 */
@Test(groups = { "functional" })
public class ReadWriteLockTest
{
   ReadWriteLock lock;
   Exception ex = null;

   @BeforeMethod(alwaysRun = true)
   public void setUp() throws Exception
   {
      ex = null;
   }

   @AfterMethod(alwaysRun = true)
   public void tearDown() throws Exception
   {
      lock = null;
      if (ex != null)
         throw ex;
   }

   public void testMoreWriteReleasesThanAcquisitions() throws InterruptedException
   {
      lock = new ReentrantReadWriteLock();
      lock.writeLock().lock();
      lock.writeLock().unlock();
      try
      {
         lock.writeLock().unlock();
         fail("Should have barfed");
      }
      catch (IllegalMonitorStateException imse)
      {
         // should barf
      }
   }

   public void testMoreReadReleasesThanAcquisitions() throws InterruptedException
   {
      lock = new ReentrantReadWriteLock();
      lock.readLock().lock();
      lock.readLock().unlock();
      try
      {
         lock.readLock().unlock();
         fail("read locks cannot be released more than acquired");
      }
      catch (IllegalMonitorStateException illegalStateEx)
      {

      }
   }

   public void testSimple() throws InterruptedException
   {
      lock = new ReentrantReadWriteLock();
      lock.readLock().lock();
      // upgrades must be manual; involving giving up the RL first.  Sucks.
      // see http://java.sun.com/j2se/1.5.0/docs/api/java/util/concurrent/locks/ReentrantReadWriteLock.html
      lock.readLock().unlock();

      lock.writeLock().lock();
      lock.writeLock().lock();
      lock.readLock().lock();
      lock.readLock().lock();

      // since the thread currently has a WL and we are reentrant, this works without a manual upgrade.
      lock.writeLock().lock();
      lock.writeLock().lock();
   }

   public void testOneWriterMultipleReaders() throws InterruptedException
   {
      lock = new ReentrantReadWriteLock();

      Writer writer = new Writer("writer");
      Reader reader1 = new Reader("reader1");
      Reader reader2 = new Reader("reader2");

      writer.start();
      reader1.start();
      reader2.start();

      writer.join();
      reader1.join();
      reader2.join();
   }

   class Writer extends Thread
   {

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

      public void run()
      {
         try
         {
            log("acquiring WL");
            lock.writeLock().lock();
            log("acquired WL successfully");
            sleep(1000);
         }
         catch (InterruptedException e)
         {
            ex = e;
         }
         finally
         {
            log("releasing WL");
            lock.writeLock().unlock();
         }
      }
   }

   class Reader extends Thread
   {

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

      public void run()
      {
         try
         {
            log("acquiring RL");
            lock.readLock().lock();
            log("acquired RL successfully");
            sleep(500);
         }
         catch (InterruptedException e)
         {
            ex = e;
         }
         finally
         {
            log("releasing RL");
            lock.readLock().unlock();
         }
      }
   }

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

}
