001: // You can redistribute this software and/or modify it under the terms of
002: // the Ozone Core License version 1 published by ozone-db.org.
003: //
004: // The original code and portions created by SMB are
005: // Copyright (C) 1997-@year@ by SMB GmbH. All rights reserved.
006: //
007: // $Id: TransactionManager.java,v 1.2 2002/06/08 00:49:38 mediumnet Exp $
008:
009: package org.ozoneDB.core;
010:
011: import java.io.IOException;
012: import java.util.Random;
013: import org.ozoneDB.*;
014: import org.ozoneDB.DxLib.*;
015: import org.ozoneDB.core.DbRemote.*;
016: import org.ozoneDB.core.dr.*;
017: import org.ozoneDB.util.*;
018:
019: /**
020: * The transaction manager is the factory for transactions and locks.
021: *
022: *
023: * @author <a href="http://www.softwarebuero.de/">SMB</a>
024: * @author <a href="http://www.medium.net/">Medium.net</a>
025: * @version $Revision: 1.2 $Date: 2002/06/08 00:49:38 $
026: */
027: public final class TransactionManager extends ServerComponent {
028:
029: /**
030: * All currently running transactions. taID -> ta
031: */
032: protected DxMap taTable;
033:
034: /**
035: * In local mode this maps threads to transactions.
036: */
037: protected DxMap threadTable;
038:
039: /**
040: * Used to signal a deadlock and which Transaction should abort.
041: */
042: protected Transaction deadlockTA;
043:
044: /**
045: * True if one thread runs exclusively.
046: */
047: protected Thread exclusiveThread;
048:
049: protected long acquireCount;
050:
051: public TransactionManager(Env _env) {
052: super (_env);
053: taTable = new DxHashMap(32);
054: threadTable = new DxHashMap(32);
055: }
056:
057: public void startup() throws Exception {
058: env.logWriter.newEntry(this , "startup...", LogWriter.INFO);
059: }
060:
061: public void shutdown() throws Exception {
062: env.logWriter.newEntry(this , "shutdown...", LogWriter.INFO);
063:
064: env.logWriter.newEntry(this , " there are " + taTable.count()
065: + " pending transaction(s)", LogWriter.INFO);
066: if (!taTable.isEmpty()) {
067: env.logWriter.newEntry(this ,
068: " aborting pending transactions...",
069: LogWriter.INFO);
070: DxIterator it = taTable.iterator();
071: while (it.next() != null) {
072: ((Transaction) it.object()).stop();
073: }
074:
075: env.logWriter.newEntry(this ,
076: " waiting for transactions to end...",
077: LogWriter.INFO);
078: for (int sec = 10; !taTable.isEmpty(); sec--) {
079: Thread.sleep(1000);
080: }
081: }
082:
083: env.logWriter.newEntry(this , "acquire count total:"
084: + acquireCount, LogWriter.INFO);
085: }
086:
087: public void save() throws Exception {
088: }
089:
090: /**
091: * Factory method to generate proper Lock objects. These Lock objects are
092: * used in the store backend but they have to be generated in the core to
093: * meet the requirements of the transaction implementation.
094: */
095: public Lock newLock() {
096: return new MROWLock();
097: }
098:
099: public int taTableCount() {
100: return taTable.count();
101: }
102:
103: public Transaction taForID(TransactionID taID) {
104: return (Transaction) taTable.elementForKey(taID);
105: }
106:
107: /**
108: * der aktuelle thread is auch die aktuelle transaktion (wenn der
109: * thread ueberhaupt eine transaktion ist)
110: */
111: public Transaction currentTA() {
112: Thread thread = Thread.currentThread();
113:
114: // see startTransaction()
115: if (thread instanceof CommandThread) {
116: return ((CommandThread) thread).ta;
117: } else {
118: Transaction result = (Transaction) threadTable
119: .elementForKey(thread);
120: return result;
121: }
122: }
123:
124: public Transaction newTransaction(User owner) throws TransactionExc {
125: if (false && env.logWriter.hasTarget(LogWriter.DEBUG2)) {
126: env.logWriter.newEntry(this ,
127: "newTransaction() *****************************",
128: LogWriter.DEBUG2);
129: }
130:
131: if (currentTA() != null) {
132: throw new TransactionExc(
133: "Thread is already joined to a transaction.",
134: TransactionExc.STATE);
135: }
136:
137: Transaction ta = new Transaction(env, owner);
138: Thread thread = Thread.currentThread();
139:
140: // in the server threads are CommandThreads and the corresponding
141: // transaction is a member of the CommandThread
142: if (thread instanceof CommandThread) {
143: CommandThread commandThread = (CommandThread) Thread
144: .currentThread();
145: commandThread.setTransaction(ta);
146: }
147:
148: if (env.logWriter.hasTarget(LogWriter.DEBUG2)) {
149: env.logWriter.newEntry(this , "newTransaction(): ta=" + ta
150: + ", thread=" + thread + ".", LogWriter.DEBUG2);
151: }
152:
153: synchronized (this ) {
154: // if we are local, threads can be of any type; we add them to the
155: // threadTable to be able to found the corresponding transaction
156: threadTable.addForKey(ta, thread);
157:
158: taTable.addForKey(ta, ta.taID());
159: }
160: return ta;
161: }
162:
163: /**
164: * Delete the transaction that is associated with the current thread.
165: */
166: public void deleteTransaction() {
167: if (false && env.logWriter.hasTarget(LogWriter.DEBUG2)) {
168: env.logWriter.newEntry(this ,
169: "deleteTransaction() --------------------------",
170: LogWriter.DEBUG2);
171: }
172:
173: Transaction ta = currentTA();
174: if (ta == null) {
175: env.logWriter
176: .newEntry(
177: this ,
178: "deleteTransaction(): thread is not joined to a transaction.",
179: LogWriter.WARN);
180: } else {
181: if (env.logWriter.hasTarget(LogWriter.DEBUG2)) {
182: env.logWriter.newEntry(this , "deleteTransaction() ta="
183: + ta + ".", LogWriter.DEBUG2);
184: }
185: synchronized (this ) {
186: acquireCount += ta.acquireCount;
187: if (false && env.logWriter.hasTarget(LogWriter.DEBUG2)) {
188: env.logWriter.newEntry(this , " acquire count:"
189: + ta.acquireCount, LogWriter.DEBUG2);
190: env.logWriter.newEntry(this , " total :"
191: + acquireCount, LogWriter.DEBUG2);
192: }
193:
194: Thread thread = Thread.currentThread();
195: if (thread instanceof CommandThread) {
196: ((CommandThread) thread).setTransaction(null);
197: }
198:
199: threadTable.removeForKey(thread);
200: taTable.removeForKey(ta.taID());
201:
202: env.getGarbageCollector()
203: .removeTransactionRequiredToComplete(ta);
204: }
205: }
206: }
207:
208: /**
209: If we notify all transactions, they not-blocked-ones may wake up from sleeping.
210: */
211: protected boolean alsoNotifySomeSleepingTransactions = true;
212:
213: /**
214: * Notify each thread that is associated with a currently blocked
215: * transaction by calling notifyAll() on the blocked transaction.
216: */
217: public void notifyWaitingTransactions() {
218: if (false && env.logWriter.hasTarget(LogWriter.DEBUG3)) {
219: env.logWriter.newEntry(this , "notifyWaitingTransactions()",
220: LogWriter.DEBUG3);
221: }
222:
223: DxArrayBag tas = new DxArrayBag(taTable.count());
224:
225: // search all blocked transactions in a synchronized block (to prevent
226: // others from chnaging the taTable) but call notifyAll() later in a
227: // non-synchronized block tp prevent us from deadlocks
228: synchronized (this ) {
229: DxIterator it = taTable.iterator();
230: Transaction ta;
231: while ((ta = (Transaction) it.next()) != null) {
232: if ((ta.blocker != null)) {
233: tas.add(ta);
234: }
235: }
236:
237: if (alsoNotifySomeSleepingTransactions) {
238: if (tas.isEmpty()) {
239: it.reset();
240: while ((ta = (Transaction) it.next()) != null) {
241: if (ta.isSleeping()) { // A sleeping
242: tas.add(ta);
243: break;
244: }
245: }
246: }
247: }
248: }
249:
250: DxIterator it = tas.iterator();
251: Transaction ta;
252: while ((ta = (Transaction) it.next()) != null) {
253: if (false && env.logWriter.hasTarget(LogWriter.DEBUG)) {
254: env.logWriter.newEntry(this , " notify: " + ta,
255: LogWriter.DEBUG);
256: }
257: synchronized (ta) {
258: ta.notifyAll();
259: }
260: }
261: }
262:
263: /**
264: * Handle the specified command on behalf of the current thread/transaction.
265: * This method is called by the InvokeServer after it has handled
266: * InvokeServer specific command.
267: */
268: public void handleCommand(DbCommand command, DbInvokeClient client) {
269: handleCommand(command, client.getUser());
270: }
271:
272: /**
273: * Handle the specified command on behalf of the current thread/transaction.
274: * This method is called by the InvokeServer after it has handled
275: * InvokeServer specific command.
276: */
277: public void handleCommand(DbCommand command, User user) {
278: if (env.logWriter.hasTarget(LogWriter.DEBUG)) {
279: env.logWriter.newEntry(this , "handleCommand(): "
280: + command.toString(), LogWriter.DEBUG);
281: }
282:
283: command.result = null;
284: try {
285: // close this connection/thread/transaction
286: if (command instanceof DbCloseConn) {
287: Transaction ta = currentTA();
288: if (ta != null) {
289: abortTransaction(ta, command);
290: deleteTransaction();
291: }
292: } else if (!(command instanceof DbTransaction)) {
293:
294: // perform all commands other then DbTransaction
295: Transaction ta = currentTA();
296: if (ta == null) {
297: completeTransaction(command, /*client*/user);
298: } else {
299: // if something goes wrong while performing this command the client
300: // has to abort the transaction
301: performCommand(ta, command);
302: }
303: } else {
304: // perform transaction demarcation command; test for error conditions
305: // and signal error via command.result to avoid exceptions that are
306: // converted to OzoneInternalExc by the enclosing try/catch
307: switch (((DbTransaction) command).mode()) {
308:
309: case DbTransaction.MODE_BEGIN: {
310: if (currentTA() != null) {
311: command.result = new TransactionExc(
312: "Thread is already joined to a transaction.",
313: TransactionExc.STATE);
314: } else {
315: Transaction ta = newTransaction(user /*client.getUser()*/);
316: command.result = ta.taID();
317: }
318: break;
319: }
320:
321: case DbTransaction.MODE_PREPARE: {
322: prepareTransaction(currentTA(), command);
323: break;
324: }
325:
326: case DbTransaction.MODE_COMMIT_TWOPHASE: {
327: commitTransaction(currentTA(), command);
328: deleteTransaction();
329: break;
330: }
331:
332: case DbTransaction.MODE_COMMIT_ONEPHASE: {
333: Transaction ta = currentTA();
334: if (prepareTransaction(ta, command)) {
335: commitTransaction(ta, command);
336: }
337: deleteTransaction();
338: break;
339: }
340:
341: case DbTransaction.MODE_ABORT: {
342: abortTransaction(currentTA(), command);
343: deleteTransaction();
344: break;
345: }
346:
347: case DbTransaction.MODE_CHECKPOINT: {
348: command.result = new OzoneInternalExc(
349: "External CHECKPOINT is not implemented yet.");
350: break;
351: }
352: case DbTransaction.MODE_STATUS: {
353: command.result = new Integer(currentTA().status());
354: break;
355: }
356: }
357: }
358: } catch (Throwable e) {
359: // all exception that are not handled by the underlying methods
360: // are internal 'panic' errors
361: env.logWriter.newEntry(this , "handleCommand(): " + e, e,
362: LogWriter.ERROR);
363: command.result = new OzoneInternalExc(e.toString(), e);
364: deleteTransaction();
365: }
366: }
367:
368: /**
369: * Perform the specified command within a new transaction.
370: */
371: protected void completeTransaction(DbCommand command, User user /*DbInvokeClient client*/)
372: throws Exception {
373: if (env.logWriter.hasTarget(LogWriter.DEBUG3)) {
374: env.logWriter.newEntry(this , "completeTransaction(): "
375: + command, LogWriter.DEBUG3);
376: }
377: if (/*client.getUser()*/user == null) {
378: throw new TransactionExc(
379: "No owner set for current transaction.");
380: }
381:
382: Transaction ta = newTransaction(/*client.getUser()*/user);
383:
384: // perform->prepare->commit/abort
385: boolean alright = false;
386:
387: try {
388: if (alright = performCommand(ta, command)) {
389: if (prepareTransaction(ta, command)) {
390: commitTransaction(ta, command);
391: }
392: }
393: } finally {
394: if (!alright) {
395: abortTransaction(ta, command);
396: }
397: }
398: deleteTransaction();
399: }
400:
401: /**
402: * Perform the specified command on behalf of the specified
403: * thread/transaction. If the transaction has performed only one command
404: * until now, this handles deadlocks by re-performing the command again
405: * until it throws an exception or completes sucessfully. Otherwise an
406: * exception is thrown
407: *
408: *
409: * @return True if the command did not throw an exception
410: * @throws Exception Any exception always signals an internal error.
411: */
412: protected boolean performCommand(Transaction ta, DbCommand command)
413: throws Exception {
414: if (env.logWriter.hasTarget(LogWriter.DEBUG3)) {
415: env.logWriter.newEntry(this , "performCommand(): start: "
416: + ta.toString() + ", " + command.toString(),
417: LogWriter.DEBUG3);
418: }
419:
420: boolean result = true;
421:
422: try {
423: // result goes in command.result
424: result = ta.performCommand(command);
425:
426: // other than TransactionError exceptions are catched by
427: // ta.performCommand()
428: } catch (TransactionError e) {
429: if (e.code() == TransactionError.DEADLOCK) {
430:
431: if (ta.commandCount > 1) {
432: env.logWriter.newEntry(this , ta.toString()
433: + " deadlocked; throwing exception...",
434: LogWriter.WARN);
435: command.result = new DeadlockExc("");
436: result = false;
437: } else {
438: Random rand = new Random();
439: boolean deadlocked = true;
440: while (deadlocked) {
441: try {
442: env.logWriter.newEntry(this , ta.toString()
443: + " aborting... (DEADLOCK)",
444: LogWriter.WARN);
445: abortTransaction(ta, command);
446:
447: ta.setDeadlocked(false);
448:
449: long millis = (long) (rand.nextDouble() * ta
450: .increaseDeadlockWaitTimeMaximum());
451: env.logWriter.newEntry(this , ta.toString()
452: + " sleeping " + millis
453: + " milliseconds...",
454: LogWriter.WARN);
455: ta.sleep(millis);
456:
457: env.logWriter.newEntry(this , ta.toString()
458: + " re-run...", LogWriter.WARN);
459: ta.reset();
460: result = ta.performCommand(command);
461:
462: deadlocked = false;
463: } catch (TransactionError ee) {
464: if (ee.code() == TransactionError.DEADLOCK) {
465: // still deadlocked
466: } else {
467: throw ee;
468: }
469: }
470: }
471: }
472: } else {
473: // other than DEADLOCK TransactionErrors are internal errors
474: throw e;
475: }
476: } finally {
477: if (env.logWriter.hasTarget(LogWriter.DEBUG3)) {
478: env.logWriter.newEntry(this , "performCommand(): end: "
479: + ta.toString() + ", " + command.toString(),
480: LogWriter.DEBUG3);
481: }
482: }
483: return result;
484: }
485:
486: /**
487: * Prepare the specified transaction. Return true on success and false
488: * if something failed. In this case the transaction is rolled back. This
489: * method throws an exception only if an internal server error occured.
490: */
491: protected boolean prepareTransaction(Transaction ta,
492: DbCommand command) throws Exception {
493: if (false && env.logWriter.hasTarget(LogWriter.DEBUG3)) {
494: env.logWriter.newEntry(this , "prepareTransaction()",
495: LogWriter.DEBUG3);
496: }
497:
498: // if the commit was requested by the client, the following conditions
499: // are not internal error and so they must not throw an exception
500: if (ta == null) {
501: env.logWriter
502: .newEntry(
503: this ,
504: "prepareTransaction(): Thread is not joined to a transaction.",
505: LogWriter.WARN);
506: command.result = new TransactionExc(
507: "Thread is not joined to a transaction.",
508: TransactionExc.STATE);
509: return false;
510: } else if (ta.status != Transaction.STATUS_STARTED) {
511: env.logWriter.newEntry(this , "prepareTransaction():"
512: + ta.toString()
513: + ": Transaction has inproper status.",
514: LogWriter.WARN);
515: command.result = new TransactionExc(
516: "Transaction has inproper status.",
517: TransactionExc.STATE);
518: return false;
519: } else if (ta.rollbackOnly) {
520: env.logWriter.newEntry(this , "prepareTransaction():"
521: + ta.toString() + ": rollback only.",
522: LogWriter.WARN);
523: abortTransaction(ta, command);
524: command.result = new TransactionExc(
525: "Transaction is in rollback only status.",
526: TransactionExc.ROLLBACK);
527: return false;
528: } else {
529: try {
530: // beginExclusion();
531: ta.prepareCommit();
532: return true;
533: } catch (Throwable e) {
534: env.logWriter.newEntry(this ,
535: "Prepare transaction failed: " + ta.toString()
536: + "; aborting...", e, LogWriter.WARN);
537: abortTransaction(ta, command);
538:
539: command.result = new TransactionExc(e.toString(),
540: TransactionExc.ROLLBACK);
541: return false;
542: } finally {
543: // endExclusion();
544: }
545: }
546: }
547:
548: protected void commitTransaction(Transaction ta, DbCommand command)
549: throws Exception {
550: if (env.logWriter.hasTarget(LogWriter.DEBUG3)) {
551: env.logWriter.newEntry(this , "commitTransaction()",
552: LogWriter.DEBUG3);
553: }
554:
555: // if the commit was requested by the client, the following conditions
556: // are not internal error and so they must not throw an exception
557: if (ta == null) {
558: env.logWriter
559: .newEntry(
560: this ,
561: "commitTransaction(): Thread is not joined to a transaction.",
562: LogWriter.WARN);
563: command.result = new TransactionExc(
564: "Thread is not joined to a transaction.",
565: TransactionExc.STATE);
566: } else if (ta.status != Transaction.STATUS_PREPARED) {
567: env.logWriter.newEntry(this , "commitTransaction(): "
568: + ta.toString()
569: + ": Transaction has inproper status.",
570: LogWriter.WARN);
571: command.result = new TransactionExc(
572: "Transaction has inproper status.",
573: TransactionExc.STATE);
574: } else {
575: try {
576: beginExclusion();
577: ta.commit();
578: } catch (Throwable e) {
579: env.logWriter.newEntry(this ,
580: "Commit transaction failed: " + ta.toString(),
581: e, LogWriter.WARN);
582: command.result = e;
583: if (e instanceof Error) {
584: throw (Error) e;
585: } else {
586: throw (Exception) e;
587: }
588: } finally {
589: endExclusion();
590: notifyWaitingTransactions();
591: }
592: }
593: }
594:
595: protected void abortTransaction(Transaction ta, DbCommand command)
596: throws Exception {
597: if (env.logWriter.hasTarget(LogWriter.DEBUG3)) {
598: env.logWriter.newEntry(this , "abortTransaction()",
599: LogWriter.DEBUG3);
600: }
601:
602: // if the commit was requested by the client, the following conditions
603: // are not internal error and so they must not throw an exception
604: if (ta == null) {
605: env.logWriter
606: .newEntry(
607: this ,
608: "abortTransaction(): Thread is not joined to a transaction.",
609: LogWriter.WARN);
610: command.result = new TransactionExc(
611: "Thread is not joined to a transaction.",
612: TransactionExc.STATE);
613: } else if (ta.status >= Transaction.STATUS_COMMITING) {
614: env.logWriter.newEntry(this , "abortTransaction(): "
615: + ta.toString()
616: + ": Transaction has inproper status.",
617: LogWriter.WARN);
618: command.result = new TransactionExc(
619: "Transaction has inproper status.",
620: TransactionExc.STATE);
621: } else {
622: try {
623: beginExclusion();
624: ta.abort(command);
625: } catch (Throwable e) {
626: env.logWriter
627: .newEntry(this , "Aborting transaction failed: "
628: + ta.toString(), e, LogWriter.WARN);
629: command.result = e;
630: if (e instanceof Error) {
631: throw (Error) e;
632: } else {
633: throw (Exception) e;
634: }
635: } finally {
636: endExclusion();
637: notifyWaitingTransactions();
638: }
639: }
640: }
641:
642: /**
643: * Blocks execution until there is no thread scheduled for exclusive
644: * execution or the exclusive thread is the current thread.
645: */
646: public void checkExclusion() {
647: // evaluate the current thread only if there actually is an
648: // exclusiveThread
649: if (exclusiveThread != null
650: && exclusiveThread != Thread.currentThread()) {
651: synchronized (this ) {
652: while (exclusiveThread != null
653: && exclusiveThread != Thread.currentThread()) {
654: try {
655: env.logWriter.newEntry(this ,
656: "checkExclusion(): waiting... ("
657: + currentTA() + ")",
658: LogWriter.DEBUG2);
659: wait();
660: env.logWriter.newEntry(this ,
661: "checkExclusion(): notified... ("
662: + currentTA() + ")",
663: LogWriter.DEBUG2);
664: } catch (InterruptedException e) {
665: }
666: }
667: }
668: }
669: }
670:
671: protected synchronized void beginExclusion() {
672: checkExclusion();
673:
674: Thread currentThread = Thread.currentThread();
675: if (exclusiveThread != null && exclusiveThread != currentThread) {
676: throw new RuntimeException(
677: "Another thread already runs exclusively.");
678: }
679:
680: exclusiveThread = currentThread;
681:
682: if (env.logWriter.hasTarget(LogWriter.DEBUG3)) {
683: // env.logWriter.newEntry( this, "beginExclusion(): ", LogWriter.DEBUG3 );
684: }
685: }
686:
687: protected synchronized void endExclusion() {
688: if (env.logWriter.hasTarget(LogWriter.DEBUG3)) {
689: // env.logWriter.newEntry( this, "endExclusion(): ", LogWriter.DEBUG3 );
690: }
691:
692: Thread currentThread = Thread.currentThread();
693: if (exclusiveThread != currentThread) {
694: throw new RuntimeException(
695: "Current thread does not run exclusively.");
696: }
697:
698: exclusiveThread = null;
699: notifyAll();
700: }
701:
702: /**
703: * This method checks for deadlocks between all current transaction.
704: */
705: public synchronized void checkDeadlocks() throws Exception {
706: DeadlockRecognition dr = env.deadlockRecognition();
707:
708: // count blocked transactions
709: int blockedCount = 0;
710: DxIterator it = taTable.iterator();
711: for (Transaction ta; (ta = (Transaction) it.next()) != null;) {
712: if (ta.isBlocked()) {
713: blockedCount++;
714: }
715: }
716:
717: // env.logWriter.newEntry( this,"*** checkDeadlocks: blockedCount=" + blockedCount + " exclusive=" + exclusiveThread,LogWriter.DEBUG3 );
718:
719: // we need to check things only if there are at least 2 blocked ta's
720: if (blockedCount >= 2) {
721:
722: try {
723: // ensure that nothing changes while we check for deadlocks
724: beginExclusion();
725:
726: it = taTable.iterator();
727: for (Transaction ta; (ta = (Transaction) it.next()) != null;) {
728: Transaction candidate = (Transaction) dr
729: .detectDeadlock(ta);
730: if (candidate != null) {
731: env.logWriter.newEntry(this ,
732: "*** *** DEADLOCK DETECTED: ta=" + ta,
733: LogWriter.WARN);
734:
735: if (false) { // Why should we sleep here?
736: Thread.sleep(5000);
737: } else {
738: /*
739: We should just synchronized so that we commit the now-set "deadlocked" flags of
740: the deadlocked transactions into main memory where they can read them out.
741: */
742: /* Even that commit should happen sooner or later. */
743: /*
744: synchronized (candidate) {
745: }
746: Thread.sleep(1);
747: */
748: }
749:
750: // this will be checked by all transactions using
751: // isDeadlockTA() to determine if it has to abort
752: deadlockTA = candidate;
753: // env.storeManager.containerForID (null, ta.blocker).notifyAllTAs (ta);
754: synchronized (deadlockTA) {
755: deadlockTA.notifyAll();
756: }
757:
758: // DR runs on a higher priority than normal transactions, so
759: // we have to stop after there is one deadlock detected to let
760: // the notification wakeup released transactions; if there are
761: // more deadlocks we will catch them next time
762: return;
763: }
764: }
765: } finally {
766: endExclusion();
767: }
768: }
769: }
770:
771: /**
772: * Check if the given transaction should abort because of
773: * a deadlock.
774: */
775: public synchronized boolean isDeadlockTA(Transaction ta) {
776: if (ta == deadlockTA) {
777: deadlockTA = null;
778: return true;
779: }
780: return false;
781: }
782:
783: /**
784: Starts the GarbageColection pre-phase. This is the time where all transactions have to complete (either commit or rollback)
785: until there are no transactions left which were created before this call.
786: */
787: public void startGarbageCollectionWaitForCurrentTransactionsToCompletePhase(
788: GarbageCollector garbageCollector) {
789: synchronized (this ) {
790: DxIterator i = taTable.elementSet().iterator();
791:
792: while (i.next() != null) {
793: garbageCollector.addTransactionRequiredToComplete(i
794: .object());
795: }
796: garbageCollector
797: .checkForEndOfWaitForCurrentTransactionsToCompletePhase();
798: }
799: }
800: }
|