Main Page | Packages | Class Hierarchy | Alphabetical List | Class List | File List | Class Members | Related Pages

TransactionExclusiveLock.java

00001 /**
00002  * C-JDBC: Clustered JDBC.
00003  * Copyright (C) 2002-2004 French National Institute For Research In Computer
00004  * Science And Control (INRIA).
00005  * Contact: c-jdbc@objectweb.org
00006  * 
00007  * This library is free software; you can redistribute it and/or modify it
00008  * under the terms of the GNU Lesser General Public License as published by the
00009  * Free Software Foundation; either version 2.1 of the License, or any later
00010  * version.
00011  * 
00012  * This library is distributed in the hope that it will be useful, but WITHOUT
00013  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
00014  * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
00015  * for more details.
00016  * 
00017  * You should have received a copy of the GNU Lesser General Public License
00018  * along with this library; if not, write to the Free Software Foundation,
00019  * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
00020  *
00021  * Initial developer(s): Emmanuel Cecchet.
00022  * Contributor(s): _________________________.
00023  */
00024 
00025 package org.objectweb.cjdbc.controller.scheduler.schema;
00026 
00027 import java.util.ArrayList;
00028 
00029 import org.objectweb.cjdbc.common.sql.AbstractRequest;
00030 
00031 /**
00032  * A <code>TransactionExclusiveLock</code> is an exclusive lock that let the
00033  * owner of the lock acquire several times the lock (but it needs to be released
00034  * only once). Acquire supports timeout and graceful withdrawal of timed out
00035  * requests.
00036  * 
00037  * @author <a href="mailto:Emmanuel.Cecchet@inria.fr">Emmanuel Cecchet </a>
00038  * @version 1.0
00039  */
00040 public class TransactionExclusiveLock
00041 {
00042   private boolean   isLocked    = false;
00043 
00044   /** Transaction id of the lock holder. */
00045   private long      locker;
00046 
00047   /** <code>ArrayList</code> of <code>WaitingListElement</code>. */
00048   private ArrayList waitingList = new ArrayList();
00049 
00050   /**
00051    * The element stored in the waiting list is the waiting thread and the
00052    * transaction id of the request waiting.
00053    */
00054   private class WaitingListElement
00055   {
00056     /** Waiting thread */
00057     Thread thread;
00058 
00059     /** Transaction id of the request waiting. */
00060     long   transactionId;
00061 
00062     /**
00063      * Creates a new <code>WaitingListElement</code> instance.
00064      * 
00065      * @param thread the waiting thread.
00066      * @param transactionId the transaction id of the request waiting.
00067      */
00068     WaitingListElement(Thread thread, long transactionId)
00069     {
00070       this.thread = thread;
00071       this.transactionId = transactionId;
00072     }
00073 
00074     /**
00075      * Returns the transaction id of the request waiting.
00076      * 
00077      * @return an <code>int</code> value
00078      */
00079     public long getTransactionId()
00080     {
00081       return transactionId;
00082     }
00083 
00084     /**
00085      * Returns the waiting thread.
00086      * 
00087      * @return a <code>Thread</code> value
00088      */
00089     public Thread getThread()
00090     {
00091       return thread;
00092     }
00093   }
00094 
00095   /**
00096    * Acquires an exclusive lock on this table. If the lock is already held by
00097    * the same transaction as the given request, this method is non-blocking else
00098    * the caller is blocked until the transaction holding the lock releases it at
00099    * commit/rollback time.
00100    * 
00101    * @param request request asking for the lock (timeout field is used and
00102    *          updated upon waiting)
00103    * @return boolean true is the lock has been successfully acquired, false on
00104    *         timeout or error
00105    * @see #release()
00106    */
00107   public boolean acquire(AbstractRequest request)
00108   {
00109     long tid = request.getTransactionId();
00110 
00111     synchronized (Thread.currentThread())
00112     {
00113       WaitingListElement wle = null;
00114       synchronized (this)
00115       {
00116         if (!isLocked)
00117         { // Lock is free, take it
00118           locker = tid;
00119           isLocked = true;
00120           return true;
00121         }
00122         else
00123         {
00124           if ((!request.isAutoCommit()) && (locker == tid))
00125             return true; // We already have the lock
00126           else
00127           { // Wait for the lock
00128             wle = new WaitingListElement(Thread.currentThread(), tid);
00129             waitingList.add(wle);
00130           }
00131         }
00132       }
00133       // At this point, we have to wait for the lock.
00134       try
00135       {
00136         int timeout = request.getTimeout();
00137         if (timeout == 0)
00138         {
00139           Thread.currentThread().wait(); // No timeout
00140           // Note: isLocked and locker are already set.
00141           return true;
00142         }
00143         else
00144         { // Wait with timeout
00145           long start = System.currentTimeMillis();
00146           // Convert seconds to milliseconds for wait call
00147           long lTimeout = timeout * 1000;
00148           Thread.currentThread().wait(lTimeout);
00149           long end = System.currentTimeMillis();
00150           int remaining = (int) (lTimeout - (end - start));
00151           if (remaining > 0)
00152           { // Ok
00153             request.setTimeout(remaining);
00154             // Note: isLocked and locker are already set.
00155             return true;
00156           }
00157           else
00158           { // Too late, remove ourselves from the waiting list
00159             synchronized (this)
00160             {
00161               int idx = waitingList.indexOf(wle);
00162               if (idx == -1)
00163                 // We got the lock before being able to acquire the lock on
00164                 // this. Give the lock to the next one.
00165                 release();
00166               else
00167                 waitingList.remove(idx);
00168             }
00169             return false;
00170           }
00171         }
00172       }
00173       catch (InterruptedException ie)
00174       {
00175         synchronized (this)
00176         { // Something wrong happened, remove ourselves from the waiting list
00177           waitingList.remove(Thread.currentThread());
00178         }
00179         return false;
00180       }
00181     }
00182   }
00183 
00184   /**
00185    * Releases the lock on this table.
00186    * 
00187    * @see #acquire(AbstractRequest)
00188    */
00189   public synchronized void release()
00190   {
00191     while (!waitingList.isEmpty())
00192     {
00193       // Wake up the first waiting thread and update locker transaction id
00194       WaitingListElement e = (WaitingListElement) waitingList.remove(0);
00195       Thread thread = e.getThread();
00196       locker = e.getTransactionId();
00197       synchronized (thread)
00198       {
00199         thread.notify();
00200         // isLocked remains true
00201         return;
00202       }
00203     }
00204     isLocked = false;
00205   }
00206 
00207   /**
00208    * Returns <code>true</code> if the lock is owned by someone.
00209    * 
00210    * @return <code>boolean</code> value
00211    */
00212   public boolean isLocked()
00213   {
00214     return isLocked;
00215   }
00216 
00217   /**
00218    * Returns the transaction id of the lock owner. The return value is undefined
00219    * if the lock is not owned (usually it is the last owner).
00220    * 
00221    * @return int the transaction id.
00222    */
00223   public long getLocker()
00224   {
00225     return locker;
00226   }
00227 
00228   /**
00229    * Returns the waitingList.
00230    * 
00231    * @return an <code>ArrayList</code> of <code>WaitingListElement</code>
00232    */
00233   public ArrayList getWaitingList()
00234   {
00235     return waitingList;
00236   }
00237 
00238   /**
00239    * Returns <code>true</code> if the given transaction id is contained in
00240    * this lock waiting queue.
00241    * 
00242    * @param transactionId a transaction id
00243    * @return a <code>boolean</code> value
00244    */
00245   public synchronized boolean isWaiting(long transactionId)
00246   {
00247     WaitingListElement e;
00248     int size = waitingList.size();
00249     for (int i = 0; i < size; i++)
00250     {
00251       e = (WaitingListElement) waitingList.get(i);
00252       if (e.getTransactionId() == transactionId)
00253         return true;
00254     }
00255     return false;
00256   }
00257 }

Generated on Mon Apr 11 22:01:35 2005 for C-JDBC by  doxygen 1.3.9.1