1 /*-
2  * See the file LICENSE for redistribution information.
3  *
4  * Copyright (c) 2002, 2014 Oracle and/or its affiliates.  All rights reserved.
5  *
6  */
7 
8 package com.sleepycat.je.txn;
9 
10 import java.util.HashMap;
11 import java.util.Map;
12 
13 import com.sleepycat.je.Database;
14 import com.sleepycat.je.DatabaseException;
15 import com.sleepycat.je.EnvironmentFailureException;
16 import com.sleepycat.je.LockConflictException;
17 import com.sleepycat.je.LockNotAvailableException;
18 import com.sleepycat.je.OperationFailureException;
19 import com.sleepycat.je.OperationStatus;
20 import com.sleepycat.je.Transaction;
21 import com.sleepycat.je.dbi.CursorImpl;
22 import com.sleepycat.je.dbi.DatabaseImpl;
23 import com.sleepycat.je.dbi.EnvironmentImpl;
24 import com.sleepycat.je.tree.BIN;
25 import com.sleepycat.je.tree.BINReference;
26 import com.sleepycat.je.utilint.DbLsn;
27 import com.sleepycat.je.utilint.StatGroup;
28 
29 /**
30  * Locker instances are JE's route to locking and transactional support.  This
31  * class is the abstract base class for BasicLocker, ThreadLocker, Txn,
32  * MasterTxn and ReadonlyTxn.  Locker instances are in fact only a transaction
33  * shell to get to the lock manager, and don't guarantee transactional
34  * semantics.
35  *
36  * Txn (includes Txns marked autoTxn) MasterTxn and ReadonlyTxn instances are
37  * truly transactional. They have potentially different transaction begin and
38  * end behaviors.
39  */
40 public abstract class Locker {
41 
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 final boolean defaultNoWait; // true for non-blocking
50     private 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<Long,BINReference> deleteInfo;
62 
63     /**
64      * The thread that created this locker.  Used for debugging, and by the
65      * ThreadLocker subclass. Note that thread may be null if the Locker is
66      * instantiated by reading the log.
67      */
68     protected Thread thread;
69 
70     /**
71      * Set to false when close() is called.  After that point no other locker
72      * operations should occur.
73      */
74     private boolean isOpen = true;
75 
76     /**
77      * True if my locks can be preempted/stolen.
78      */
79     private boolean preemptable = true;
80 
81     /**
82      * Non-null if a lock has been stolen from this locker by the HA replayer.
83      */
84     private RuntimeException preemptedCause;
85 
86     /**
87      * Non-null if this locker is replacing another locker that is in the
88      * process of closing because a cursor is being moved.
89      */
90     private Locker closingLocker;
91 
92     /**
93      * Create a locker id. This constructor is called very often, so it should
94      * be as streamlined as possible. It should never be called directly,
95      * because the mandatedId mechanism only works if the generateId() method
96      * is overridden to use the mandatedId value.
97      *
98      * @param readUncommittedDefault if true, this transaction does
99      * read-uncommitted by default
100      * @param noWait if true, non-blocking lock requests are used.
101      */
Locker(EnvironmentImpl envImpl, boolean readUncommittedDefault, boolean noWait, long mandatedId)102     protected Locker(EnvironmentImpl envImpl,
103                      boolean readUncommittedDefault,
104                      boolean noWait,
105                      long mandatedId) {
106 
107         TxnManager txnManager = envImpl.getTxnManager();
108         this.lockManager = txnManager.getLockManager();
109         this.id = generateId(txnManager, mandatedId);
110         this.envImpl = envImpl;
111         this.readUncommittedDefault = readUncommittedDefault;
112         this.waitingFor = null;
113 
114         /* get the default lock timeout. */
115         defaultNoWait = noWait;
116         lockTimeoutMillis = getInitialLockTimeout();
117 
118         /*
119          * Check the default txn timeout. If non-zero, remember the txn start
120          * time.
121          */
122         txnTimeoutMillis = envImpl.getTxnTimeout();
123 
124         if (txnTimeoutMillis != 0) {
125             txnStartMillis = System.currentTimeMillis();
126         } else {
127             txnStartMillis = 0;
128         }
129 
130         /* Save the thread used to create the locker. */
131         thread = Thread.currentThread();
132 
133         /* Do lazy initialization of deleteInfo, to conserve memory. */
134     }
135 
136     /**
137      * For reading from the log.
138      */
Locker()139     Locker() {
140         defaultNoWait = false;
141     }
142 
getInitialLockTimeout()143     protected long getInitialLockTimeout() {
144         return envImpl.getLockTimeout();
145     }
146 
getEnvironment()147     public EnvironmentImpl getEnvironment() {
148         return envImpl;
149     }
150 
151     /**
152      * A Locker has to generate its next id. Some subtypes, like BasicLocker,
153      * have a single id for all instances because they are never used for
154      * recovery. Other subtypes ask the txn manager for an id or use a
155      * specific, mandated id.
156      */
generateId(TxnManager txnManager, long mandatedId)157     protected abstract long generateId(TxnManager txnManager, long mandatedId);
158 
159     /**
160      * @return the transaction's id.
161      */
getId()162     public long getId() {
163         return id;
164     }
165 
166     /**
167      * @return the default no-wait (non-blocking) setting.
168      */
getDefaultNoWait()169     public boolean getDefaultNoWait() {
170         return defaultNoWait;
171     }
172 
173     /**
174      * Get the lock timeout period for this locker, in milliseconds
175      *
176      * WARNING: Be sure to always access the timeout with this accessor, since
177      * it is overridden in BuddyLocker.
178      */
getLockTimeout()179     public synchronized long getLockTimeout() {
180         return lockTimeoutMillis;
181     }
182 
183     /**
184      * Set the lock timeout period for any locks in this transaction,
185      * in milliseconds.
186      *
187      * @param timeout The timeout value for the transaction lifetime, in
188      * milliseconds. A value of 0 disables timeouts for the transaction.
189      *
190      * @throws IllegalArgumentException via Transaction.setLockTimeout
191      */
setLockTimeout(long timeout)192     public synchronized void setLockTimeout(long timeout) {
193 
194         if (timeout < 0) {
195             throw new IllegalArgumentException
196                 ("the timeout value cannot be negative");
197         } else if (timeout > Math.pow(2, 32)) {
198             throw new IllegalArgumentException
199                 ("the timeout value cannot be greater than 2^32");
200         }
201 
202         lockTimeoutMillis = timeout;
203     }
204 
205     /**
206      * Set the timeout period for this transaction, in milliseconds.
207      *
208      * @param timeout The timeout value for the transaction lifetime, in
209      * microseconds. A value of 0 disables timeouts for the transaction.
210      *
211      * @throws IllegalArgumentException via Transaction.setLockTimeout
212      */
setTxnTimeout(long timeout)213     public synchronized void setTxnTimeout(long timeout) {
214 
215         if (timeout < 0) {
216             throw new IllegalArgumentException
217                 ("the timeout value cannot be negative");
218         } else if (timeout > Math.pow(2, 32)) {
219             throw new IllegalArgumentException
220                 ("the timeout value cannot be greater than 2^32");
221         }
222 
223         txnTimeoutMillis = timeout;
224         if (txnTimeoutMillis != 0) {
225             txnStartMillis = System.currentTimeMillis();
226         } else {
227             txnStartMillis = 0;
228         }
229     }
230 
231     /**
232      * @return true if transaction was created with read-uncommitted as a
233      * default.
234      */
isReadUncommittedDefault()235     public boolean isReadUncommittedDefault() {
236         return readUncommittedDefault;
237     }
238 
getWaitingFor()239     Lock getWaitingFor() {
240         return waitingFor;
241     }
242 
setWaitingFor(Lock lock)243     void setWaitingFor(Lock lock) {
244         waitingFor = lock;
245     }
246 
247     /**
248      * Set the state of a transaction to abort-only.  Should ONLY be called
249      * by OperationFailureException.
250      */
setOnlyAbortable(OperationFailureException cause)251     public void setOnlyAbortable(OperationFailureException cause) {
252         /* no-op unless Txn. */
253     }
254 
255     /**
256      * Set the state of a transaction's IMPORTUNATE bit.
257      */
setImportunate(boolean importunate)258     public void setImportunate(boolean importunate) {
259         /* no-op unless Txn. */
260     }
261 
262     /**
263      * Get the state of a transaction's IMPORTUNATE bit.
264      */
getImportunate()265     public boolean getImportunate() {
266         return false;
267     }
268 
269     /**
270      * Allows/disallows my locks from being stolen/preemted.
271      */
setPreemptable(boolean preemptable)272     public void setPreemptable(boolean preemptable) {
273         this.preemptable = preemptable;
274     }
275 
276     /**
277      * Returns whether my locks can be stolen/preemted.
278      */
getPreemptable()279     public boolean getPreemptable() {
280         return preemptable;
281     }
282 
283     /**
284      * Called when a lock is stolen from this locker by the HA replayer.
285      */
setPreempted()286     public void setPreempted() {
287 
288         /*
289          * Record the stack trace when a lock is stolen.  This will provide
290          * more "cause" information when it is wrapped in a
291          * LockPreemptedException that is thrown later -- see checkPreempted.
292          */
293         preemptedCause = new RuntimeException
294             ("Lock was preempted by the replication replayer");
295     }
296 
297     /**
298      * Called when obtaining a lock to cause a LockPreemptedException to be
299      * thrown if a lock was preempted earlier.
300      *
301      * This operation is split into two methods, checkPreempted and
302      * throwIfPreempted, so that Txn.checkPreempted can call throwIfPreempted
303      * for all its BuddyLockers without causing an infinite recursion.  This
304      * method is overridden by BuddyLocker to forward the call to its parent
305      * buddy (the Txn), and by Txn to check all its child buddies.
306      *
307      * @param allowPreemptedLocker is a locker that is being closed as the
308      * result of a cursor move operation.  If the operation is successful then
309      * allowPreemptedLocker will be closed, and the fact that a lock has been
310      * stolen from allowPreemptedLocker can be ignored.
311      */
checkPreempted(final Locker allowPreemptedLocker)312     public void checkPreempted(final Locker allowPreemptedLocker)
313         throws OperationFailureException {
314 
315         throwIfPreempted(allowPreemptedLocker);
316     }
317 
318     /**
319      * Called by checkPreempted to cause a LockPreemptedException to be thrown
320      * if a lock was preempted earlier.  Creating the LockPreemptedException
321      * sets the txn to abort-only.
322      *
323      * @see #checkPreempted
324      */
throwIfPreempted(final Locker allowPreemptedLocker)325     final void throwIfPreempted(final Locker allowPreemptedLocker)
326         throws OperationFailureException {
327 
328         if (this != allowPreemptedLocker &&
329             preemptedCause != null) {
330             throw envImpl.createLockPreemptedException(this, preemptedCause);
331         }
332     }
333 
334     /** For unit testing. */
isPreempted()335     final boolean isPreempted() {
336         return (preemptedCause != null);
337     }
338 
339     /**
340      * This method is called to set the closingLocker when a cursor has been
341      * duplicated prior to being moved.  The new locker is informed of the old
342      * locker, so that a preempted lock taken by the old locker can be ignored.
343      * When the operation is complete, this method is called to clear the
344      * closingLocker so that a reference to the old closed locker is not held
345      * by this object.  [#16513]
346      *
347      * @param closingLocker the old locker that will be closed if the new
348      * cursor (using this locker) is moved successfully.
349      */
setClosingLocker(final Locker closingLocker)350     public void setClosingLocker(final Locker closingLocker) {
351         this.closingLocker = closingLocker;
352     }
353 
354     /**
355      * See ThreadLocker.allowMultithreadedAccess.
356      */
setAllowMultithreadedAccess(boolean allow)357     boolean setAllowMultithreadedAccess(boolean allow) {
358         /* Do nothing by default. Is overridden by ThreadLocker. */
359         return false;
360     }
361 
checkState(boolean ignoreCalledByAbort)362     protected abstract void checkState(boolean ignoreCalledByAbort)
363         throws DatabaseException;
364 
365     /**
366      * Overridden to perform actions in a non-transactional cursor when it is
367      * opened, for example, ReplicaThreadLocker performs consistency checks.
368      */
openCursorHook(DatabaseImpl dbImpl)369     public void openCursorHook(DatabaseImpl dbImpl) {
370         /* Do nothing. */
371     }
372 
373     /**
374      * Returns whether a transaction is method indicates whether the txn is
375      * part of the rep stream.
376      *
377      * A replicated txn must be used for writing to a replicated DB, and a
378      * non-replicated txn must be used for writing to a non-replicated DB.
379      * This is critical for avoiding corruption when HA failovers occur
380      * [#23234] [#23330].
381      *
382      * See guard in LN.logInternal.
383      */
isReplicated()384     public boolean isReplicated() {
385         return TxnManager.isReplicatedTxn(id);
386     }
387 
388     /**
389      * Returns true if writes may only be to non-replicated DBs using this
390      * locker, or false if writes may only be to replicated DBs.
391      *
392      * By default (this implementation) local-write is true, since it is
393      * allowed for all non-txnal lockers and for all lockers in a standalone
394      * environment.  This method is overridden and returns false for
395      * for user transactions in a replicated environment that are not
396      * explicitly configured for local-write.
397      *
398      * This method is used to describe a locker's configured usage for checking
399      * the validity of an API write operation.  This is checked by Cursor
400      * methods at the beginning of each write operation.
401      */
isLocalWrite()402     public boolean isLocalWrite() {
403         return true;
404     }
405 
406     /**
407      * Returns whether writes are prohibited using this locker.
408      */
isReadOnly()409     public boolean isReadOnly() {
410         return false;
411     }
412 
413     /*
414      * Obtain and release locks.
415      */
416 
417     /**
418      * Abstract method to a blocking or non-blocking lock of the given type on
419      * the given LSN.  Unlike the lock() method, this method does not throw
420      * LockNotAvailableException and can therefore be used by nonBlockingLock
421      * to probe for a lock without the overhead of an exception stack trace.
422      *
423      * @param lsn is the node to lock.
424      *
425      * @param lockType is the type of lock to request.
426      *
427      * @param noWait is true to override the defaultNoWait setting.  If true,
428      * or if defaultNoWait is true, throws LockNotAvailableException if the
429      * lock cannot be granted without waiting.
430      *
431      * @param jumpAheadOfWaiters grant the lock before other waiters, if any.
432      *
433      * @param database is the database containing lsn.
434      *
435      * @throws LockConflictException if a blocking lock could not be acquired.
436      */
lockInternal(long lsn, LockType lockType, boolean noWait, boolean jumpAheadOfWaiters, DatabaseImpl database)437     abstract LockResult lockInternal(long lsn,
438                                      LockType lockType,
439                                      boolean noWait,
440                                      boolean jumpAheadOfWaiters,
441                                      DatabaseImpl database)
442         throws LockConflictException, DatabaseException;
443 
444     /**
445      * Request a blocking or non-blocking lock of the given type on the given
446      * LSN.
447      *
448      * @param lsn is the node to lock.
449      *
450      * @param lockType is the type of lock to request.
451      *
452      * @param noWait is true to override the defaultNoWait setting.  If true,
453      * or if defaultNoWait is true, throws LockNotAvailableException if the
454      * lock cannot be granted without waiting.
455      *
456      * @param database is the database containing lsn.
457      *
458      * @throws LockNotAvailableException if a non-blocking lock was denied.
459      *
460      * @throws LockConflictException if a blocking lock could not be acquired.
461      */
lock(long lsn, LockType lockType, boolean noWait, DatabaseImpl database)462     public LockResult lock(long lsn,
463                            LockType lockType,
464                            boolean noWait,
465                            DatabaseImpl database)
466         throws LockNotAvailableException, LockConflictException {
467 
468         final LockResult result = lockInternal
469             (lsn, lockType, noWait, false /*jumpAheadOfWaiters*/, database);
470 
471         if (result.getLockGrant() == LockGrantType.DENIED) {
472             /* DENIED can only be returned for a non-blocking lock. */
473             throw lockManager.newLockNotAvailableException
474                 (this, "Non-blocking lock was denied.");
475         } else {
476             checkPreempted(closingLocker);
477             return result;
478         }
479     }
480 
481     /**
482      * Request a non-blocking lock of the given type on the given LSN.
483      *
484      * <p>Unlike lock(), this method returns LockGrantType.DENIED if the lock
485      * is denied rather than throwing LockNotAvailableException.  This method
486      * should therefore not be used as the final lock for a user operation,
487      * since in that case LockNotAvailableException should be thrown for a
488      * denied lock.  It is normally used only to probe for a lock internally,
489      * and other recourse is taken if the lock is denied.</p>
490      *
491      * @param lsn is the node to lock.
492      *
493      * @param lockType is the type of lock to request.
494      *
495      * @param jumpAheadOfWaiters grant the lock before other waiters, if any.
496      *
497      * @param database is the database containing LSN.
498      */
nonBlockingLock(long lsn, LockType lockType, boolean jumpAheadOfWaiters, DatabaseImpl database)499     public LockResult nonBlockingLock(long lsn,
500                                       LockType lockType,
501                                       boolean jumpAheadOfWaiters,
502                                       DatabaseImpl database) {
503         final LockResult result = lockInternal
504             (lsn, lockType, true /*noWait*/, jumpAheadOfWaiters, database);
505         if (result.getLockGrant() != LockGrantType.DENIED) {
506             checkPreempted(closingLocker);
507         }
508         return result;
509     }
510 
511     /**
512      * Release the lock on this LN and remove from the transaction's owning
513      * set.
514      */
releaseLock(long lsn)515     public synchronized boolean releaseLock(long lsn)
516         throws DatabaseException {
517 
518         boolean ret = lockManager.release(lsn, this);
519         removeLock(lsn);
520         return ret;
521     }
522 
523     /**
524      * Revert this lock from a write lock to a read lock.
525      */
demoteLock(long lsn)526     public void demoteLock(long lsn)
527         throws DatabaseException {
528 
529         /*
530          * If successful, the lock manager will call back to the transaction
531          * and adjust the location of the lock in the lock collection.
532          */
533         lockManager.demote(lsn, this);
534     }
535 
536     /**
537      * Called when an LN is logged by an operation that will not hold the lock
538      * such as eviction/checkpoint deferred-write logging or cleaner LN
539      * migration.  We must acquire a lock on the new LSN on behalf of every
540      * locker that currently holds a lock on the old LSN.
541      *
542      * Lock is non-blocking because no contention is possible on the new LSN.
543      *
544      * Because this locker is being used by multiple threads, this method may
545      * be called for a locker that has been closed or for which the lock on the
546      * old LSN has been released.  Unlike other locking methods, in this case
547      * we simply return rather than report an error.
548      */
lockAfterLsnChange(long oldLsn, long newLsn, DatabaseImpl dbImpl)549     public synchronized void lockAfterLsnChange(long oldLsn,
550                                                 long newLsn,
551                                                 DatabaseImpl dbImpl) {
552         if (!isValid()) {
553             /* Locker was recently closed, made abort-only, etc. */
554             return;
555         }
556 
557         final LockType lockType = lockManager.getOwnedLockType(oldLsn, this);
558         if (lockType == null) {
559             /* Lock was recently released. */
560             return;
561         }
562 
563         final LockResult lockResult = nonBlockingLock
564             (newLsn, lockType, false /*jumpAheadOfWaiters*/, dbImpl);
565 
566         if (lockResult.getLockGrant() == LockGrantType.DENIED) {
567             throw EnvironmentFailureException.unexpectedState
568                 ("No contention is possible on new LSN: " +
569                  DbLsn.getNoFormatString(newLsn) +
570                  " old LSN: " + DbLsn.getNoFormatString(oldLsn) +
571                  " LockType: " + lockType);
572         }
573     }
574 
575     /**
576      * In the case where logging occurs before locking, allow lockers to reject
577      * the operation (e.g., if writing on a replica) and also prepare to undo
578      * in the (very unlikely) event that logging succeeds but locking fails.
579      */
preLogWithoutLock(DatabaseImpl database)580     public abstract void preLogWithoutLock(DatabaseImpl database);
581 
582     /**
583      * Throws ReplicaWriteException if called for a locker on a Replica.  This
584      * implementation does nothing but is overridden by replication lockers.
585      * [#20543]
586      */
disallowReplicaWrite()587     public void disallowReplicaWrite() {
588     }
589 
590     /**
591      * Returns whether this locker is transactional.
592      */
isTransactional()593     public abstract boolean isTransactional();
594 
595     /**
596      * Returns whether the isolation level of this locker is serializable.
597      */
isSerializableIsolation()598     public abstract boolean isSerializableIsolation();
599 
600     /**
601      * Returns whether the isolation level of this locker is read-committed.
602      */
isReadCommittedIsolation()603     public abstract boolean isReadCommittedIsolation();
604 
605     /**
606      * Returns the underlying Txn if the locker is transactional, or null if
607      * the locker is non-transactional.  For a Txn-based locker, this method
608      * returns 'this'.  For a BuddyLocker, this method may return the buddy.
609      */
getTxnLocker()610     public abstract Txn getTxnLocker();
611 
612     /**
613      * Returns a Transaction if the locker is transctional, or null otherwise.
614      */
getTransaction()615     public Transaction getTransaction() {
616         return null;
617     }
618 
619     /**
620      * Only BuddyLockers have buddies.
621      */
getBuddy()622     Locker getBuddy() {
623         return null;
624     }
625 
626     /**
627      * Creates a fresh non-transactional locker, while retaining any
628      * transactional locks held by this locker.  This method is called when the
629      * cursor for this locker is cloned.
630      *
631      * <p>This method must return a locker that shares locks with this
632      * locker, e.g., a ThreadLocker.</p>
633      *
634      * <p>In general, transactional lockers return 'this' when this method is
635      * called, while non-transactional lockers return a new instance.</p>
636      */
newNonTxnLocker()637     public abstract Locker newNonTxnLocker()
638         throws DatabaseException;
639 
640     /**
641      * Releases any non-transactional locks held by this locker.  This method
642      * is called when the cursor moves to a new position or is closed.
643      *
644      * <p>In general, transactional lockers do nothing when this method is
645      * called, while non-transactional lockers release all locks as if
646      * operationEnd were called.</p>
647      */
releaseNonTxnLocks()648     public abstract void releaseNonTxnLocks()
649         throws DatabaseException;
650 
651     /**
652      * Releases locks and closes the locker at the end of a non-transactional
653      * cursor operation.  For a transctional cursor this method should do
654      * nothing, since locks must be held until transaction end.
655      */
nonTxnOperationEnd()656     public abstract void nonTxnOperationEnd()
657         throws DatabaseException;
658 
659     /**
660      * By default the set of buddy lockers is not maintained.  This is
661      * overridden by Txn.
662      */
addBuddy(BuddyLocker buddy)663     void addBuddy(BuddyLocker buddy) {
664     }
665 
666     /**
667      * By default the set of buddy lockers is not maintained.  This is
668      * overridden by Txn.
669      */
removeBuddy(BuddyLocker buddy)670     void removeBuddy(BuddyLocker buddy) {
671     }
672 
673     /**
674      * Returns whether this locker can share locks with the given locker.
675      */
sharesLocksWith(Locker other)676     public boolean sharesLocksWith(Locker other) {
677         return false;
678     }
679 
680     /**
681      * The equivalent of calling operationEnd(true).
682      */
operationEnd()683     public final void operationEnd()
684         throws DatabaseException {
685 
686         operationEnd(true);
687     }
688 
689     /**
690      * A SUCCESS status equals operationOk.
691      */
operationEnd(OperationStatus status)692     public final void operationEnd(OperationStatus status)
693         throws DatabaseException {
694 
695         operationEnd(status == OperationStatus.SUCCESS);
696     }
697 
698     /**
699      * Different types of transactions do different things when the operation
700      * ends. Txn does nothing, auto Txn commits or aborts, and BasicLocker (and
701      * its subclasses) just releases locks.
702      *
703      * @param operationOK is whether the operation succeeded, since
704      * that may impact ending behavior. (i.e for an auto Txn)
705      */
operationEnd(boolean operationOK)706     public abstract void operationEnd(boolean operationOK)
707         throws DatabaseException;
708 
709     /**
710      * Should be called by all subclasses when the locker is no longer used.
711      * For Txns and auto Txns this is at commit or abort.  For
712      * non-transactional lockers it is at operationEnd.
713      */
close()714     void close()
715         throws DatabaseException {
716 
717         isOpen = false;
718     }
719 
720     /**
721      * Used to determine whether the locker is usable.
722      *
723      * FUTURE: Note that this method is overridden by Txn, and Txn.abort sets
724      * the state to closed when it begins rather than when it ends, but calls
725      * close() (the method above) when it ends.  This is not ideal and deserves
726      * attention in the future.
727      */
isValid()728     public boolean isValid() {
729         return isOpen;
730     }
731 
732     /**
733      * @see Txn#addOpenedDatabase
734      */
addOpenedDatabase(Database dbHandle)735     public void addOpenedDatabase(Database dbHandle) {
736     }
737 
738     /**
739      * @see HandleLocker#allowReleaseLockAfterLsnChange
740      */
allowReleaseLockAfterLsnChange()741     public boolean allowReleaseLockAfterLsnChange() {
742         return false;
743     }
744 
745     /**
746      * Tell this transaction about a cursor.
747      */
registerCursor(CursorImpl cursor)748     public abstract void registerCursor(CursorImpl cursor);
749 
750     /**
751      * Remove a cursor from this txn.
752      */
unRegisterCursor(CursorImpl cursor)753     public abstract void unRegisterCursor(CursorImpl cursor);
754 
755     /**
756      * Returns true if locking is required for this Locker.  All Txnal lockers
757      * require it; most BasicLockers do not, but BasicLockers on internal dbs
758      * do.
759      */
lockingRequired()760     public abstract boolean lockingRequired();
761 
762     /*
763      * Transactional support
764      */
765 
766     /**
767      * @return the WriteLockInfo for this node.
768      */
getWriteLockInfo(long lsn)769     public abstract WriteLockInfo getWriteLockInfo(long lsn);
770 
771     /**
772      * Database operations like remove and truncate leave behind
773      * residual DatabaseImpls that must be purged at transaction
774      * commit or abort.
775      */
markDeleteAtTxnEnd(DatabaseImpl db, boolean deleteAtCommit)776     public abstract void markDeleteAtTxnEnd(DatabaseImpl db,
777                                             boolean deleteAtCommit)
778         throws DatabaseException;
779 
780     /**
781      * Add delete information, to be added to the inCompressor queue when the
782      * transaction ends.
783      */
addDeleteInfo(BIN bin)784     public void addDeleteInfo(BIN bin) {
785 
786         /*
787          * Skip queue addition if a delta will be logged.  In this case the
788          * slot compression will occur in BIN.beforeLog, when a full version is
789          * logged.
790          */
791         if (bin.shouldLogDelta()) {
792             return;
793         }
794 
795         synchronized (this) {
796             /* Maintain only one binRef per node. */
797             if (deleteInfo == null) {
798                 deleteInfo = new HashMap<Long,BINReference>();
799             }
800             Long nodeId = Long.valueOf(bin.getNodeId());
801             if (deleteInfo.containsKey(nodeId)) {
802                 return;
803             }
804             deleteInfo.put(nodeId, bin.createReference());
805         }
806     }
807 
808     /*
809      * Manage locks owned by this transaction. Note that transactions that will
810      * be multithreaded must override these methods and provide synchronized
811      * implementations.
812      */
813 
814     /**
815      * Add a lock to set owned by this transaction.
816      */
addLock(Long lsn, LockType type, LockGrantType grantStatus)817     protected abstract void addLock(Long lsn,
818                                     LockType type,
819                                     LockGrantType grantStatus)
820         throws DatabaseException;
821 
822     /**
823      * Remove the lock from the set owned by this transaction. If specified to
824      * LockManager.release, the lock manager will call this when its releasing
825      * a lock.
826      */
removeLock(long lsn)827     abstract void removeLock(long lsn)
828         throws DatabaseException;
829 
830     /**
831      * A lock is being demoted. Move it from the write collection into the read
832      * collection.
833      */
moveWriteToReadLock(long lsn, Lock lock)834     abstract void moveWriteToReadLock(long lsn, Lock lock);
835 
836     /**
837      * Get lock count, for per transaction lock stats, for internal debugging.
838      */
collectStats()839     public abstract StatGroup collectStats()
840         throws DatabaseException;
841 
842     /*
843      * Check txn timeout, if set. Called by the lock manager when blocking on a
844      * lock.
845      */
isTimedOut()846     public boolean isTimedOut() {
847         long timeout = getTxnTimeout();
848         if (timeout != 0) {
849             long diff = System.currentTimeMillis() - txnStartMillis;
850             if (diff > timeout) {
851                 return true;
852             }
853         }
854         return false;
855     }
856 
857     /**
858      * Get the transaction timeout period for this locker, in milliseconds
859      *
860      * public for jca/ra/JELocalTransaction.
861      *
862      * WARNING: Be sure to always access the timeout with this accessor, since
863      * it is overridden in BuddyLocker.
864      */
getTxnTimeout()865     public synchronized long getTxnTimeout() {
866         return txnTimeoutMillis;
867     }
868 
getTxnStartMillis()869     long getTxnStartMillis() {
870         return txnStartMillis;
871     }
872 
873     /**
874      * @return if this locker has ever been rolled back.
875      */
isRolledBack()876     public boolean isRolledBack() {
877         return false;  // most Locker types will never roll back.
878     }
879 
880     /**
881      * This method is safe to call without synchronizing and this fact is
882      * relied on by LockManager when creating exception messages.
883      */
884     @Override
toString()885     public String toString() {
886         String className = getClass().getName();
887         className = className.substring(className.lastIndexOf('.') + 1);
888 
889         return System.identityHashCode(this) + " " + Long.toString(id) + "_" +
890                ((thread == null) ? "" : thread.getName()) + "_" +
891                className;
892     }
893 
894     /**
895      * Dump lock table, for debugging
896      */
dumpLockTable()897     public void dumpLockTable()
898         throws DatabaseException {
899 
900         lockManager.dump();
901     }
902 }
903