Quadcap Embeddable Database

com/quadcap/sql/lock/LockManager.java

Go to the documentation of this file.
00001 package com.quadcap.sql.lock; 00002 00003 /* Copyright 1999 - 2003 Quadcap Software. All rights reserved. 00004 * 00005 * This software is distributed under the Quadcap Free Software License. 00006 * This software may be used or modified for any purpose, personal or 00007 * commercial. Open Source redistributions are permitted. Commercial 00008 * redistribution of larger works derived from, or works which bundle 00009 * this software requires a "Commercial Redistribution License"; see 00010 * http://www.quadcap.com/purchase. 00011 * 00012 * Redistributions qualify as "Open Source" under one of the following terms: 00013 * 00014 * Redistributions are made at no charge beyond the reasonable cost of 00015 * materials and delivery. 00016 * 00017 * Redistributions are accompanied by a copy of the Source Code or by an 00018 * irrevocable offer to provide a copy of the Source Code for up to three 00019 * years at the cost of materials and delivery. Such redistributions 00020 * must allow further use, modification, and redistribution of the Source 00021 * Code under substantially the same terms as this license. 00022 * 00023 * Redistributions of source code must retain the copyright notices as they 00024 * appear in each source code file, these license terms, and the 00025 * disclaimer/limitation of liability set forth as paragraph 6 below. 00026 * 00027 * Redistributions in binary form must reproduce this Copyright Notice, 00028 * these license terms, and the disclaimer/limitation of liability set 00029 * forth as paragraph 6 below, in the documentation and/or other materials 00030 * provided with the distribution. 00031 * 00032 * The Software is provided on an "AS IS" basis. No warranty is 00033 * provided that the Software is free of defects, or fit for a 00034 * particular purpose. 00035 * 00036 * Limitation of Liability. Quadcap Software shall not be liable 00037 * for any damages suffered by the Licensee or any third party resulting 00038 * from use of the Software. 00039 */ 00040 00041 import java.util.ArrayList; 00042 import java.util.HashMap; 00043 import java.util.Iterator; 00044 00045 import java.util.Comparator; 00046 00047 import java.sql.SQLException; 00048 00049 import javax.concurrent.Sync; 00050 00051 import com.quadcap.util.Debug; 00052 import com.quadcap.util.Util; 00053 00054 import com.quadcap.util.ConfigNumber; 00055 00056 00057 /** 00058 * Manage a hierarchical set of read/write/intention locks. 00059 * 00060 * @author Stan Bailes 00061 */ 00062 public class LockManager { 00063 //#ifdef TRACE 00064 /*{com.quadcap.qed.Trace-vars.xml-1064} 00065 * <config-var> 00066 * <config-name>qed.trace.locks</config-name> 00067 * <config-dflt>0</config-dflt> 00068 * <config-desc> 00069 * <pre> 00070 * bit 0: verbose lock tracing 00071 * bit 1: lock tracing 00072 * bit 2: really verbose lock tracing 00073 * bit 3: transactions 00074 * bit 4: dump locktable every 30 seconds 00075 * </pre> 00076 * </config-desc> 00077 * </config-var> 00078 */ 00079 protected static final 00080 ConfigNumber trace = ConfigNumber.find("qed.trace.locks", "0"); 00081 //#endif 00082 00083 protected static final 00084 ConfigNumber lockTimeout = ConfigNumber.find("qed.lock.Timeout", "60"); 00085 00086 private long transCount = 1; 00087 00088 final Object locksLock = new Object(); 00089 00090 /** Pool of lock objects */ 00091 LockPool locks = new LockPool(); 00092 00093 /** Pool of 'held' locks */ 00094 HeldLockPool held = new HeldLockPool(); 00095 00096 /** Temp held lock object used for searching */ 00097 HeldLock tmpHeld = null; 00098 00099 /** Pool of transaction objects. */ 00100 TransactionPool transactions =new TransactionPool(); 00101 00102 /** held locks sorted by transaction */ 00103 SortedArray byTrans; 00104 SortedArrayIterator byTransIter; 00105 00106 /** 00107 * Constructor 00108 */ 00109 public LockManager() { 00110 Comparator compare = new Comparator() { 00111 public int compare(Object a, Object b) { 00112 return HeldLock.compare((HeldLock)a, (HeldLock)b); 00113 } 00114 }; 00115 byTrans = new SortedArray(compare); 00116 byTransIter = new SortedArrayIterator(byTrans); 00117 tmpHeld = held.get(null, null, LockMode.IX); 00118 final LockManager lockMgr = this; 00119 00120 //#ifdef DEBUG 00121 if (trace.bit(4)) { 00122 Thread t = new Thread() { 00123 public void run() { 00124 setName("LockManager Debug"); 00125 Debug.println("Lock dump thread running"); 00126 while (lockMgr != null) { 00127 try { 00128 Thread.sleep(10000); 00129 synchronized (lockMgr.locksLock) { 00130 if (lockMgr.byTrans.size() > 0) { 00131 Debug.println("DUMP: " + 00132 lockMgr.toString()); 00133 } 00134 } 00135 } catch (Throwable e) { 00136 Debug.print(e); 00137 } 00138 } 00139 } 00140 }; 00141 t.setDaemon(true); 00142 t.start(); 00143 } 00144 //#endif 00145 } 00146 00147 /** 00148 * Allocate a new Transaction. All lock operations are performed 00149 * on behalf of a Transaction. 00150 */ 00151 public final Transaction getTransaction(long transId) { 00152 synchronized (locksLock) { 00153 Transaction t = (Transaction)transactions.get(transId); 00154 //#ifdef DEBUG 00155 if (trace.bit(3)) { 00156 Debug.println("getTransaction() = " + t); 00157 if (trace.bit(2)) { 00158 Debug.println("this = " + this); 00159 } 00160 } 00161 //#endif 00162 return t; 00163 } 00164 } 00165 00166 /** 00167 * Find an existing transaction. Return null if the specified 00168 * transaction doesn't exist. 00169 */ 00170 public Transaction findTransaction(long transId) { 00171 return transactions.find(transId); 00172 } 00173 00174 /** 00175 * End a transaction, releasing all of its locks 00176 */ 00177 public void releaseTransaction(Transaction t) { 00178 //#ifdef DEBUG 00179 if (trace.bit(3)) { 00180 Debug.println("releaseTransaction(" + t + ")"); 00181 if (trace.bit(2)) { 00182 Debug.println("this = " + this); 00183 } 00184 } 00185 //#endif 00186 synchronized (locksLock) { 00187 try { 00188 // for each lock held by the transaction 00189 Iterator tlocks = locksForTransaction(t); 00190 //#ifdef TRACE 00191 if (trace.bit(1)) { 00192 Debug.println("releaseTransaction(" + t + ") [1]"); 00193 } 00194 //#endif 00195 //Debug.println("releaseTransaction(" + t + "), held locks = " + byTrans); 00196 while (tlocks.hasNext()) { 00197 HeldLock h = (HeldLock)tlocks.next(); 00198 boolean saveMe = false; 00199 if (h.trans.getTransactionId() < t.getTransactionId()) continue; 00200 if (h.trans.getTransactionId() > t.getTransactionId()) break; 00201 //Debug.println(" Releasing: " + h); 00202 Lock lock = h.lock; 00203 int mode = h.mode; 00204 tlocks.remove(); 00205 if (h.mode == LockMode.NL) continue; 00206 //#ifdef TRACE 00207 if (trace.bit(1)) { 00208 Debug.println("[release] lock(" + t + ", " + lock + " in " + 00209 LockMode.toString(mode)); 00210 } 00211 //#endif 00212 lock.decrHeldCount(mode); 00213 Transaction w = lock.headWaitQueue(); 00214 while (w != null && w.getWaitMode() == LockMode.NL) { 00215 lock.popWaitQueue(); 00216 w = lock.headWaitQueue(); 00217 } 00218 if (w != null && !w.equals(t)) { 00219 if (lock.couldLock(w.getWaitMode())) { 00220 lock.popWaitQueue(); 00221 HeldLock wl = w.getWait(); 00222 //#ifdef TRACE 00223 if (trace.bit(1)) { 00224 Debug.println("" + t + " releasing " + lock + 00225 ", waking " + wl); 00226 } 00227 //#endif 00228 00229 wl.mode = wl.waitMode; 00230 acquireLock(wl); 00231 w.clearWait(); 00232 try { 00235 } catch (InterruptedException ex) { 00236 } 00237 } 00238 } 00239 held.release(h); 00240 } 00241 } finally { 00242 transactions.release(t); 00243 } 00244 //#ifdef TRACE 00245 if (trace.bit(1)) { 00246 Debug.println("releaseTransaction(" + t + ") done"); 00247 if (trace.bit(2)) { 00248 Debug.println("this = " + this); 00249 } 00250 } 00251 //#endif 00252 } 00253 } 00254 00255 /** 00256 * Return an iterator over all active transactions 00257 */ 00258 public Iterator transactions() { 00259 return transactions.iterator(); 00260 } 00261 00262 /** 00263 * Return an Iterator over the held locks positioned at the first 00264 * lock owned by transaction 't'. 00265 */ 00266 final Iterator locksForTransaction(Transaction t) { 00267 tmpHeld.trans = t; 00268 tmpHeld.lock = null; 00269 byTransIter.position(tmpHeld); 00270 return byTransIter; 00271 } 00272 00273 final HeldLock getLockForTransaction(Transaction t, Lock lock) { 00274 tmpHeld.trans = t; 00275 tmpHeld.lock = lock; 00276 tmpHeld.mode = LockMode.NL; 00277 byTransIter.position(tmpHeld); 00278 while (byTransIter.hasNext()) { 00279 HeldLock h = (HeldLock)byTransIter.next(); 00280 if (t.getTransactionId() > h.trans.getTransactionId()) { 00281 return null; 00282 } 00283 if (t.getTransactionId() == h.trans.getTransactionId() && 00284 lock.hashCode() == h.lock.hashCode()) { 00285 return h; 00286 } 00287 } 00288 return null; 00289 } 00290 00291 /** 00292 * Instantiate the specified lock on behalf of transaction 't', 00293 * which may or may not already have this lock. 00294 */ 00295 public final Lock getLock(Transaction t, Lock parent, String name, 00296 int mode) 00297 throws SQLException 00298 { 00299 HeldLock h = null; 00300 boolean newLock = false; 00302 //#ifdef TRACE 00303 if (trace.bit(1)) { 00304 Debug.println("---- getLock(" + t + ", " + parent + ", " + 00305 name + ", " + LockMode.toString(mode) + ")"); 00306 } 00307 //#endif 00308 try { 00309 synchronized (locksLock) { 00310 Lock lock = locks.get(parent, name); 00311 checkParentsLockMode(t, lock, mode); 00312 00313 h = getLockForTransaction(t, lock); 00314 if (h != null) { 00315 //Debug.println(" holding: " + h); 00316 // 't' already has 'lock'; check the mode. 00317 if (LockMode.implies(h.mode, mode)) { 00318 //Debug.println(" Already have: " + h); 00319 return h.lock; 00320 } 00321 if (LockMode.implies(mode, h.mode)) { 00322 if (lock.couldPromote(h.mode, mode)) { 00323 promoteLock(h, mode); 00324 //Debug.println(" Promoted: " + h); 00325 return h.lock; 00326 } else { 00327 //Debug.println(" Can't promote: " + h + ": " + lock.dump()); 00328 } 00329 } else { 00330 throw new RuntimeException("Transaction " + t + 00331 " has lock " + lock + 00332 " in mode " + 00333 LockMode.toString(h.mode) + 00334 ", trying to lock now in mode "+ 00335 LockMode.toString(mode)); 00336 } 00337 } else { 00338 newLock = true; 00339 //Debug.println(" Don't have it yet"); 00340 // 't' does not have 'lock' in any way 00341 h = held.get(t, lock, mode); 00342 if (lock.couldLock(mode) && !lock.hasWaiters()) { 00343 acquireLock(h); 00344 //Debug.println(" Got lock: " + h); 00345 return h.lock; 00346 } 00347 } 00348 h.setWaitMode(mode); 00349 try { 00350 s = waitForLock(h); 00351 } catch (InterruptedException ex) { 00352 throw new SQLException("Interrupted trying to get lock: " + 00353 name); 00354 } 00355 00356 } 00357 //#ifdef DEBUG 00358 if (trace.bit(0)) { 00359 Debug.println(" Waiting for: " + h); 00360 if (trace.bit(2)) { 00361 Debug.println(byTrans.toString()); 00362 } 00363 } 00364 //#endif 00365 boolean got = false; 00366 try { 00368 } catch (InterruptedException ex) { } 00369 if (!got) { 00370 Debug.println("Lock timed out: " + name + " after " + 00371 lockTimeout.longValue() + 00372 " seconds"); 00373 Debug.println("LockManager: " + this); 00374 // try { 00375 // got = s.attempt(lockTimeout.longValue() * 1000); 00376 // } catch (InterruptedException ex) {} 00377 if (!got) { 00378 t.setWait(null); 00379 if (newLock) { 00380 held.release(h); 00381 newLock = false; 00382 } 00383 throw new SQLException("Timed out trying to get lock: " + 00384 name + " for [T:" + t + "]"); 00385 } 00386 } 00387 //#ifdef DEBUG 00388 if (trace.bit(0)) { 00389 Debug.println(" Wait done for: " + h); 00390 } 00391 //#endif 00392 return h.lock; 00393 } finally { 00394 //#ifdef TRACE 00395 if (trace.bit(1)) { 00396 Debug.println("getLock(" + name + ") done"); 00397 } 00398 //#endif 00399 } 00400 } 00401 00402 /** 00403 * We're in the critical section still 00404 */ 00406 final Transaction t = h.trans; 00407 //#ifdef PARANOID 00408 //- if (t.getWait() != null) 00409 //- throw new RuntimeException("Transaction already waiting"); 00410 //#endif 00411 h.lock.addWaitQueue(t); 00412 t.setWait(h); 00413 return t.getSync(); 00414 } 00415 00416 /** 00417 * Ensure that the lock's parent(s) are properly locked, to enable 00418 * locking <code>lock</code> in the specified mode. 00419 */ 00420 final void checkParentsLockMode(Transaction t, Lock lock, int mode) { 00421 Lock p = lock.getParent(); 00422 if (p != null) { 00423 HeldLock h = getLockForTransaction(t, p); 00424 if (h == null) { 00425 //#ifdef DEBUG 00426 Debug.println("Held: " + byTrans + ", lock = " + lock); 00427 Debug.println("LockManager: " + this); 00428 //#endif 00429 throw new RuntimeException("no lock on parent " + p); 00430 } 00431 if (!couldLockWithParentMode(h.mode, mode)) { 00432 //#ifdef DEBUG 00433 Debug.println("lock = " + lock + ", p = " + p + ", h = " + h); 00434 Debug.println("LockManager: " + this); 00435 //#endif 00436 throw new RuntimeException("parent not properly locked: " + h); 00437 } 00438 checkParentsLockMode(t, p, mode); 00439 } 00440 } 00441 00442 final boolean couldLockWithParentMode(int pmode, int mode) { 00443 switch (mode) { 00444 case LockMode.S: 00445 case LockMode.IS: 00446 return pmode == LockMode.X || pmode == LockMode.S 00447 || pmode == LockMode.IX || pmode == LockMode.IS; 00448 case LockMode.X: 00449 case LockMode.IX: 00450 case LockMode.SIX: 00451 return pmode == LockMode.X 00452 || pmode == LockMode.IX || pmode == LockMode.SIX; 00453 default: 00454 throw new RuntimeException("bad lock mode: " + 00455 LockMode.toString(mode)); 00456 } 00457 } 00458 00459 void promoteLock(HeldLock h, int mode) { 00460 h.lock.decrHeldCount(h.mode); 00461 h.lock.incrHeldCount(mode); 00462 h.mode = mode; 00463 } 00464 00465 void acquireLock(HeldLock h) { 00466 h.lock.incrHeldCount(h.mode); 00467 byTrans.add(h); 00468 } 00469 00470 //#ifdef DEBUG 00471 public String toString() { 00472 synchronized (locksLock) { 00473 StringBuffer sb = new StringBuffer(); 00474 sb.append("\nTransactions:\n------------\n"); 00475 sb.append(transactions); 00476 sb.append("\nHeld:\n----\n"); 00477 sb.append(byTrans); 00478 sb.append("\nLocks:\n-----\n"); 00479 sb.append(locks); 00480 sb.append("\n"); 00481 return sb.toString(); 00482 } 00483 } 00484 //#endif 00485 } 00486