/*
 *
 * JBoss, the OpenSource J2EE webOS
 *
 * Distributable under LGPL license.
 * See terms of license at gnu.org.
 */

package org.jboss.cache;


import junit.framework.Test;
import junit.framework.TestCase;
import junit.framework.TestSuite;
import org.jboss.cache.lock.*;

/**
 * Tests performance of IdentityLock
 *
 * @author Bela Ban
 * @version $Id: IdentityLockPerfTest.java 201 2005-07-08 05:58:12Z msurtani $
 */
public class IdentityLockPerfTest extends TestCase {
   final int NUM=1000 * 1000;
   final int NUM_THREADS=10;
   long start, stop, total;
   double locks_per_sec;
   IdentityLock lock;
   Thread[] threads;
   Object[] results;


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


   public void testAcquireReadLocks() throws Exception {
      LockStrategyFactory.setIsolationLevel(IsolationLevel.REPEATABLE_READ);
      lock=new IdentityLock(null, null);
      Thread  curr=Thread.currentThread();

      start=System.currentTimeMillis();
      for(int i=0; i < NUM; i++) {
         lock.acquireReadLock(curr, 5000);
      }
      stop=System.currentTimeMillis();
      total=stop-start;
      locks_per_sec=NUM / (total / 1000.0);
      System.out.println("Time to acquire " + NUM + " read locks: " + (total / 1000.0) + " secs" +
                         " (" + locks_per_sec + " locks/sec)");
   }

   public void testAcquireAndReleaseReadLocks() throws Exception {
      LockStrategyFactory.setIsolationLevel(IsolationLevel.REPEATABLE_READ);
      lock=new IdentityLock(null, null);
      Thread  curr=Thread.currentThread();

      start=System.currentTimeMillis();
      for(int i=0; i < NUM; i++) {
         lock.acquireReadLock(curr, 5000);
         lock.release(curr);
      }
      stop=System.currentTimeMillis();
      total=stop-start;
      locks_per_sec=NUM / (total / 1000.0);
      System.out.println("Time to acquire and release " + NUM + " read locks: " + (total / 1000.0) + " secs" +
                         " (" + locks_per_sec + " locks/sec)");
   }

   public void testAcquireAndUpgradeReadLocks() throws Exception {
      LockStrategyFactory.setIsolationLevel(IsolationLevel.REPEATABLE_READ);
      lock=new IdentityLock(null, null);
      Thread  curr=Thread.currentThread();

      start=System.currentTimeMillis();
      for(int i=0; i < NUM; i++) {
         lock.acquireReadLock(curr, 5000);
         lock.acquireWriteLock(curr, 5000);
      }
      stop=System.currentTimeMillis();
      total=stop-start;
      locks_per_sec=NUM / (total / 1000.0);
      System.out.println("Time to acquire and upgrade " + NUM + " read locks: " + (total / 1000.0) + " secs" +
                         " (" + locks_per_sec + " locks/sec)");
   }

   public void testAcquireWriteLocks() throws Exception {
      LockStrategyFactory.setIsolationLevel(IsolationLevel.REPEATABLE_READ);
      lock=new IdentityLock(null, null);
      Thread  curr=Thread.currentThread();

      start=System.currentTimeMillis();
      for(int i=0; i < NUM; i++) {
         lock.acquireWriteLock(curr, 5000);
      }
      stop=System.currentTimeMillis();
      total=stop-start;
      locks_per_sec=NUM / (total / 1000.0);
      System.out.println("Time to acquire " + NUM + " write locks: " + (total / 1000.0) + " secs" +
                         " (" + locks_per_sec + " locks/sec)");
   }

   public void testAcquireAndReleaseWriteLocks() throws Exception {
      LockStrategyFactory.setIsolationLevel(IsolationLevel.REPEATABLE_READ);
      lock=new IdentityLock(null, null);
      Thread  curr=Thread.currentThread();

      start=System.currentTimeMillis();
      for(int i=0; i < NUM; i++) {
         lock.acquireWriteLock(curr, 5000);
         lock.release(curr);
      }
      stop=System.currentTimeMillis();
      total=stop-start;
      locks_per_sec=NUM / (total / 1000.0);
      System.out.println("Time to acquire " + NUM + " write locks: " + (total / 1000.0) + " secs" +
                         " (" + locks_per_sec + " locks/sec)");
   }


   public void testConcurrentReadLockAcquisition() throws Exception {
      LockStrategyFactory.setIsolationLevel(IsolationLevel.REPEATABLE_READ);
      lock=new IdentityLock(null, null);
      threads=new Thread[NUM_THREADS];
      results=new Object[NUM_THREADS];

      for(int i=0; i < threads.length; i++) {
         threads[i]=new Reader(i);
      }
      System.out.println("-- starting all " + NUM_THREADS + " threads");
      start=System.currentTimeMillis();
      for(int i=0; i < threads.length; i++) {
         threads[i].start();
      }

      System.out.println("-- waiting for all threads to terminate");
      for(int i=0; i < threads.length; i++) {
         threads[i].join();
      }

      stop=System.currentTimeMillis();

      for(int i=0; i < results.length; i++) {
         if(results[i] != null && results[i] instanceof Exception)
            fail("result[" + i + "]: " + results[i]);
      }

      total=stop-start;
      locks_per_sec=NUM * NUM_THREADS / (total / 1000.0);
      System.out.println("Time to acquire " + NUM * NUM_THREADS + " read locks: " + (total / 1000.0) + " secs" +
                         " (" + locks_per_sec + " locks/sec)");
   }


   public void testConcurrentWriteLockAcquisition() throws Exception {
      LockStrategyFactory.setIsolationLevel(IsolationLevel.REPEATABLE_READ);
      lock=new IdentityLock(null, null);
      threads=new Thread[NUM_THREADS];
      results=new Object[NUM_THREADS];

      for(int i=0; i < threads.length; i++) {
         threads[i]=new Writer(i);
      }
      System.out.println("-- starting all " + NUM_THREADS + " threads");
      start=System.currentTimeMillis();
      for(int i=0; i < threads.length; i++) {
         threads[i].start();
      }

      System.out.println("-- waiting for all threads to terminate");
      for(int i=0; i < threads.length; i++) {
         threads[i].join();
      }

      stop=System.currentTimeMillis();

      for(int i=0; i < results.length; i++) {
         if(results[i] != null && results[i] instanceof Exception)
            fail("result[" + i + "]: " + results[i]);
      }

      total=stop-start;
      locks_per_sec=NUM * NUM_THREADS / (total / 1000.0);
      System.out.println("Time to acquire " + NUM * NUM_THREADS + " write locks: " + (total / 1000.0) + " secs" +
                         " (" + locks_per_sec + " locks/sec)");
   }



   class Reader extends Thread {
      int num=-1;

      Reader(int num) {
         this.num=num;
      }

      public void run() {
         for(int i=0; i < NUM; i++) {
            try {
               lock.acquireReadLock(this, 5000);
            }
            catch(Exception e) {
               results[i]=e;
               break;
            }
         }
      }
   }

   class Writer extends Thread {
      int num=-1;

      Writer(int num) {
         this.num=num;
      }

      public void run() {
         for(int i=0; i < NUM; i++) {
            try {
               lock.acquireWriteLock(this, 15000);
               lock.release(this);
            }
            catch(Exception e) {
               results[i]=e;
               break;
            }
         }
      }
   }


   public static Test suite() throws Exception {
      return new TestSuite(IdentityLockPerfTest.class);
   }

}
