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/prepared-query.hxx | 96 ++++++++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 89 insertions(+), 7 deletions(-) (limited to 'odb/prepared-query.hxx') 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 -- cgit v1.1