summaryrefslogtreecommitdiff
path: root/libodb/odb/transaction.cxx
diff options
context:
space:
mode:
Diffstat (limited to 'libodb/odb/transaction.cxx')
-rw-r--r--libodb/odb/transaction.cxx356
1 files changed, 356 insertions, 0 deletions
diff --git a/libodb/odb/transaction.cxx b/libodb/odb/transaction.cxx
new file mode 100644
index 0000000..f75cf32
--- /dev/null
+++ b/libodb/odb/transaction.cxx
@@ -0,0 +1,356 @@
+// file : odb/transaction.cxx
+// license : GNU GPL v2; see accompanying LICENSE file
+
+#include <odb/transaction.hxx>
+#include <odb/exceptions.hxx>
+
+#include <odb/details/tls.hxx>
+
+using namespace std;
+
+namespace odb
+{
+ using namespace details;
+
+ //
+ // transaction
+ //
+
+ static ODB_TLS_POINTER (transaction) current_transaction;
+
+ transaction::
+ ~transaction ()
+ {
+ if (!finalized_)
+ try {rollback ();} catch (...) {}
+ }
+
+ void transaction::
+ reset (transaction_impl* impl, bool make_current)
+ {
+ details::unique_ptr<transaction_impl> i (impl);
+
+ if (!finalized_)
+ rollback ();
+
+ impl_.reset (i.release ());
+
+ if (make_current && tls_get (current_transaction) != 0)
+ throw already_in_transaction ();
+
+ impl_->start ();
+ finalized_ = false;
+
+ if (make_current)
+ tls_set (current_transaction, this);
+ }
+
+ bool transaction::
+ has_current ()
+ {
+ return tls_get (current_transaction) != 0;
+ }
+
+ transaction& transaction::
+ current ()
+ {
+ transaction* cur (tls_get (current_transaction));
+
+ if (cur == 0)
+ throw not_in_transaction ();
+
+ return *cur;
+ }
+
+ void transaction::
+ current (transaction& t)
+ {
+ tls_set (current_transaction, &t);
+ }
+
+ void transaction::
+ reset_current ()
+ {
+ transaction* t (0);
+ tls_set (current_transaction, t);
+ }
+
+ struct rollback_guard
+ {
+ rollback_guard (transaction& t): t_ (&t) {}
+ ~rollback_guard ()
+ {if (t_ != 0) t_->callback_call (transaction::event_rollback);}
+ void release () {t_ = 0;}
+ private:
+ transaction* t_;
+ };
+
+ void transaction::
+ commit ()
+ {
+ if (finalized_)
+ throw transaction_already_finalized ();
+
+ finalized_ = true;
+ rollback_guard rg (*this);
+
+ impl_->tracer (0);
+
+ if (tls_get (current_transaction) == this)
+ {
+ transaction* t (0);
+ tls_set (current_transaction, t);
+ }
+
+ impl_->commit ();
+ rg.release ();
+
+ if (callback_count_ != 0)
+ callback_call (event_commit);
+ }
+
+ void transaction::
+ rollback ()
+ {
+ if (finalized_)
+ throw transaction_already_finalized ();
+
+ finalized_ = true;
+ rollback_guard rg (*this);
+
+ impl_->tracer (0);
+
+ if (tls_get (current_transaction) == this)
+ {
+ transaction* t (0);
+ tls_set (current_transaction, t);
+ }
+
+ impl_->rollback ();
+ rg.release ();
+
+ if (callback_count_ != 0)
+ callback_call (event_rollback);
+ }
+
+ void transaction::
+ callback_call (unsigned short event)
+ {
+ size_t stack_count (callback_count_ < stack_callback_count
+ ? callback_count_ : stack_callback_count);
+ size_t dyn_count (callback_count_ - stack_count);
+
+ // We need to be careful with the situation where a callback
+ // throws and we neither call the rest of the callbacks nor
+ // reset their states. To make sure this doesn't happen, we
+ // do a first pass and reset all the states.
+ //
+ for (size_t i (0); i < stack_count; ++i)
+ {
+ callback_data& d (stack_callbacks_[i]);
+ if (d.event != 0 && d.state != 0)
+ *d.state = 0;
+ }
+
+ for (size_t i (0); i < dyn_count; ++i)
+ {
+ callback_data& d (dyn_callbacks_[i]);
+ if (d.event != 0 && d.state != 0)
+ *d.state = 0;
+ }
+
+ // Now do the actual calls.
+ //
+ for (size_t i (0); i < stack_count; ++i)
+ {
+ callback_data& d (stack_callbacks_[i]);
+ if (d.event & event)
+ d.func (event, d.key, d.data);
+ }
+
+ for (size_t i (0); i < dyn_count; ++i)
+ {
+ callback_data& d (dyn_callbacks_[i]);
+ if (d.event & event)
+ d.func (event, d.key, d.data);
+ }
+
+ // Clean things up in case this instance is going to be reused.
+ //
+ if (dyn_count != 0)
+ dyn_callbacks_.clear ();
+
+ free_callback_ = max_callback_count;
+ callback_count_ = 0;
+ }
+
+ void transaction::
+ callback_register (callback_type func,
+ void* key,
+ unsigned short event,
+ unsigned long long data,
+ transaction** state)
+ {
+ callback_data* s;
+
+ // If we have a free slot, use it.
+ //
+ if (free_callback_ != max_callback_count)
+ {
+ s = (free_callback_ < stack_callback_count)
+ ? stack_callbacks_ + free_callback_
+ : &dyn_callbacks_[free_callback_ - stack_callback_count];
+
+ free_callback_ = reinterpret_cast<size_t> (s->key);
+ }
+ // If we have space in the stack, grab that.
+ //
+ else if (callback_count_ < stack_callback_count)
+ {
+ s = stack_callbacks_ + callback_count_;
+ callback_count_++;
+ }
+ // Otherwise use the dynamic storage.
+ //
+ else
+ {
+ dyn_callbacks_.push_back (callback_data ());
+ s = &dyn_callbacks_.back ();
+ callback_count_++;
+ }
+
+ s->func = func;
+ s->key = key;
+ s->event = event;
+ s->data = data;
+ s->state = state;
+ }
+
+ size_t transaction::
+ callback_find (void* key)
+ {
+ if (callback_count_ == 0)
+ return 0;
+
+ size_t stack_count;
+
+ // See if this is the last slot registered. This will be a fast path if,
+ // for example, things are going to be unregistered from destructors.
+ //
+ if (callback_count_ <= stack_callback_count)
+ {
+ if (stack_callbacks_[callback_count_ - 1].key == key)
+ return callback_count_ - 1;
+
+ stack_count = callback_count_;
+ }
+ else
+ {
+ if (dyn_callbacks_.back ().key == key)
+ return callback_count_ - 1;
+
+ stack_count = stack_callback_count;
+ }
+
+ // Otherwise do a linear search.
+ //
+ for (size_t i (0); i < stack_count; ++i)
+ if (stack_callbacks_[i].key == key)
+ return i;
+
+ for (size_t i (0), dyn_count (callback_count_ - stack_count);
+ i < dyn_count; ++i)
+ if (dyn_callbacks_[i].key == key)
+ return i + stack_callback_count;
+
+ return callback_count_;
+ }
+
+ void transaction::
+ callback_unregister (void* key)
+ {
+ size_t i (callback_find (key));
+
+ // It is ok for this function not to find the key.
+ //
+ if (i == callback_count_)
+ return;
+
+ // See if this is the last slot registered.
+ //
+ if (i == callback_count_ - 1)
+ {
+ if (i >= stack_callback_count)
+ dyn_callbacks_.pop_back ();
+
+ callback_count_--;
+ }
+ else
+ {
+ callback_data& d (
+ i < stack_callback_count
+ ? stack_callbacks_[i]
+ : dyn_callbacks_[i - stack_callback_count]);
+
+ // Add to the free list.
+ //
+ d.event = 0;
+ d.key = reinterpret_cast<void*> (free_callback_);
+ free_callback_ = i;
+ }
+ }
+
+ void transaction::
+ callback_update (void* key,
+ unsigned short event,
+ unsigned long long data,
+ transaction** state)
+ {
+ size_t i (callback_find (key));
+
+ // It is ok for this function not to find the key.
+ //
+ if (i == callback_count_)
+ return;
+
+ callback_data& d (
+ i < stack_callback_count
+ ? stack_callbacks_[i]
+ : dyn_callbacks_[i - stack_callback_count]);
+
+ d.event = event;
+ d.data = data;
+ d.state = state;
+ }
+
+ //
+ // transaction_impl
+ //
+
+ transaction_impl::
+ ~transaction_impl ()
+ {
+ }
+
+ connection& transaction_impl::
+ connection (database_type* db)
+ {
+ assert (db == 0 || db == &database_);
+ return *connection_;
+ }
+
+ // The transaction-specific tracer is stored in the connection. See the
+ // connection class for the reason.
+ //
+ void transaction_impl::
+ tracer (tracer_type* t)
+ {
+ connection_->transaction_tracer_ = t;
+ }
+
+ tracer* transaction_impl::
+ tracer () const
+ {
+ return connection_->transaction_tracer_;
+ }
+}