From 1e78bdc724e95898c04a3409b0b192aa7f77780b Mon Sep 17 00:00:00 2001 From: Boris Kolpackov Date: Mon, 15 Oct 2012 13:17:30 +0200 Subject: Implement early connection release --- odb/connection.cxx | 55 ++++++++++++++++++---- odb/connection.hxx | 29 +++++++++++- odb/connection.ixx | 6 ++- odb/forward.hxx | 5 ++ odb/no-id-object-result.hxx | 13 +----- odb/polymorphic-object-result.hxx | 13 +----- odb/polymorphic-object-result.txx | 4 +- odb/prepared-query.cxx | 28 ++++++++++++ odb/prepared-query.hxx | 96 ++++++++++++++++++++++++++++++++++++--- odb/result.cxx | 28 ++++++++++++ odb/result.hxx | 28 +++++++++++- odb/simple-object-result.hxx | 13 +----- odb/simple-object-result.txx | 7 ++- odb/statement.cxx | 9 ---- odb/statement.hxx | 16 +------ odb/view-result.hxx | 13 +----- 16 files changed, 269 insertions(+), 94 deletions(-) (limited to 'odb') diff --git a/odb/connection.cxx b/odb/connection.cxx index f479131..2686887 100644 --- a/odb/connection.cxx +++ b/odb/connection.cxx @@ -4,6 +4,8 @@ #include #include +#include +#include #include // prepared_* using namespace std; @@ -13,16 +15,45 @@ namespace odb connection:: ~connection () { + assert (prepared_queries_ == 0); + assert (prepared_map_.empty ()); + } + + void connection:: + clear_prepared_map () + { for (prepared_map_type::iterator i (prepared_map_.begin ()), e (prepared_map_.end ()); i != e; ++i) { if (i->second.params != 0) i->second.params_deleter (i->second.params); } + + prepared_map_.clear (); + } + + void connection:: + recycle () + { + while (prepared_queries_ != 0) + { + prepared_queries_->stmt.reset (); + prepared_queries_->list_remove (); + } } void connection:: - cache_query_ (details::shared_ptr pq, + invalidate_results () + { + while (results_ != 0) + { + results_->invalidate (); + results_->list_remove (); + } + } + + void connection:: + cache_query_ (prepared_query_impl* pq, const type_info& ti, void* params, const type_info* params_info, @@ -37,19 +68,25 @@ namespace odb prepared_entry_type& e (r.first->second); - e.prep_query = pq; - e.type_info = &ti; - - // Mark the statement as cached. + // Mark this prepared query as cached , get its ref count to 1 + // (prepared_query instances now reference this impl object), + // and remove it from the invalidation list. // - pq->stmt->cached (true); + pq->cached = true; + + while (pq->_ref_count () > 1) + pq->_dec_ref (); + pq->list_remove (); + + e.prep_query.reset (pq); + e.type_info = &ti; e.params = params; e.params_info = params_info; e.params_deleter = params_deleter; } - details::shared_ptr connection:: + prepared_query_impl* connection:: lookup_query_ (const char* name, const type_info& ti, void** params, @@ -72,7 +109,7 @@ namespace odb } if (i == prepared_map_.end ()) - return details::shared_ptr (); + return 0; // Make sure the types match. // @@ -87,6 +124,6 @@ namespace odb *params = i->second.params; } - return i->second.prep_query; + return i->second.prep_query.get (); } } diff --git a/odb/connection.hxx b/odb/connection.hxx index b4a9d5b..f1cb41a 100644 --- a/odb/connection.hxx +++ b/odb/connection.hxx @@ -123,6 +123,12 @@ namespace odb virtual ~connection (); + // Recycle the connection to be used by another thread. This call + // invalidates uncached prepared queries. + // + void + recycle (); + protected: connection (database_type&); @@ -132,13 +138,13 @@ namespace odb struct query_; virtual void - cache_query_ (details::shared_ptr pq, + cache_query_ (prepared_query_impl* pq, const std::type_info& ti, void* params, const std::type_info* params_info, void (*params_deleter) (void*)); - details::shared_ptr + prepared_query_impl* lookup_query_ (const char* name, const std::type_info& ti, void** params, // out @@ -170,10 +176,29 @@ namespace odb prepared_map_type prepared_map_; + void + clear_prepared_map (); + protected: database_type& database_; tracer_type* tracer_; + // Active query result list. + // + protected: + friend class result_impl; + result_impl* results_; + + void + invalidate_results (); + + // Prepared but uncached query list (cached ones are stored in + // prepared_map_). + // + protected: + friend class prepared_query_impl; + prepared_query_impl* prepared_queries_; + protected: friend class transaction; tracer_type* transaction_tracer_; diff --git a/odb/connection.ixx b/odb/connection.ixx index 8842d8e..bd91070 100644 --- a/odb/connection.ixx +++ b/odb/connection.ixx @@ -9,7 +9,11 @@ namespace odb { inline connection:: connection (database_type& database) - : database_ (database), tracer_ (0), transaction_tracer_ (0) + : database_ (database), + tracer_ (0), + results_ (0), + prepared_queries_ (0), + transaction_tracer_ (0) { } diff --git a/odb/forward.hxx b/odb/forward.hxx index ff4a531..653bdbb 100644 --- a/odb/forward.hxx +++ b/odb/forward.hxx @@ -115,6 +115,11 @@ namespace odb template struct no_op_reference_cache_traits; template struct reference_cache_traits; + // + // + class result_impl; + class prepared_query_impl; + // Polymorphism support. // template diff --git a/odb/no-id-object-result.hxx b/odb/no-id-object-result.hxx index 21be2dc..16df125 100644 --- a/odb/no-id-object-result.hxx +++ b/odb/no-id-object-result.hxx @@ -26,8 +26,6 @@ namespace odb class no_id_object_result_impl: public result_impl { protected: - typedef odb::database database_type; - // In result_impl, T is always non-const and the same as object_type. // typedef T object_type; @@ -44,15 +42,9 @@ namespace odb friend class object_result_iterator; protected: - no_id_object_result_impl (database_type& db) - : begin_ (true), end_ (false), db_ (db), current_ () - { - } - - database_type& - database () const + no_id_object_result_impl (connection& conn) + : result_impl (conn), begin_ (true), end_ (false), current_ () { - return db_; } // To make this work with all kinds of pointers (raw, std::auto_ptr, @@ -135,7 +127,6 @@ namespace odb load (); private: - database_type& db_; pointer_type current_; typename pointer_traits::guard guard_; }; diff --git a/odb/polymorphic-object-result.hxx b/odb/polymorphic-object-result.hxx index 5804db5..59592b1 100644 --- a/odb/polymorphic-object-result.hxx +++ b/odb/polymorphic-object-result.hxx @@ -26,8 +26,6 @@ namespace odb class polymorphic_object_result_impl: public result_impl { protected: - typedef odb::database database_type; - // In result_impl, T is always non-const and the same as object_type. // typedef T object_type; @@ -49,15 +47,9 @@ namespace odb friend class object_result_iterator; protected: - polymorphic_object_result_impl (database_type& db) - : begin_ (true), end_ (false), db_ (db), current_ () - { - } - - database_type& - database () const + polymorphic_object_result_impl (connection& conn) + : result_impl (conn), begin_ (true), end_ (false), current_ () { - return db_; } // To make this work with all kinds of pointers (raw, std::auto_ptr, @@ -167,7 +159,6 @@ namespace odb load (); private: - database_type& db_; pointer_type current_; typename pointer_traits::guard guard_; }; diff --git a/odb/polymorphic-object-result.txx b/odb/polymorphic-object-result.txx index 033b1ad..5d6c479 100644 --- a/odb/polymorphic-object-result.txx +++ b/odb/polymorphic-object-result.txx @@ -22,7 +22,7 @@ namespace odb const id_type& id (load_id ()); root_pointer_type rp ( - object_traits::pointer_cache_traits::find (database (), id)); + object_traits::pointer_cache_traits::find (this->db_, id)); if (!root_pointer_traits::null_ptr (rp)) { @@ -62,7 +62,7 @@ namespace odb typename object_traits::reference_cache_traits::insert_guard ig ( object_traits::reference_cache_traits::insert ( - res_->database (), res_->load_id (), obj)); + res_->db_, res_->load_id (), obj)); res_->load (&obj, false); ig.release (); } diff --git a/odb/prepared-query.cxx b/odb/prepared-query.cxx index d192541..e4c44a1 100644 --- a/odb/prepared-query.cxx +++ b/odb/prepared-query.cxx @@ -2,6 +2,7 @@ // copyright : Copyright (c) 2009-2012 Code Synthesis Tools CC // license : GNU GPL v2; see accompanying LICENSE file +#include #include namespace odb @@ -9,5 +10,32 @@ namespace odb prepared_query_impl:: ~prepared_query_impl () { + if (next_ != this) + list_remove (); + } + + prepared_query_impl:: + prepared_query_impl (connection& c) + : cached (false), conn (c), prev_ (0), next_ (this) + { + // Add to the list. + // + next_ = conn.prepared_queries_; + conn.prepared_queries_ = this; + + if (next_ != 0) + next_->prev_ = this; + } + + void prepared_query_impl:: + list_remove () + { + (prev_ == 0 ? conn.prepared_queries_ : prev_->next_) = next_; + + if (next_ != 0) + next_->prev_ = prev_; + + prev_ = 0; + next_ = this; } } diff --git a/odb/prepared-query.hxx b/odb/prepared-query.hxx index e4c9c2b..53d8110 100644 --- a/odb/prepared-query.hxx +++ b/odb/prepared-query.hxx @@ -17,24 +17,56 @@ namespace odb { - struct LIBODB_EXPORT prepared_query_impl: details::shared_base + class LIBODB_EXPORT prepared_query_impl: public details::shared_base { + public: virtual ~prepared_query_impl (); + prepared_query_impl (connection&); + + bool cached; + connection& conn; const char* name; details::shared_ptr stmt; details::shared_ptr (*execute) (prepared_query_impl&); + + private: + prepared_query_impl (const prepared_query_impl&); + prepared_query_impl& operator= (const prepared_query_impl&); + + // Doubly-linked list of results. + // + // prev_ == 0 means we are the first element. + // next_ == 0 means we are the last element. + // next_ == this means we are not on the list. + // + protected: + friend class connection; + + void + list_remove (); + + prepared_query_impl* prev_; + prepared_query_impl* next_; }; template struct prepared_query { - prepared_query () {} + // Cached version. + // + explicit + prepared_query (prepared_query_impl* impl = 0): impl_ (impl) {} + // Uncached version. + // explicit - prepared_query (details::shared_ptr impl) - : impl_ (impl) {} + prepared_query (const details::shared_ptr& impl) + : impl_ (impl.get ()) + { + impl_->_inc_ref (); + } result execute (bool cache = true) @@ -68,16 +100,66 @@ namespace odb return *impl_->stmt; } - typedef details::shared_ptr - prepared_query::*unspecified_bool_type; + typedef prepared_query_impl* prepared_query::*unspecified_bool_type; operator unspecified_bool_type () const { return impl_ ? &prepared_query::impl_ : 0; } + public: + ~prepared_query () + { + if (impl_ != 0 && !impl_->cached && impl_->_dec_ref ()) + delete impl_; + } + + prepared_query (const prepared_query& x) + : impl_ (x.impl_) + { + if (!impl_->cached) + impl_->_inc_ref (); + } + + prepared_query& + operator= (const prepared_query& x) + { + if (impl_ != x.impl_) + { + if (impl_ != 0 && !impl_->cached && impl_->_dec_ref ()) + delete impl_; + + impl_ = x.impl_; + + if (!impl_->cached) + impl_->_inc_ref (); + } + + return *this; + } + private: + // Ideally, we would just use shared_ptr to manage the impl object. + // However, there is a problem if the prepared query is cached on + // the connection and the connection is released early when the + // transaction is committed or rolled back. In this case, the + // prepared_query object might still be around pointing to impl. If + // this connection and the prepared query are then used by another + // thread while we release the impl object, then we have a race + // condition. + // + // To work around this problem we will simply "reference" the impl + // object without counting if the prepared query is cached. For + // transition from pointer to reference, see cache_query_() in + // connection.cxx. + // + // You may also observe that in order to know whether this is a + // cached prepared query or not, we have to read the cached data + // member in the impl object. This does not cause a race because, + // unlike the reference count, this member is immutable once set + // to true. + // friend class connection; - details::shared_ptr impl_; + prepared_query_impl* impl_; }; namespace core diff --git a/odb/result.cxx b/odb/result.cxx index e5b72d0..a088913 100644 --- a/odb/result.cxx +++ b/odb/result.cxx @@ -3,11 +3,39 @@ // license : GNU GPL v2; see accompanying LICENSE file #include +#include namespace odb { result_impl:: ~result_impl () { + if (next_ != this) + list_remove (); + } + + result_impl:: + result_impl (connection& c) + : db_ (c.database ()), conn_ (c), prev_ (0), next_ (this) + { + // Add to the list. + // + next_ = conn_.results_; + conn_.results_ = this; + + if (next_ != 0) + next_->prev_ = this; + } + + void result_impl:: + list_remove () + { + (prev_ == 0 ? conn_.results_ : prev_->next_) = next_; + + if (next_ != 0) + next_->prev_ = prev_; + + prev_ = 0; + next_ = this; } } diff --git a/odb/result.hxx b/odb/result.hxx index b85242f..9f590d0 100644 --- a/odb/result.hxx +++ b/odb/result.hxx @@ -16,10 +16,36 @@ namespace odb { - struct result_impl: details::shared_base + class result_impl: public details::shared_base { + public: virtual ~result_impl (); + + virtual void + invalidate () = 0; + + protected: + result_impl (connection&); + + protected: + database& db_; + connection& conn_; + + // Doubly-linked list of results. + // + // prev_ == 0 means we are the first element. + // next_ == 0 means we are the last element. + // next_ == this means we are not on the list. + // + protected: + friend class connection; + + void + list_remove (); + + result_impl* prev_; + result_impl* next_; }; template diff --git a/odb/simple-object-result.hxx b/odb/simple-object-result.hxx index 6f0868a..b0f3a9a 100644 --- a/odb/simple-object-result.hxx +++ b/odb/simple-object-result.hxx @@ -26,8 +26,6 @@ namespace odb class object_result_impl: public result_impl { protected: - typedef odb::database database_type; - // In result_impl, T is always non-const and the same as object_type. // typedef T object_type; @@ -45,15 +43,9 @@ namespace odb friend class object_result_iterator; protected: - object_result_impl (database_type& db) - : begin_ (true), end_ (false), db_ (db), current_ () - { - } - - database_type& - database () const + object_result_impl (connection& conn) + : result_impl (conn), begin_ (true), end_ (false), current_ () { - return db_; } // To make this work with all kinds of pointers (raw, std::auto_ptr, @@ -153,7 +145,6 @@ namespace odb load (); private: - database_type& db_; pointer_type current_; typename pointer_traits::guard guard_; }; diff --git a/odb/simple-object-result.txx b/odb/simple-object-result.txx index 5ee9686..60705e8 100644 --- a/odb/simple-object-result.txx +++ b/odb/simple-object-result.txx @@ -16,8 +16,7 @@ namespace odb // const id_type& id (load_id ()); - pointer_type p ( - object_traits::pointer_cache_traits::find (database (), id)); + pointer_type p (object_traits::pointer_cache_traits::find (db_, id)); if (!pointer_traits::null_ptr (p)) current (p, false); // Pointer from cache should not be guarded. @@ -26,7 +25,7 @@ namespace odb p = object_traits::create (); typename object_traits::pointer_cache_traits::insert_guard ig ( - object_traits::pointer_cache_traits::insert (database (), id, p)); + object_traits::pointer_cache_traits::insert (db_, id, p)); object_type& obj (pointer_traits::get_ref (p)); current (p); @@ -50,7 +49,7 @@ namespace odb typename object_traits::reference_cache_traits::insert_guard ig ( object_traits::reference_cache_traits::insert ( - res_->database (), res_->load_id (), obj)); + res_->db_, res_->load_id (), obj)); res_->load (obj, false); ig.release (); } diff --git a/odb/statement.cxx b/odb/statement.cxx index eb99227..1d96078 100644 --- a/odb/statement.cxx +++ b/odb/statement.cxx @@ -2,8 +2,6 @@ // copyright : Copyright (c) 2009-2012 Code Synthesis Tools CC // license : GNU GPL v2; see accompanying LICENSE file -#include - #include namespace odb @@ -12,11 +10,4 @@ namespace odb ~statement () { } - - void statement:: - cached (bool cached) - { - assert (cached); - cached_ = true; - } } diff --git a/odb/statement.hxx b/odb/statement.hxx index 3cd5598..284c8ae 100644 --- a/odb/statement.hxx +++ b/odb/statement.hxx @@ -32,22 +32,8 @@ namespace odb virtual ~statement () = 0; - // Statement caching status. - // - public: - bool - cached () const - { - return cached_; - } - - virtual void - cached (bool); - protected: - statement (): cached_ (false) {} - - bool cached_; + statement () {} }; } diff --git a/odb/view-result.hxx b/odb/view-result.hxx index d111e36..a07e5b8 100644 --- a/odb/view-result.hxx +++ b/odb/view-result.hxx @@ -29,8 +29,6 @@ namespace odb friend class result_iterator; friend class result_iterator; - typedef odb::database database_type; - // In result_impl, T is always non-const and the same as view_type. // typedef T view_type; @@ -39,15 +37,9 @@ namespace odb typedef typename view_traits::pointer_type pointer_type; typedef odb::pointer_traits pointer_traits; - view_result_impl (database_type& db) - : begin_ (true), end_ (false), db_ (db), current_ () - { - } - - database_type& - database () const + view_result_impl (connection& conn) + : result_impl (conn), begin_ (true), end_ (false), current_ () { - return db_; } // To make this work with all kinds of pointers (raw, std::auto_ptr, @@ -120,7 +112,6 @@ namespace odb bool end_; private: - database_type& db_; pointer_type current_; typename pointer_traits::guard guard_; }; -- cgit v1.1