From 095fdc14f030acf61bee801562b8bdafb7d54e33 Mon Sep 17 00:00:00 2001 From: "Brian S. O'Neill" Date: Fri, 25 Jan 2008 19:56:16 +0000 Subject: Add transaction detach/attach methods. --- .../java/com/amazon/carbonado/Transaction.java | 26 +++++++++ .../carbonado/repo/logging/LoggingTransaction.java | 14 +++++ .../amazon/carbonado/spi/TransactionManager.java | 28 ++++++---- .../com/amazon/carbonado/spi/TransactionPair.java | 20 +++++++ .../com/amazon/carbonado/spi/TransactionScope.java | 65 +++++++++++++--------- 5 files changed, 114 insertions(+), 39 deletions(-) (limited to 'src/main/java/com') diff --git a/src/main/java/com/amazon/carbonado/Transaction.java b/src/main/java/com/amazon/carbonado/Transaction.java index 5c8203f..1c2c06d 100644 --- a/src/main/java/com/amazon/carbonado/Transaction.java +++ b/src/main/java/com/amazon/carbonado/Transaction.java @@ -117,4 +117,30 @@ public interface Transaction { * Returns the isolation level of this transaction. */ IsolationLevel getIsolationLevel(); + + /** + * Detaches this transaction from the current thread. It can be attached + * later, and to any thread which currently has no thread-local + * transaction. + * + *

Detaching a transaction also detaches any parent and nested child + * transactions. Attaching any of them achieves the same result as + * attaching this transaction. + * + * @throws IllegalStateException if transaction is attached to a different + * thread + * @since 1.2 + */ + void detach(); + + /** + * Attaches this transaction to the current thread, if it has been + * detached. Attaching a transaction also attaches any parent and nested + * child transactions. + * + * @throws IllegalStateException if current thread has a different + * transaction already attached + * @since 1.2 + */ + void attach(); } diff --git a/src/main/java/com/amazon/carbonado/repo/logging/LoggingTransaction.java b/src/main/java/com/amazon/carbonado/repo/logging/LoggingTransaction.java index fd88f35..dd2a4f5 100644 --- a/src/main/java/com/amazon/carbonado/repo/logging/LoggingTransaction.java +++ b/src/main/java/com/amazon/carbonado/repo/logging/LoggingTransaction.java @@ -83,6 +83,20 @@ class LoggingTransaction implements Transaction { return mTxn.getIsolationLevel(); } + public void detach() { + if (mLog.isEnabled()) { + mLog.write("Transaction.detach() on " + idChain()); + } + mTxn.detach(); + } + + public void attach() { + if (mLog.isEnabled()) { + mLog.write("Transaction.attach() on " + idChain()); + } + mTxn.attach(); + } + private String idChain() { if (mParent == null) { return String.valueOf(mID); diff --git a/src/main/java/com/amazon/carbonado/spi/TransactionManager.java b/src/main/java/com/amazon/carbonado/spi/TransactionManager.java index 4a28d24..a2b1375 100644 --- a/src/main/java/com/amazon/carbonado/spi/TransactionManager.java +++ b/src/main/java/com/amazon/carbonado/spi/TransactionManager.java @@ -74,32 +74,36 @@ public abstract class TransactionManager { * which does not currently have a TransactionScope. * * @return detached thread-local TransactionScope or null if none + * @since 1.2 */ - /* public TransactionScope detachLocalScope() { TransactionScope scope = mLocalScope.get(); if (scope != null) { - if (!scope.detach()) { - scope = null; - } else { - mLocalScope.remove(); - } + scope.markDetached(); + mLocalScope.remove(); } return scope; } - */ // Called by TransactionScope. - /* - void attach(TransactionScope scope) { + boolean removeLocalScope(TransactionScope scope) { + TransactionScope existing = mLocalScope.get(); + if (existing == null || existing == scope) { + mLocalScope.remove(); + return true; + } + return false; + } + + // Called by TransactionScope. + boolean setLocalScope(TransactionScope scope) { TransactionScope existing = mLocalScope.get(); if (existing == null || existing == scope) { mLocalScope.set(scope); - } else { - throw new IllegalStateException("Current thread already has a transaction scope"); + return true; } + return false; } - */ /** * Closes all transaction scopes. Should be called only when repository is diff --git a/src/main/java/com/amazon/carbonado/spi/TransactionPair.java b/src/main/java/com/amazon/carbonado/spi/TransactionPair.java index d97aef4..ff07bc2 100644 --- a/src/main/java/com/amazon/carbonado/spi/TransactionPair.java +++ b/src/main/java/com/amazon/carbonado/spi/TransactionPair.java @@ -86,4 +86,24 @@ public class TransactionPair implements Transaction { return mPrimaryTransaction.getIsolationLevel() .lowestCommon(mSecondaryTransaction.getIsolationLevel()); } + + public void detach() { + mPrimaryTransaction.detach(); + try { + mSecondaryTransaction.detach(); + } catch (IllegalStateException e) { + mPrimaryTransaction.attach(); + throw e; + } + } + + public void attach() { + mPrimaryTransaction.attach(); + try { + mSecondaryTransaction.attach(); + } catch (IllegalStateException e) { + mPrimaryTransaction.detach(); + throw e; + } + } } diff --git a/src/main/java/com/amazon/carbonado/spi/TransactionScope.java b/src/main/java/com/amazon/carbonado/spi/TransactionScope.java index 0ac2cee..82a20eb 100644 --- a/src/main/java/com/amazon/carbonado/spi/TransactionScope.java +++ b/src/main/java/com/amazon/carbonado/spi/TransactionScope.java @@ -41,8 +41,6 @@ import com.amazon.carbonado.Transaction; * @see TransactionManager */ public class TransactionScope { - private static final int OPEN = 0, DETACHED = 1, CLOSED = 2; - final TransactionManager mTxnMgr; final Lock mLock; @@ -51,12 +49,12 @@ public class TransactionScope { // Tracks all registered cursors by storage type. private Map, CursorList>> mCursors; - private int mState; + private boolean mClosed; + private boolean mDetached; TransactionScope(TransactionManager txnMgr, boolean closed) { mTxnMgr = txnMgr; mLock = new ReentrantLock(true); - mState = closed ? CLOSED : OPEN; } /** @@ -192,7 +190,7 @@ public class TransactionScope { public boolean isForUpdate() { mLock.lock(); try { - return (mState == CLOSED || mActive == null) ? false : mActive.isForUpdate(); + return (mClosed || mActive == null) ? false : mActive.isForUpdate(); } finally { mLock.unlock(); } @@ -205,7 +203,7 @@ public class TransactionScope { public IsolationLevel getIsolationLevel() { mLock.lock(); try { - return (mState == CLOSED || mActive == null) ? null : mActive.getIsolationLevel(); + return (mClosed || mActive == null) ? null : mActive.getIsolationLevel(); } finally { mLock.unlock(); } @@ -215,41 +213,46 @@ public class TransactionScope { * Attach this scope to the current thread, if it has been {@link * TransactionManager#detachLocalScope detached}. * - * @throws IllegalStateException if scope is not detached or if current - * thread already has a transaction scope + * @throws IllegalStateException if current thread has a different + * transaction already attached */ - /* public void attach() { mLock.lock(); try { - if (mState != DETACHED) { - throw new IllegalStateException("Transaction scope is not detached"); + if (mTxnMgr.setLocalScope(this)) { + mDetached = false; + } else { + throw new IllegalStateException + ("Current thread has a different transaction already attached"); } - mState = OPEN; } finally { mLock.unlock(); } - mTxnMgr.attach(this); } - */ - /** - * @return false if closed - */ - /* - boolean detach() { + // Called by TransactionImpl. + void detach() { mLock.lock(); try { - if (mState == CLOSED) { - return false; + if (mTxnMgr.removeLocalScope(this)) { + mDetached = true; + } else { + throw new IllegalStateException("Transaction is attached to a different thread"); } - mState = DETACHED; - return true; } finally { mLock.unlock(); } } - */ + + // Called by TransactionManager. + void markDetached() { + mLock.lock(); + try { + mDetached = true; + } finally { + mLock.unlock(); + } + } /** * Exits all transactions and closes all cursors. Should be called only @@ -258,7 +261,7 @@ public class TransactionScope { void close() throws RepositoryException { mLock.lock(); try { - if (mState != CLOSED) { + if (!mClosed) { while (mActive != null) { mActive.exit(); } @@ -269,7 +272,7 @@ public class TransactionScope { } } } finally { - mState = CLOSED; + mClosed = true; mLock.unlock(); } } @@ -278,7 +281,7 @@ public class TransactionScope { * Caller must hold mLock. */ private void checkClosed() { - if (mState == CLOSED) { + if (mClosed) { throw new IllegalStateException("Repository is closed"); } } @@ -409,6 +412,14 @@ public class TransactionScope { return mLevel; } + public void detach() { + mScope.detach(); + } + + public void attach() { + mScope.attach(); + } + // Caller must hold mLock. void register(Cursor cursor) { if (mCursorList == null) { -- cgit v1.2.3