diff options
author | Boris Kolpackov <boris@codesynthesis.com> | 2013-01-16 16:31:29 +0200 |
---|---|---|
committer | Boris Kolpackov <boris@codesynthesis.com> | 2013-01-16 16:31:29 +0200 |
commit | 856a438b33184959b64864f1afdf5a6a2fd6b0d2 (patch) | |
tree | b1fd1d9c616262d8d62e4431469f74e5e74d4fe1 | |
parent | 5cf30ccfe764701f549e4152ad312187221f5285 (diff) |
Make session cache management functions static, add notifications
-rw-r--r-- | common/session/custom/driver.cxx | 20 | ||||
-rw-r--r-- | common/session/custom/session.cxx | 23 | ||||
-rw-r--r-- | common/session/custom/session.hxx | 64 | ||||
-rw-r--r-- | common/session/custom/session.txx | 92 |
4 files changed, 131 insertions, 68 deletions
diff --git a/common/session/custom/driver.cxx b/common/session/custom/driver.cxx index 1ec7cbb..0a58ea5 100644 --- a/common/session/custom/driver.cxx +++ b/common/session/custom/driver.cxx @@ -145,10 +145,10 @@ main (int argc, char* argv[]) transaction t (db->begin ()); tracer.count = 0; t.tracer (tracer); - s.flush (*db); // Flush all the changes. + s.flush (*db); assert (tracer.count == 2); t.commit (); - s.mark (); // Mark all the changed objects as unchanged. + s.mark (); } { @@ -159,6 +159,22 @@ main (int argc, char* argv[]) 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 (); + s.mark (); + } } catch (const odb::exception& e) { diff --git a/common/session/custom/session.cxx b/common/session/custom/session.cxx index b50493f..f8cd102 100644 --- a/common/session/custom/session.cxx +++ b/common/session/custom/session.cxx @@ -6,33 +6,20 @@ #include "session.hxx" -static session* current_; // Use TLS in multi-threaded applications. +session* session::current; session:: session () { - assert (current_ == 0); - current_ = this; + assert (current == 0); + current = this; } session:: ~session () { - assert (current_ == this); - current_ = 0; -} - -bool session:: -has_current () -{ - return current_ != 0; -} - -session& session:: -current () -{ - assert (current_ != 0); - return *current_; + assert (current == this); + current = 0; } void session:: diff --git a/common/session/custom/session.hxx b/common/session/custom/session.hxx index bb60a4b..3b1789a 100644 --- a/common/session/custom/session.hxx +++ b/common/session/custom/session.hxx @@ -26,14 +26,13 @@ private: session (const session&); session& operator= (const session&); - // Current session interface. + // Session for the current thread. This can be implemented in pretty + // much any way that makes sense to the application. It can be a global + // session as we have here. In multi-threaded applications we could use + // TLS instead. // public: - static bool - has_current (); - - static session& - current (); + static session* current; // Change tracking interface. // @@ -61,23 +60,30 @@ private: mark () = 0; }; + enum object_state + { + tracking, // Tracking any modifications by storing the original copy. + changed, // Known to be changed. + flushed // Flushed but not yet committed/rolled back. + }; + template <typename T> - struct object_state + struct object_data { typedef typename odb::object_traits<T>::pointer_type pointer_type; explicit - object_state (pointer_type o): obj (o), flushed_ (false) {} + object_data (pointer_type o): obj (o), state (tracking) {} pointer_type obj; pointer_type orig; - bool flushed_; + object_state state; }; template <typename T> struct object_map: object_map_base, std::map<typename odb::object_traits<T>::id_type, - object_state<T> > + object_data<T> > { virtual void flush (odb::database&); @@ -95,37 +101,53 @@ public: typedef object_map<T> map; typedef typename map::iterator iterator; - position () {} + position (): map_ (0) {} position (map& m, const iterator& p): map_ (&m), pos_ (p) {} map* map_; iterator pos_; }; + // Cache management. + // template <typename T> - position<T> + static position<T> insert (odb::database&, const typename odb::object_traits<T>::id_type&, const typename odb::object_traits<T>::pointer_type&); template <typename T> + static typename odb::object_traits<T>::pointer_type + find (odb::database&, const typename odb::object_traits<T>::id_type&); + + template <typename T> + static void + erase (const position<T>& p) + { + if (p.map_ != 0) + p.map_->erase (p.pos_); + } + + // Notifications. + // + template <typename T> static void - initialize (const position<T>&); + persist (const position<T>& p) + { + load (p); + } template <typename T> - typename odb::object_traits<T>::pointer_type - find (odb::database&, const typename odb::object_traits<T>::id_type&) const; + static void + load (const position<T>&); template <typename T> - void - erase (odb::database&, const typename odb::object_traits<T>::id_type&); + static void + update (odb::database&, const T&); template <typename T> static void - erase (const position<T>& p) - { - p.map_->erase (p.pos_); - } + erase (odb::database&, const typename odb::object_traits<T>::id_type&); private: typedef std::map<const std::type_info*, diff --git a/common/session/custom/session.txx b/common/session/custom/session.txx index 203b4e3..fd90b6a 100644 --- a/common/session/custom/session.txx +++ b/common/session/custom/session.txx @@ -12,14 +12,17 @@ insert (odb::database&, { typedef odb::object_traits<T> object_traits; - std::shared_ptr<object_map_base>& pm (map_[&typeid (T)]); + if (current == 0) + return position<T> (); // No session, return empty position. + + std::shared_ptr<object_map_base>& pm (current->map_[&typeid (T)]); if (!pm) pm.reset (new object_map<T>); object_map<T>& m (static_cast<object_map<T>&> (*pm)); - typename object_map<T>::value_type vt (id, object_state<T> (obj)); + typename object_map<T>::value_type vt (id, object_data<T> (obj)); std::pair<typename object_map<T>::iterator, bool> r (m.insert (vt)); // We shall never try to re-insert the same object into the cache. @@ -30,11 +33,37 @@ insert (odb::database&, } template <typename T> +typename odb::object_traits<T>::pointer_type session:: +find (odb::database&, const typename odb::object_traits<T>::id_type& id) +{ + typedef typename odb::object_traits<T>::pointer_type pointer_type; + + if (current == 0) + return pointer_type (); // No session, return NULL pointer. + + type_map::const_iterator ti (current->map_.find (&typeid (T))); + + if (ti == current->map_.end ()) + return pointer_type (); + + const object_map<T>& m (static_cast<const object_map<T>&> (*ti->second)); + typename object_map<T>::const_iterator oi (m.find (id)); + + if (oi == m.end ()) + return pointer_type (); + + return oi->second.obj; +} + +template <typename T> void session:: -initialize (const position<T>& p) +load (const position<T>& p) { typedef typename odb::object_traits<T>::pointer_type pointer_type; + if (p.map_ == 0) + return; // Empty position. + // Make a copy for change tracking. If our object model had a // polymorphic hierarchy, then we would have had to use a // virtual function-based mechanism (e.g., clone()) instead of @@ -45,32 +74,45 @@ initialize (const position<T>& p) } template <typename T> -typename odb::object_traits<T>::pointer_type session:: -find (odb::database&, const typename odb::object_traits<T>::id_type& id) const +void session:: +update (odb::database&, const T& obj) { - typedef typename odb::object_traits<T>::pointer_type pointer_type; + typedef odb::object_traits<T> object_traits; + typedef typename object_traits::pointer_type pointer_type; - type_map::const_iterator ti (map_.find (&typeid (T))); + if (current == 0) + return; // No session. - if (ti == map_.end ()) - return pointer_type (); + // User explicitly updated the object by calling database::update(). + // Change the state to flushed and reset the original copy (we are + // still tracking changes after the update). + // + type_map::iterator ti (current->map_.find (&typeid (T))); - const object_map<T>& m (static_cast<const object_map<T>&> (*ti->second)); - typename object_map<T>::const_iterator oi (m.find (id)); + if (ti == current->map_.end ()) + return; // This object is not in the session. + + object_map<T>& m (static_cast<object_map<T>&> (*ti->second)); + typename object_map<T>::iterator oi (m.find (object_traits::id (obj))); if (oi == m.end ()) - return pointer_type (); + return; // This object is not in the session. - return oi->second.obj; + object_data<T>& d (oi->second); + d.orig = pointer_type (new T (*d.obj)); + d.state = flushed; } template <typename T> void session:: erase (odb::database&, const typename odb::object_traits<T>::id_type& id) { - type_map::iterator ti (map_.find (&typeid (T))); + if (current == 0) + return; // No session. - if (ti == map_.end ()) + type_map::iterator ti (current->map_.find (&typeid (T))); + + if (ti == current->map_.end ()) return; object_map<T>& m (static_cast<object_map<T>&> (*ti->second)); @@ -82,7 +124,7 @@ erase (odb::database&, const typename odb::object_traits<T>::id_type& id) m.erase (oi); if (m.empty ()) - map_.erase (ti); + current->map_.erase (ti); } template <typename T> @@ -92,13 +134,10 @@ flush (odb::database& db) for (typename object_map<T>::iterator i (this->begin ()), e (this->end ()); i != e; ++i) { - const T& obj (*i->second.obj); + object_data<T>& d (i->second); - if (obj.changed (*i->second.orig)) - { - db.update (obj); - i->second.flushed_ = true; - } + if (d.state == changed || d.obj->changed (*d.orig)) + db.update (d.obj); // State changed by the update() notification. } } @@ -109,10 +148,9 @@ mark () for (typename object_map<T>::iterator i (this->begin ()), e (this->end ()); i != e; ++i) { - if (i->second.flushed_) - { - i->second.orig.reset (new T (*i->second.obj)); - i->second.flushed_ = false; - } + object_data<T>& d (i->second); + + if (d.state == flushed) + d.state = tracking; } } |