// ********************************************************************** // // Copyright (c) 2003-2008 ZeroC, Inc. All rights reserved. // // This copy of Ice is licensed to you under the terms described in the // ICE_LICENSE file included in this distribution. // // ********************************************************************** #include #include #include #include #include using namespace std; using namespace Ice; DbTxn* Freeze::getTxn(const Freeze::TransactionPtr& tx) { return dynamic_cast(tx.get())->dbTxn(); } void Freeze::TransactionI::commit() { if(_txn == 0) { throw DatabaseException(__FILE__, __LINE__, "inactive transaction"); } long txnId = 0; try { _connection->closeAllIterators(); if(_txTrace >= 1) { txnId = (_txn->id() & 0x7FFFFFFF) + 0x80000000L; } _txn->commit(0); if(_txTrace >= 1) { Trace out(_communicator->getLogger(), "Freeze.Transaction"); out << "committed transaction " << hex << txnId << dec; } } catch(const ::DbDeadlockException& dx) { if(_txTrace >= 1) { Trace out(_communicator->getLogger(), "Freeze.Transaction"); out << "failed to commit transaction " << hex << txnId << dec << ": " << dx.what(); } postCompletion(false, true); // After postCompletion is called the transaction may be // dead. Beware! DeadlockException ex(__FILE__, __LINE__); ex.message = dx.what(); throw ex; } catch(const ::DbException& dx) { if(_txTrace >= 1) { Trace out(_communicator->getLogger(), "Freeze.Transaction"); out << "failed to commit transaction " << hex << txnId << dec << ": " << dx.what(); } postCompletion(false, false); // After postCompletion is called the transaction may be // dead. Beware! DatabaseException ex(__FILE__, __LINE__); ex.message = dx.what(); throw ex; } postCompletion(true, false); // After postCompletion is called the transaction may be // dead. Beware! } void Freeze::TransactionI::rollback() { rollbackInternal(false); } Freeze::ConnectionPtr Freeze::TransactionI::getConnection() const { if(_txn) { return _connection; } return 0; } void Freeze::TransactionI::rollbackInternal(bool warning) { if(_txn != 0) { long txnId = 0; try { _connection->closeAllIterators(); if(_txTrace >= 1 || (warning && _warnRollback)) { txnId = (_txn->id() & 0x7FFFFFFF) + 0x80000000L; if(warning && _warnRollback) { Warning warn(_communicator->getLogger()); warn << "Freeze.Transaction: rolled back transaction " << hex << txnId << dec << " due to destruction.\nApplication code should explicitly call rollback or commit."; } } _txn->abort(); if(_txTrace >= 1) { Trace out(_communicator->getLogger(), "Freeze.Transaction"); out << "rolled back transaction " << hex << txnId << dec; } } catch(const ::DbDeadlockException& dx) { if(_txTrace >= 1) { Trace out(_communicator->getLogger(), "Freeze.Transaction"); out << "failed to rollback transaction " << hex << txnId << dec << ": " << dx.what(); } DeadlockException deadlockException(__FILE__, __LINE__, dx.what(), this); postCompletion(false, true); // After postCompletion is called the transaction may be // dead. Beware! throw deadlockException; } catch(const ::DbException& dx) { if(_txTrace >= 1) { Trace out(_communicator->getLogger(), "Freeze.Transaction"); out << "failed to rollback transaction " << hex << txnId << dec << ": " << dx.what(); } postCompletion(false, false); // After postCompletion is called the transaction may be // dead. Beware! throw DatabaseException(__FILE__, __LINE__, dx.what()); } postCompletion(true, false); // After postCompletion is called the transaction may be // dead. Beware! } } void Freeze::TransactionI::__incRef() { IceUtil::Mutex::Lock sync(_refCountMutex->mutex); _refCount++; } void Freeze::TransactionI::__decRef() { IceUtil::Mutex::Lock sync(_refCountMutex->mutex); if(--_refCount == 0) { sync.release(); delete this; } else if(_txn != 0 && _refCount == 1 && _connection->__getRefNoSync() == 1) { sync.release(); rollbackInternal(true); } } int Freeze::TransactionI::__getRef() const { IceUtil::Mutex::Lock sync(_refCountMutex->mutex); return _refCount; } int Freeze::TransactionI::__getRefNoSync() const { return _refCount; } void Freeze::TransactionI::setPostCompletionCallback(const Freeze::PostCompletionCallbackPtr& cb) { _postCompletionCallback = cb; } // // The constructor takes a ConnectionI* instead of a ConnectionIPtr // because we have to ensure there is no call to __decRef while the // transaction or the connection are not assigned to a Ptr in // user-code. // Freeze::TransactionI::TransactionI(ConnectionI* connection) : _communicator(connection->communicator()), _connection(connection), _txTrace(connection->txTrace()), _warnRollback(_communicator->getProperties()->getPropertyAsIntWithDefault("Freeze.Warn.Rollback", 1)), _txn(0), _refCountMutex(connection->_refCountMutex), _refCount(0) { try { _connection->dbEnv()->getEnv()->txn_begin(0, &_txn, 0); if(_txTrace >= 1) { long txnId = (_txn->id() & 0x7FFFFFFF) + 0x80000000L; Trace out(_communicator->getLogger(), "Freeze.Transaction"); out << "started transaction " << hex << txnId << dec; } } catch(const ::DbException& dx) { if(_txTrace >= 1) { Trace out(_communicator->getLogger(), "Freeze.Transaction"); out << "failed to start transaction: " << dx.what(); } DatabaseException ex(__FILE__, __LINE__); ex.message = dx.what(); throw ex; } } Freeze::TransactionI::~TransactionI() { assert(_txn == 0); } void Freeze::TransactionI::postCompletion(bool committed, bool deadlock) { // The order of assignment in this method is very important as // calling both the post completion callback and // Connection::clearTransaction may alter the transaction // reference count which checks _txn. { // // We synchronize here as _txn is checked (read) in the refcounting code // IceUtil::Mutex::Lock sync(_refCountMutex->mutex); _txn = 0; } if(_postCompletionCallback != 0) { _postCompletionCallback->postCompletion(committed, deadlock); } ConnectionIPtr connection = _connection; _connection = 0; connection->clearTransaction(); // may release the last _refCount }