aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBoris Kolpackov <boris@codesynthesis.com>2013-01-18 10:23:39 +0200
committerBoris Kolpackov <boris@codesynthesis.com>2013-01-18 10:23:39 +0200
commitd304a4b2b19dca115954f209d6a37a6958e73ab9 (patch)
tree0822a40a7d44e7921076b6ad458d29e3ad91c525
parent856a438b33184959b64864f1afdf5a6a2fd6b0d2 (diff)
Add support for post-commit/rollback callbacks
New test: common/transaction/callback.
-rw-r--r--common/makefile3
-rw-r--r--common/session/custom/driver.cxx201
-rw-r--r--common/session/custom/session.cxx28
-rw-r--r--common/session/custom/session.hxx27
-rw-r--r--common/session/custom/session.txx11
-rw-r--r--common/transaction/basics/driver.cxx (renamed from common/transaction/driver.cxx)4
-rw-r--r--common/transaction/basics/makefile (renamed from common/transaction/makefile)12
-rw-r--r--common/transaction/basics/test.std (renamed from common/transaction/test.std)0
-rw-r--r--common/transaction/callback/driver.cxx212
-rw-r--r--common/transaction/callback/makefile89
-rw-r--r--common/transaction/callback/test.std34
11 files changed, 513 insertions, 108 deletions
diff --git a/common/makefile b/common/makefile
index 8c93f72..9e6e319 100644
--- a/common/makefile
+++ b/common/makefile
@@ -40,7 +40,8 @@ relationship-query \
schema \
session/cache \
template \
-transaction \
+transaction/basics \
+transaction/callback \
types \
view \
virtual \
diff --git a/common/session/custom/driver.cxx b/common/session/custom/driver.cxx
index 0a58ea5..1b00ada 100644
--- a/common/session/custom/driver.cxx
+++ b/common/session/custom/driver.cxx
@@ -30,16 +30,14 @@ using odb::transaction;
struct counting_tracer: odb::tracer
{
virtual void
- execute (odb::connection&, const char*)
- {
- count++;
- }
-
+ execute (odb::connection&, const char*) {count++;}
size_t count;
};
static counting_tracer tracer;
+struct failed {};
+
int
main (int argc, char* argv[])
{
@@ -81,99 +79,138 @@ main (int argc, char* argv[])
t.commit ();
}
- session s;
- shared_ptr<employer> st, cs;
- shared_ptr<employee> ste, cse;
-
{
- transaction t (db->begin ());
+ session s;
+ shared_ptr<employer> st, cs;
+ shared_ptr<employee> ste, cse;
- st = db->load<employer> ("Simple Tech Ltd");
- ste = db->load<employee> (st->employees ()[0].object_id ());
+ {
+ transaction t (db->begin ());
- // Test object cache.
- //
- shared_ptr<employee> e (st->employees ()[0].load ());
- assert (ste->employer () == st);
- assert (ste == e);
+ st = db->load<employer> ("Simple Tech Ltd");
+ ste = db->load<employee> (st->employees ()[0].object_id ());
- t.commit ();
- }
+ // Test object cache.
+ //
+ shared_ptr<employee> e (st->employees ()[0].load ());
+ assert (ste->employer () == st);
+ assert (ste == e);
- {
- transaction t (db->begin ());
+ t.commit ();
+ }
- cs = db->load<employer> ("Complex Systems Inc");
- cse = db->load<employee> (cs->employees ()[0].object_id ());
- cs->employees ()[0].load ();
+ {
+ transaction t (db->begin ());
- t.commit ();
- }
+ cs = db->load<employer> ("Complex Systems Inc");
+ cse = db->load<employee> (cs->employees ()[0].object_id ());
+ cs->employees ()[0].load ();
- cs->symbol ("CSI");
-
- // Swap employees.
- //
- ste->employer (cs);
- cse->employer (st);
- st->employees ()[0] = cse;
- cs->employees ()[0] = ste;
+ t.commit ();
+ }
- {
- transaction t (db->begin ());
- tracer.count = 0;
- t.tracer (tracer);
- s.flush (*db); // Flush all the changes.
- assert (tracer.count == 3);
- t.commit ();
- s.mark (); // Mark all the changed objects as unchanged.
- }
+ cs->symbol ("CSI");
- {
- transaction t (db->begin ());
- tracer.count = 0;
- t.tracer (tracer);
- s.flush (*db);
- assert (tracer.count == 0);
- t.commit ();
- }
-
- cs->symbol ("COMP");
- st->symbol ("SMPL");
-
- {
- transaction t (db->begin ());
- tracer.count = 0;
- t.tracer (tracer);
- s.flush (*db);
- assert (tracer.count == 2);
- t.commit ();
- s.mark ();
- }
-
- {
- transaction t (db->begin ());
- tracer.count = 0;
- t.tracer (tracer);
- s.flush (*db);
- assert (tracer.count == 0);
- t.commit ();
+ // Swap employees.
+ //
+ ste->employer (cs);
+ cse->employer (st);
+ st->employees ()[0] = cse;
+ cs->employees ()[0] = ste;
+
+ {
+ transaction t (db->begin ());
+ tracer.count = 0;
+ t.tracer (tracer);
+ s.flush (*db);
+ assert (tracer.count == 3);
+ t.commit ();
+ }
+
+ {
+ transaction t (db->begin ());
+ tracer.count = 0;
+ t.tracer (tracer);
+ s.flush (*db);
+ assert (tracer.count == 0);
+ t.commit ();
+ }
+
+ cs->symbol ("COMP");
+ st->symbol ("SMPL");
+
+ {
+ transaction t (db->begin ());
+ tracer.count = 0;
+ t.tracer (tracer);
+ s.flush (*db);
+ assert (tracer.count == 2);
+ t.commit ();
+ }
+
+ {
+ transaction t (db->begin ());
+ tracer.count = 0;
+ t.tracer (tracer);
+ s.flush (*db);
+ assert (tracer.count == 0);
+ t.commit ();
+ }
+
+ // Explicit update.
+ //
+ cs->symbol ("CS");
+ st->symbol ("ST");
+
+ {
+ transaction t (db->begin ());
+ db->update (cs);
+ tracer.count = 0;
+ t.tracer (tracer);
+ s.flush (*db);
+ assert (tracer.count == 1);
+ t.commit ();
+ }
+
+ // Rollback after update.
+ //
+ cs->symbol ("CSI");
+
+ try
+ {
+ transaction t (db->begin ());
+ tracer.count = 0;
+ t.tracer (tracer);
+ s.flush (*db);
+ assert (tracer.count == 1);
+ throw failed ();
+ t.commit ();
+ }
+ catch (const failed&)
+ {
+ transaction t (db->begin ());
+ tracer.count = 0;
+ t.tracer (tracer);
+ s.flush (*db);
+ assert (tracer.count == 1);
+ t.commit ();
+ }
}
- // Explicit update.
+ // Test session destruction before transaction is commited.
//
- cs->symbol ("CS");
- st->symbol ("ST");
-
{
transaction t (db->begin ());
- db->update (cs);
- tracer.count = 0;
- t.tracer (tracer);
- s.flush (*db);
- assert (tracer.count == 1);
+ {
+ session s;
+ shared_ptr<employer> st (db->load<employer> ("Simple Tech Ltd"));
+ st->symbol ("STL");
+ tracer.count = 0;
+ t.tracer (tracer);
+ s.flush (*db);
+ assert (tracer.count == 1);
+ }
t.commit ();
- s.mark ();
}
}
catch (const odb::exception& e)
diff --git a/common/session/custom/session.cxx b/common/session/custom/session.cxx
index f8cd102..0009d79 100644
--- a/common/session/custom/session.cxx
+++ b/common/session/custom/session.cxx
@@ -10,6 +10,7 @@ session* session::current;
session::
session ()
+ : tran_ (0)
{
assert (current == 0);
current = this;
@@ -18,6 +19,11 @@ session ()
session::
~session ()
{
+ // Unregister from transaction.
+ //
+ if (tran_ != 0)
+ tran_->unregister (this);
+
assert (current == this);
current = 0;
}
@@ -25,13 +31,27 @@ session::
void session::
flush (odb::database& db)
{
+ bool flushed (false);
+
for (type_map::iterator i (map_.begin ()), e (map_.end ()); i != e; ++i)
- i->second->flush (db);
+ {
+ bool r (i->second->flush (db));
+ flushed = flushed || r;
+ }
+
+ // If we flushed anything, then register the post-commit/rollback callback.
+ //
+ if (flushed)
+ {
+ tran_ = &odb::transaction::current ();
+ tran_->register_ (&mark, this, odb::transaction::event_all, 0, &tran_);
+ }
}
void session::
-mark ()
+mark (unsigned short event, void* key, unsigned long long)
{
- for (type_map::iterator i (map_.begin ()), e (map_.end ()); i != e; ++i)
- i->second->mark ();
+ session& s (*static_cast<session*> (key));
+ for (type_map::iterator i (s.map_.begin ()), e (s.map_.end ()); i != e; ++i)
+ i->second->mark (event);
}
diff --git a/common/session/custom/session.hxx b/common/session/custom/session.hxx
index 3b1789a..ce7e43c 100644
--- a/common/session/custom/session.hxx
+++ b/common/session/custom/session.hxx
@@ -10,6 +10,8 @@
#include <typeinfo>
#include <odb/database.hxx>
+#include <odb/transaction.hxx>
+
#include <odb/traits.hxx> // odb::object_traits
#include <odb/details/type-info.hxx> // odb::details::type_info_comparator
@@ -36,28 +38,26 @@ public:
// Change tracking interface.
//
+public:
// Call flush() within a transaction to apply the changes to the
- // database. Then after successfully committing the transaction,
- // call mark() to mark all the changed objects as again unchanged.
+ // database.
//
-public:
void
flush (odb::database&);
- void
- mark ();
-
private:
struct object_map_base
{
virtual
~object_map_base () {}
- virtual void
+ // Return true we flushed anything.
+ //
+ virtual bool
flush (odb::database&) = 0;
virtual void
- mark () = 0;
+ mark (unsigned short event) = 0;
};
enum object_state
@@ -85,11 +85,11 @@ private:
std::map<typename odb::object_traits<T>::id_type,
object_data<T> >
{
- virtual void
+ virtual bool
flush (odb::database&);
virtual void
- mark ();
+ mark (unsigned short event);
};
// Object cache interface.
@@ -150,10 +150,17 @@ public:
erase (odb::database&, const typename odb::object_traits<T>::id_type&);
private:
+ // Post-commit/rollback callback.
+ //
+ static void
+ mark (unsigned short event, void* key, unsigned long long);
+
+private:
typedef std::map<const std::type_info*,
std::shared_ptr<object_map_base>,
odb::details::type_info_comparator> type_map;
type_map map_;
+ odb::transaction* tran_;
};
#include "session.txx"
diff --git a/common/session/custom/session.txx b/common/session/custom/session.txx
index fd90b6a..31c08c7 100644
--- a/common/session/custom/session.txx
+++ b/common/session/custom/session.txx
@@ -128,9 +128,10 @@ erase (odb::database&, const typename odb::object_traits<T>::id_type& id)
}
template <typename T>
-void session::object_map<T>::
+bool session::object_map<T>::
flush (odb::database& db)
{
+ bool r (false);
for (typename object_map<T>::iterator i (this->begin ()), e (this->end ());
i != e; ++i)
{
@@ -138,12 +139,16 @@ flush (odb::database& db)
if (d.state == changed || d.obj->changed (*d.orig))
db.update (d.obj); // State changed by the update() notification.
+
+ r = r || d.state == flushed;
}
+
+ return r;
}
template <typename T>
void session::object_map<T>::
-mark ()
+mark (unsigned short event)
{
for (typename object_map<T>::iterator i (this->begin ()), e (this->end ());
i != e; ++i)
@@ -151,6 +156,6 @@ mark ()
object_data<T>& d (i->second);
if (d.state == flushed)
- d.state = tracking;
+ d.state = event == odb::transaction::event_commit ? tracking : changed;
}
}
diff --git a/common/transaction/driver.cxx b/common/transaction/basics/driver.cxx
index f785870..984e9e8 100644
--- a/common/transaction/driver.cxx
+++ b/common/transaction/basics/driver.cxx
@@ -1,8 +1,8 @@
-// file : common/transaction/driver.cxx
+// file : common/transaction/basics/driver.cxx
// copyright : Copyright (c) 2009-2012 Code Synthesis Tools CC
// license : GNU GPL v2; see accompanying LICENSE file
-// Test transaction operations.
+// Test basic transaction operations.
//
#include <string>
diff --git a/common/transaction/makefile b/common/transaction/basics/makefile
index 516990c..dbee915 100644
--- a/common/transaction/makefile
+++ b/common/transaction/basics/makefile
@@ -1,8 +1,8 @@
-# file : common/transaction/makefile
+# file : common/transaction/basics/makefile
# copyright : Copyright (c) 2009-2012 Code Synthesis Tools CC
# license : GNU GPL v2; see accompanying LICENSE file
-include $(dir $(lastword $(MAKEFILE_LIST)))../../build/bootstrap.make
+include $(dir $(lastword $(MAKEFILE_LIST)))../../../build/bootstrap.make
cxx_tun := driver.cxx
cxx_obj := $(addprefix $(out_base)/,$(cxx_tun:.cxx=.o))
@@ -40,10 +40,10 @@ $(dist): export extra_dist := $(data_dist) $(call vc9projs,$(name)) \
$(call vc10projs,$(name)) $(call vc11projs,$(name))
$(dist):
$(call dist-data,$(sources) $(headers) $(data_dist))
- $(call meta-automake,../template/Makefile.am)
- $(call meta-vc9projs,../template/template,$(name))
- $(call meta-vc10projs,../template/template,$(name))
- $(call meta-vc11projs,../template/template,$(name))
+ $(call meta-automake,../../template/Makefile.am)
+ $(call meta-vc9projs,../../template/template,$(name))
+ $(call meta-vc10projs,../../template/template,$(name))
+ $(call meta-vc11projs,../../template/template,$(name))
# Test.
#
diff --git a/common/transaction/test.std b/common/transaction/basics/test.std
index 37d3598..37d3598 100644
--- a/common/transaction/test.std
+++ b/common/transaction/basics/test.std
diff --git a/common/transaction/callback/driver.cxx b/common/transaction/callback/driver.cxx
new file mode 100644
index 0000000..b74df4c
--- /dev/null
+++ b/common/transaction/callback/driver.cxx
@@ -0,0 +1,212 @@
+// file : common/transaction/callback/driver.cxx
+// copyright : Copyright (c) 2009-2012 Code Synthesis Tools CC
+// license : GNU GPL v2; see accompanying LICENSE file
+
+// Test transaction callbacks.
+//
+
+#include <cstddef> // std::size_t
+#include <cassert>
+#include <iostream>
+
+#include <odb/database.hxx>
+#include <odb/transaction.hxx>
+
+#include <common/common.hxx>
+
+using namespace std;
+using namespace odb::core;
+
+struct callback
+{
+ callback (unsigned short v): v_ (v), t_ (0) {}
+ callback (unsigned short v, transaction& t): v_ (v), t_ (0) {register_ (t);}
+ ~callback () {if (t_ != 0) unregister ();}
+
+ void
+ register_ (transaction& t)
+ {
+ t_ = &t;
+ t.register_ (&func, this, transaction::event_all, v_, &t_);
+ }
+
+ void
+ unregister ()
+ {
+ cout << " unregister callback " << v_ << endl;
+ t_->unregister (this);
+ t_ = 0;
+ }
+
+private:
+ static void
+ func (unsigned short event, void* key, unsigned long long data)
+ {
+ callback& c (*static_cast<callback*> (key));
+
+ const char* en;
+ switch (event)
+ {
+ case transaction::event_commit:
+ en = "commit";
+ break;
+ case transaction::event_rollback:
+ en = "rollback";
+ break;
+ default:
+ en = "unknown";
+ }
+
+ cout << " callback " << c.v_ << " " << en << endl;
+
+ assert (data == c.v_);
+ assert (c.t_ == 0);
+ }
+
+ unsigned short v_;
+ transaction* t_;
+};
+
+struct failed {};
+
+static void
+throw_func (unsigned short, void*, unsigned long long)
+{
+ throw failed ();
+}
+
+static void
+dummy_func (unsigned short, void* key, unsigned long long data)
+{
+ assert (reinterpret_cast<unsigned long long> (key) == data);
+}
+
+static void
+fill (transaction& t)
+{
+ // 20 is from odb/transaction.hxx.
+ //
+ for (size_t i (0); i < 20; ++i)
+ t.register_ (&dummy_func,
+ reinterpret_cast<void*> (i),
+ transaction::event_all,
+ i);
+}
+
+int
+main (int argc, char* argv[])
+{
+ try
+ {
+ auto_ptr<database> db (create_database (argc, argv, false));
+
+ // We want to test both stack and dynamic slots.
+ //
+ for (unsigned short i (1); i < 3; ++i)
+ {
+ // Test basic logic.
+ //
+ cout << "test " << i << "/001" << endl;
+
+ // Commit callback.
+ //
+ {
+ transaction t (db->begin ());
+ if (i == 2) fill (t);
+ callback c1 (1, t);
+ t.commit ();
+ }
+
+ // Rollback callback.
+ //
+ {
+ transaction t (db->begin ());
+ if (i == 2) fill (t);
+ callback c1 (1, t);
+ t.rollback ();
+ }
+
+ // Rollback via exception callback.
+ //
+ {
+ callback c1 (1);
+
+ try
+ {
+ transaction t (db->begin ());
+ if (i == 2) fill (t);
+ c1.register_ (t);
+ throw failed ();
+ }
+ catch (const failed&)
+ {
+ }
+ }
+
+ // Unregister callback at the end.
+ //
+ {
+ transaction t (db->begin ());
+ if (i == 2) fill (t);
+ callback c1 (1, t);
+ c1.unregister ();
+ t.unregister (&c1); // Test unregistering non-registered key.
+ t.commit ();
+ }
+
+ {
+ transaction t (db->begin ());
+ if (i == 2) fill (t);
+ callback c1 (1, t);
+ c1.unregister ();
+ callback c2 (2, t);
+ t.commit ();
+ }
+
+ // Unregister callback in the middle.
+ //
+ cout << "test " << i << "/002" << endl;
+ {
+ transaction t (db->begin ());
+ if (i == 2) fill (t);
+ callback c1 (1, t);
+ callback c2 (2, t);
+ callback c3 (3, t);
+ c2.unregister ();
+ t.commit ();
+ }
+
+ {
+ transaction t (db->begin ());
+ if (i == 2) fill (t);
+ callback c1 (1, t);
+ callback c2 (2, t);
+ callback c3 (3, t);
+ c2.unregister ();
+ callback c4 (4, t); // Using the free slot.
+ t.commit ();
+ }
+
+ // Test a callback in the middle that throws.
+ //
+ cout << "test " << i << "/003" << endl;
+ try
+ {
+ transaction t (db->begin ());
+ if (i == 2) fill (t);
+ callback c1 (1, t);
+ t.register_ (&throw_func, 0);
+ callback c2 (2, t);
+ t.commit ();
+ }
+ catch (const failed&)
+ {
+ }
+ }
+ }
+ catch (const odb::exception& e)
+ {
+ cerr << e.what () << endl;
+ return 1;
+ }
+}
diff --git a/common/transaction/callback/makefile b/common/transaction/callback/makefile
new file mode 100644
index 0000000..936a02d
--- /dev/null
+++ b/common/transaction/callback/makefile
@@ -0,0 +1,89 @@
+# file : common/transaction/callback/makefile
+# copyright : Copyright (c) 2009-2012 Code Synthesis Tools CC
+# license : GNU GPL v2; see accompanying LICENSE file
+
+include $(dir $(lastword $(MAKEFILE_LIST)))../../../build/bootstrap.make
+
+cxx_tun := driver.cxx
+cxx_obj := $(addprefix $(out_base)/,$(cxx_tun:.cxx=.o))
+cxx_od := $(cxx_obj:.o=.o.d)
+
+common.l := $(out_root)/libcommon/common/common.l
+common.l.cpp-options := $(out_root)/libcommon/common/common.l.cpp-options
+
+driver := $(out_base)/driver
+dist := $(out_base)/.dist
+test := $(out_base)/.test
+clean := $(out_base)/.clean
+
+# Build.
+#
+$(driver): $(cxx_obj) $(common.l)
+$(cxx_obj) $(cxx_od): cpp_options := -I$(out_base) -I$(src_base)
+$(cxx_obj) $(cxx_od): $(common.l.cpp-options)
+
+$(call include-dep,$(cxx_od))
+
+# Alias for default target.
+#
+$(out_base)/: $(driver)
+
+# Dist
+#
+name := $(subst /,-,$(subst $(src_root)/common/,,$(src_base)))
+
+$(dist): db_id := @database@
+$(dist): sources := $(cxx_tun)
+$(dist): data_dist := test.std
+$(dist): export name := $(name)
+$(dist): export extra_dist := $(data_dist) $(call vc9projs,$(name)) \
+$(call vc10projs,$(name)) $(call vc11projs,$(name))
+$(dist):
+ $(call dist-data,$(sources) $(headers) $(data_dist))
+ $(call meta-automake,../../template/Makefile.am)
+ $(call meta-vc9projs,../../template/template,$(name))
+ $(call meta-vc10projs,../../template/template,$(name))
+ $(call meta-vc11projs,../../template/template,$(name))
+
+# Test.
+#
+$(test): $(driver) $(src_base)/test.std
+ $(call message,test $<,$< --options-file $(dcf_root)/db.options \
+>$(out_base)/test.out)
+ $(call message,,diff -u $(src_base)/test.std $(out_base)/test.out)
+ $(call message,,rm -f $(out_base)/test.out)
+
+# Clean.
+#
+$(clean): \
+ $(driver).o.clean \
+ $(addsuffix .cxx.clean,$(cxx_obj)) \
+ $(addsuffix .cxx.clean,$(cxx_od))
+ $(call message,,rm -f $(out_base)/test.out)
+
+# Generated .gitignore.
+#
+ifeq ($(out_base),$(src_base))
+$(driver): | $(out_base)/.gitignore
+
+$(out_base)/.gitignore: files := driver
+$(clean): $(out_base)/.gitignore.clean
+
+$(call include,$(bld_root)/git/gitignore.make)
+endif
+
+# How to.
+#
+$(call include,$(bld_root)/dist.make)
+$(call include,$(bld_root)/meta/vc9proj.make)
+$(call include,$(bld_root)/meta/vc10proj.make)
+$(call include,$(bld_root)/meta/vc11proj.make)
+$(call include,$(bld_root)/meta/automake.make)
+
+$(call include,$(bld_root)/cxx/cxx-d.make)
+$(call include,$(bld_root)/cxx/cxx-o.make)
+$(call include,$(bld_root)/cxx/o-e.make)
+
+# Dependencies.
+#
+$(call import,$(src_root)/libcommon/makefile)
diff --git a/common/transaction/callback/test.std b/common/transaction/callback/test.std
new file mode 100644
index 0000000..f86579b
--- /dev/null
+++ b/common/transaction/callback/test.std
@@ -0,0 +1,34 @@
+test 1/001
+ callback 1 commit
+ callback 1 rollback
+ callback 1 rollback
+ unregister callback 1
+ unregister callback 1
+ callback 2 commit
+test 1/002
+ unregister callback 2
+ callback 1 commit
+ callback 3 commit
+ unregister callback 2
+ callback 1 commit
+ callback 4 commit
+ callback 3 commit
+test 1/003
+ callback 1 commit
+test 2/001
+ callback 1 commit
+ callback 1 rollback
+ callback 1 rollback
+ unregister callback 1
+ unregister callback 1
+ callback 2 commit
+test 2/002
+ unregister callback 2
+ callback 1 commit
+ callback 3 commit
+ unregister callback 2
+ callback 1 commit
+ callback 4 commit
+ callback 3 commit
+test 2/003
+ callback 1 commit