KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > com > sleepycat > je > txn > Locker


1 /*-
2  * See the file LICENSE for redistribution information.
3  *
4  * Copyright (c) 2002,2006 Oracle. All rights reserved.
5  *
6  * $Id: Locker.java,v 1.100 2006/11/17 23:47:28 mark Exp $
7  */

8
9 package com.sleepycat.je.txn;
10
11 import java.util.HashMap JavaDoc;
12 import java.util.HashSet JavaDoc;
13 import java.util.Hashtable JavaDoc;
14 import java.util.Iterator JavaDoc;
15 import java.util.Map JavaDoc;
16 import java.util.Set JavaDoc;
17
18 import com.sleepycat.je.Database;
19 import com.sleepycat.je.DatabaseException;
20 import com.sleepycat.je.DeadlockException;
21 import com.sleepycat.je.DbInternal;
22 import com.sleepycat.je.LockNotGrantedException;
23 import com.sleepycat.je.LockStats;
24 import com.sleepycat.je.OperationStatus;
25 import com.sleepycat.je.dbi.CursorImpl;
26 import com.sleepycat.je.dbi.DatabaseImpl;
27 import com.sleepycat.je.dbi.EnvironmentImpl;
28 import com.sleepycat.je.tree.BIN;
29 import com.sleepycat.je.tree.BINReference;
30 import com.sleepycat.je.tree.Key;
31
32 /**
33  * Locker instances are JE's route to locking and transactional support. This
34  * class is the abstract base class for BasicLocker, ThreadLocker, Txn and
35  * AutoTxn. Locker instances are in fact only a transaction shell to get to
36  * the lock manager, and don't guarantee transactional semantics. Txn and
37  * AutoTxn instances are both truely transactional, but have different ending
38  * behaviors.
39  */

40 public abstract class Locker {
41     private static final String JavaDoc DEBUG_NAME = Locker.class.getName();
42     protected EnvironmentImpl envImpl;
43     protected LockManager lockManager;
44
45     protected long id; // transaction id
46
protected boolean readUncommittedDefault; // read-uncommitted is default
47

48     /* Timeouts */
49     protected boolean defaultNoWait; // true for non-blocking
50
protected long lockTimeOutMillis; // timeout period for lock, in ms
51
private long txnTimeOutMillis; // timeout period for txns, in ms
52
private long txnStartMillis; // for txn timeout determination
53

54     private Lock waitingFor; // The lock that this txn is
55
// waiting for.
56

57     /*
58      * DeleteInfo refers to BINReferences that should be sent to the
59      * INCompressor for asynchronous compressing after the transaction ends.
60      */

61     protected Map JavaDoc deleteInfo;
62
63     /*
64      * To support handle lock transfers, each txn keeps maps handle locks to
65      * database handles. This is maintained as a map where the key is the
66      * handle lock id and the value is a set of database handles that
67      * correspond to that handle lock. This is a 1 - many relationship because
68      * a single handle lock can cover multiple database handles opened by the
69      * same transaction.
70      */

71     protected Map JavaDoc handleLockToHandleMap; // 1-many, used for commits
72
protected Map JavaDoc handleToHandleLockMap; // 1-1, used for aborts
73

74     /**
75      * The thread that created this locker. Used for debugging, and by the
76      * ThreadLocker subclass. Note that thread may be null if the Locker is
77      * instantiated by reading the log.
78      */

79     protected Thread JavaDoc thread;
80
81     /**
82      * Create a locker id. This constructor is called very often, so it should
83      * be as streamlined as possible.
84      *
85      * @param lockManager lock manager for this environment
86      * @param readUncommittedDefault if true, this transaction does
87      * read-uncommitted by default
88      * @param noWait if true, non-blocking lock requests are used.
89      */

90     public Locker(EnvironmentImpl envImpl,
91                   boolean readUncommittedDefault,
92                   boolean noWait)
93         throws DatabaseException {
94
95         TxnManager txnManager = envImpl.getTxnManager();
96         this.id = generateId(txnManager);
97         this.envImpl = envImpl;
98         lockManager = txnManager.getLockManager();
99         this.readUncommittedDefault = readUncommittedDefault;
100     this.waitingFor = null;
101
102         /* get the default lock timeout. */
103         defaultNoWait = noWait;
104         lockTimeOutMillis = envImpl.getLockTimeout();
105
106         /*
107          * Check the default txn timeout. If non-zero, remember the txn start
108          * time.
109          */

110         txnTimeOutMillis = envImpl.getTxnTimeout();
111
112         if (txnTimeOutMillis != 0) {
113             txnStartMillis = System.currentTimeMillis();
114         } else {
115             txnStartMillis = 0;
116         }
117
118         /* Save the thread used to create the locker. */
119         thread = Thread.currentThread();
120
121         /*
122          * Do lazy initialization of deleteInfo and handle lock maps, to
123          * conserve memory.
124          */

125     }
126
127     /**
128      * For reading from the log.
129      */

130     Locker() {
131     }
132
133     /**
134      * A Locker has to generate its next id. Some subtypes, like BasicLocker,
135      * have a single id for all instances because they are never used for
136      * recovery. Other subtypes ask the txn manager for an id.
137      */

138     protected abstract long generateId(TxnManager txnManager);
139
140     /**
141      * @return the transaction's id.
142      */

143     public long getId() {
144         return id;
145     }
146
147     /**
148      * @return the default no-wait (non-blocking) setting.
149      */

150     public boolean getDefaultNoWait() {
151         return defaultNoWait;
152     }
153
154     /**
155      * Get the lock timeout period for this transaction, in milliseconds
156      */

157     public synchronized long getLockTimeout() {
158         return lockTimeOutMillis;
159     }
160
161     /**
162      * Set the lock timeout period for any locks in this transaction,
163      * in milliseconds.
164      */

165     public synchronized void setLockTimeout(long timeOutMillis) {
166         lockTimeOutMillis = timeOutMillis;
167     }
168
169     /**
170      * Set the timeout period for this transaction, in milliseconds.
171      */

172     public synchronized void setTxnTimeout(long timeOutMillis) {
173         txnTimeOutMillis = timeOutMillis;
174         txnStartMillis = System.currentTimeMillis();
175     }
176
177     /**
178      * @return true if transaction was created with read-uncommitted as a
179      * default.
180      */

181     public boolean isReadUncommittedDefault() {
182         return readUncommittedDefault;
183     }
184
185     Lock getWaitingFor() {
186     return waitingFor;
187     }
188
189     void setWaitingFor(Lock lock) {
190     waitingFor = lock;
191     }
192
193     /**
194      * Set the state of a transaction to ONLY_ABORTABLE.
195      */

196     void setOnlyAbortable() {
197     /* no-op unless Txn. */
198     }
199
200     protected abstract void checkState(boolean ignoreCalledByAbort)
201         throws DatabaseException;
202
203     /*
204      * Obtain and release locks.
205      */

206
207     /**
208      * Abstract method to a blocking or non-blocking lock of the given type on
209      * the given nodeId. Unlike the lock() method, this method does not throw
210      * LockNotGrantedException and can therefore be used by nonBlockingLock to
211      * probe for a lock without the overhead of an exception stack trace.
212      *
213      * @param nodeId is the node to lock.
214      *
215      * @param lockType is the type of lock to request.
216      *
217      * @param noWait is true to override the defaultNoWait setting. If true,
218      * or if defaultNoWait is true, throws LockNotGrantedException if the lock
219      * cannot be granted without waiting.
220      *
221      * @param database is the database containing nodeId.
222      *
223      * @throws DeadlockException if acquiring a blocking lock would result in a
224      * deadlock.
225      */

226     abstract LockResult lockInternal(long nodeId,
227                                      LockType lockType,
228                                      boolean noWait,
229                                      DatabaseImpl database)
230         throws DeadlockException, DatabaseException;
231
232     /**
233      * Request a blocking or non-blocking lock of the given type on the given
234      * nodeId.
235      *
236      * @param nodeId is the node to lock.
237      *
238      * @param lockType is the type of lock to request.
239      *
240      * @param noWait is true to override the defaultNoWait setting. If true,
241      * or if defaultNoWait is true, throws LockNotGrantedException if the lock
242      * cannot be granted without waiting.
243      *
244      * @param database is the database containing nodeId.
245      *
246      * @throws LockNotGrantedException if a non-blocking lock was denied.
247      *
248      * @throws DeadlockException if acquiring a blocking lock would result in a
249      * deadlock.
250      */

251     public LockResult lock(long nodeId,
252                            LockType lockType,
253                            boolean noWait,
254                            DatabaseImpl database)
255         throws LockNotGrantedException, DeadlockException, DatabaseException {
256
257         LockResult result = lockInternal(nodeId, lockType, noWait, database);
258
259         if (result.getLockGrant() == LockGrantType.DENIED) {
260             /* DENIED can only be returned for a non-blocking lock. */
261             throw new LockNotGrantedException("Non-blocking lock was denied.");
262         } else {
263             return result;
264         }
265     }
266
267     /**
268      * Request a non-blocking lock of the given type on the given nodeId.
269      *
270      * <p>Unlike lock(), this method returns LockGrantType.DENIED if the lock
271      * is denied rather than throwing LockNotGrantedException. This method
272      * should therefore not be used as the final lock for a user operation,
273      * since in that case LockNotGrantedException should be thrown for a denied
274      * lock. It is normally used only to probe for a lock, and other recourse
275      * is taken if the lock is denied.</p>
276      *
277      * @param nodeId is the node to lock.
278      *
279      * @param lockType is the type of lock to request.
280      *
281      * @param database is the database containing nodeId.
282      */

283     public LockResult nonBlockingLock(long nodeId,
284                                       LockType lockType,
285                                       DatabaseImpl database)
286         throws DatabaseException {
287
288         return lockInternal(nodeId, lockType, true, database);
289     }
290
291     /**
292      * Release the lock on this LN and remove from the transaction's owning
293      * set.
294      */

295     public void releaseLock(long nodeId)
296         throws DatabaseException {
297
298         /*
299          * If successful, the lock manager will call back to the transaction
300          * and remove the lock from the lock collection. Done this way because
301          * we can't get a handle on the lock without creating another object
302          * XXX: bite the bullet, new a holder object, pass it back?
303          */

304         lockManager.release(nodeId, this);
305     }
306
307     /**
308      * Revert this lock from a write lock to a read lock.
309      */

310     public void demoteLock(long nodeId)
311         throws DatabaseException {
312
313         /*
314          * If successful, the lock manager will call back to the transaction
315          * and adjust the location of the lock in the lock collection.
316          */

317         lockManager.demote(nodeId, this);
318     }
319
320     /**
321      * Returns whether this locker is transactional.
322      */

323     public abstract boolean isTransactional();
324
325     /**
326      * Returns whether the isolation level of this locker is serializable.
327      */

328     public abstract boolean isSerializableIsolation();
329
330     /**
331      * Returns whether the isolation level of this locker is read-committed.
332      */

333     public abstract boolean isReadCommittedIsolation();
334
335     /**
336      * Returns the underlying Txn if the locker is transactional, or null if
337      * the locker is non-transactional. For a Txn-based locker, this method
338      * returns 'this'. For a BuddyLocker, this method may returns the buddy.
339      */

340     public abstract Txn getTxnLocker();
341
342     /**
343      * Creates a fresh non-transactional locker, while retaining any
344      * transactional locks held by this locker. This method is called when the
345      * cursor for this locker is cloned.
346      *
347      * <p>In general, transactional lockers return 'this' when this method is
348      * called, while non-transactional lockers return a new instance.</p>
349      */

350     public abstract Locker newNonTxnLocker()
351         throws DatabaseException;
352
353     /**
354      * Releases any non-transactional locks held by this locker. This method
355      * is called when the cursor moves to a new position or is closed.
356      *
357      * <p>In general, transactional lockers do nothing when this method is
358      * called, while non-transactional lockers release all locks as if
359      * operationEnd were called.</p>
360      */

361     public abstract void releaseNonTxnLocks()
362         throws DatabaseException;
363
364     /**
365      * Returns whether this locker can share locks with the given locker.
366      *
367      * <p>All lockers share locks with a BuddyLocker whose buddy is this
368      * locker. To support BuddyLocker when overriding this method, always
369      * return true if this implementation (super.sharesLocksWith(...)) returns
370      * true.</p>
371      */

372     public boolean sharesLocksWith(Locker other) {
373     if (other instanceof BuddyLocker) {
374             BuddyLocker buddy = (BuddyLocker) other;
375             return buddy.getBuddy() == this;
376     } else {
377         return false;
378     }
379     }
380
381     /**
382      * The equivalent of calling operationEnd(true).
383      */

384     public abstract void operationEnd()
385         throws DatabaseException;
386
387     /**
388      * Different types of transactions do different things when the operation
389      * ends. Txns do nothing, AutoTxns commit or abort, and BasicLockers and
390      * ThreadLockers just release locks.
391      *
392      * @param operationOK is whether the operation succeeded, since
393      * that may impact ending behavior. (i.e for AutoTxn)
394      */

395     public abstract void operationEnd(boolean operationOK)
396         throws DatabaseException;
397
398     /**
399      * We're at the end of an operation. Move this handle lock to the
400      * appropriate owner.
401      */

402     public abstract void setHandleLockOwner(boolean operationOK,
403                                             Database dbHandle,
404                                             boolean dbIsClosing)
405         throws DatabaseException;
406
407     /**
408      * A SUCCESS status equals operationOk.
409      */

410     public void operationEnd(OperationStatus status)
411         throws DatabaseException {
412
413         operationEnd(status == OperationStatus.SUCCESS);
414     }
415
416     /**
417      * Tell this transaction about a cursor.
418      */

419     public abstract void registerCursor(CursorImpl cursor)
420         throws DatabaseException;
421
422     /**
423      * Remove a cursor from this txn.
424      */

425     public abstract void unRegisterCursor(CursorImpl cursor)
426         throws DatabaseException;
427
428     /*
429      * Transactional support
430      */

431
432     /**
433      * @return the abort LSN for this node.
434      */

435     public abstract long getAbortLsn(long nodeId)
436         throws DatabaseException;
437
438     /**
439      * @return the WriteLockInfo for this node.
440      */

441     public abstract WriteLockInfo getWriteLockInfo(long nodeId)
442     throws DatabaseException;
443
444     /**
445      * Database operations like remove and truncate leave behind
446      * residual DatabaseImpls that must be purged at transaction
447      * commit or abort.
448      */

449     public abstract void markDeleteAtTxnEnd(DatabaseImpl db,
450                                             boolean deleteAtCommit)
451         throws DatabaseException;
452
453     /**
454      * Add delete information, to be added to the inCompressor queue
455      * when the transaction ends.
456      */

457     public void addDeleteInfo(BIN bin, Key deletedKey)
458         throws DatabaseException {
459
460         synchronized (this) {
461             /* Maintain only one binRef per node. */
462             if (deleteInfo == null) {
463                 deleteInfo = new HashMap JavaDoc();
464             }
465             Long JavaDoc nodeId = new Long JavaDoc(bin.getNodeId());
466             BINReference binRef = (BINReference) deleteInfo.get(nodeId);
467             if (binRef == null) {
468                 binRef = bin.createReference();
469                 deleteInfo.put(nodeId, binRef);
470             }
471             binRef.addDeletedKey(deletedKey);
472         }
473     }
474     
475     /*
476      * Manage locks owned by this transaction. Note that transactions that will
477      * be multithreaded must override these methods and provide synchronized
478      * implementations.
479      */

480
481     /**
482      * Add a lock to set owned by this transaction.
483      */

484     abstract void addLock(Long JavaDoc nodeId,
485                           Lock lock,
486                           LockType type,
487                           LockGrantType grantStatus)
488         throws DatabaseException;
489
490     /**
491      * @return true if this transaction created this node,
492      * for a operation with transactional semantics.
493      */

494     public abstract boolean createdNode(long nodeId)
495         throws DatabaseException;
496
497     /**
498      * Remove the lock from the set owned by this transaction. If specified to
499      * LockManager.release, the lock manager will call this when its releasing
500      * a lock.
501      */

502     abstract void removeLock(long nodeId, Lock lock)
503         throws DatabaseException;
504
505     /**
506      * A lock is being demoted. Move it from the write collection into the read
507      * collection.
508      */

509     abstract void moveWriteToReadLock(long nodeId, Lock lock);
510
511     /**
512      * Get lock count, for per transaction lock stats, for internal debugging.
513      */

514     public abstract LockStats collectStats(LockStats stats)
515         throws DatabaseException;
516
517     /*
518      * Check txn timeout, if set. Called by the lock manager when blocking on a
519      * lock.
520      */

521     boolean isTimedOut()
522         throws DatabaseException {
523
524         if (txnStartMillis != 0) {
525             long diff = System.currentTimeMillis() - txnStartMillis;
526             if (diff > txnTimeOutMillis) {
527                 return true;
528             }
529         }
530         return false;
531     }
532
533     /* public for jca/ra/JELocalTransaction. */
534     public long getTxnTimeOut() {
535         return txnTimeOutMillis;
536     }
537
538     long getTxnStartMillis() {
539         return txnStartMillis;
540     }
541
542     /**
543      * Remove this Database from the protected Database handle set
544      */

545     void unregisterHandle(Database dbHandle) {
546
547         /*
548          * handleToHandleLockMap may be null if the db handle was never really
549          * added. This might be the case because of an unregisterHandle that
550          * comes from a finally clause, where the db handle was never
551          * successfully opened.
552          */

553         if (handleToHandleLockMap != null) {
554             handleToHandleLockMap.remove(dbHandle);
555         }
556     }
557
558     /**
559      * Remember how handle locks and handles match up.
560      */

561     public void addToHandleMaps(Long JavaDoc handleLockId,
562                 Database databaseHandle) {
563         // TODO: mutex after measurement
564
Set JavaDoc dbHandleSet = null;
565         if (handleLockToHandleMap == null) {
566
567             /*
568          * We do lazy initialization of the maps, since they're used
569              * infrequently.
570              */

571             handleLockToHandleMap = new Hashtable JavaDoc();
572             handleToHandleLockMap = new Hashtable JavaDoc();
573         } else {
574             dbHandleSet = (Set JavaDoc) handleLockToHandleMap.get(handleLockId);
575         }
576
577         if (dbHandleSet == null) {
578             dbHandleSet = new HashSet JavaDoc();
579             handleLockToHandleMap.put(handleLockId, dbHandleSet);
580         }
581
582         /* Map handle lockIds -> 1 or more database handles. */
583         dbHandleSet.add(databaseHandle);
584         /* Map database handles -> handle lock id */
585         handleToHandleLockMap.put(databaseHandle, handleLockId);
586     }
587
588     /**
589      * @return true if this txn is willing to give up the handle lock to
590      * another txn before this txn ends.
591      */

592     public boolean isHandleLockTransferrable() {
593         return true;
594     }
595
596     /**
597      * The currentTxn passes responsiblity for this db handle lock to a txn
598      * owned by the Database object.
599      */

600     void transferHandleLockToHandle(Database dbHandle)
601         throws DatabaseException {
602
603         /*
604          * Transfer responsiblity for this db lock from this txn to a new
605          * protector.
606          */

607         Locker holderTxn = new BasicLocker(envImpl);
608         transferHandleLock(dbHandle, holderTxn, true );
609     }
610
611     /**
612      *
613      */

614     public void transferHandleLock(Database dbHandle,
615                                    Locker destLocker,
616                                    boolean demoteToRead)
617         throws DatabaseException {
618
619         /*
620          * Transfer responsiblity for dbHandle's handle lock from this txn to
621          * destLocker. If the dbHandle's databaseImpl is null, this handle
622          * wasn't opened successfully.
623          */

624         if (DbInternal.dbGetDatabaseImpl(dbHandle) != null) {
625             Long JavaDoc handleLockId = (Long JavaDoc) handleToHandleLockMap.get(dbHandle);
626             if (handleLockId != null) {
627                 /* We have a handle lock for this db. */
628                 long nodeId = handleLockId.longValue();
629
630                 /* Move this lock to the destination txn. */
631                 lockManager.transfer(nodeId, this, destLocker, demoteToRead);
632
633                 /*
634                  * Make the destination txn remember that it now owns this
635                  * handle lock.
636                  */

637                 destLocker.addToHandleMaps(handleLockId, dbHandle);
638
639                 /* Take this out of the handle lock map. */
640                 Set JavaDoc dbHandleSet = (Set JavaDoc)
641             handleLockToHandleMap.get(handleLockId);
642                 Iterator JavaDoc iter = dbHandleSet.iterator();
643                 while (iter.hasNext()) {
644                     if (((Database) iter.next()) == dbHandle) {
645                         iter.remove();
646                         break;
647                     }
648                 }
649                 if (dbHandleSet.size() == 0) {
650                     handleLockToHandleMap.remove(handleLockId);
651                 }
652                 
653                 /*
654                  * This Database must remember what txn owns it's handle lock.
655                  */

656                 DbInternal.dbSetHandleLocker(dbHandle, destLocker);
657             }
658         }
659     }
660     
661     /*
662      * Helpers
663      */

664     public String JavaDoc toString() {
665         String JavaDoc className = getClass().getName();
666         className = className.substring(className.lastIndexOf('.') + 1);
667
668         return Long.toString(id) + "_" +
669                ((thread == null) ? "" : thread.getName()) + "_" +
670                className;
671     }
672
673     /**
674      * Dump lock table, for debugging
675      */

676     public void dumpLockTable()
677         throws DatabaseException {
678
679         lockManager.dump();
680     }
681 }
682
Popular Tags