aboutsummaryrefslogtreecommitdiff
path: root/odb/prepared-query.hxx
diff options
context:
space:
mode:
authorBoris Kolpackov <boris@codesynthesis.com>2012-10-15 13:17:30 +0200
committerBoris Kolpackov <boris@codesynthesis.com>2012-10-19 11:38:24 +0200
commit1e78bdc724e95898c04a3409b0b192aa7f77780b (patch)
treed26ae47ae9956612b5973f536219f0c9b455db03 /odb/prepared-query.hxx
parent5b0430fdf4617b396e462872d438a663b174a3a8 (diff)
Implement early connection release
Diffstat (limited to 'odb/prepared-query.hxx')
-rw-r--r--odb/prepared-query.hxx96
1 files changed, 89 insertions, 7 deletions
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<statement> stmt;
details::shared_ptr<result_impl> (*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 <typename T>
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<prepared_query_impl> impl)
- : impl_ (impl) {}
+ prepared_query (const details::shared_ptr<prepared_query_impl>& impl)
+ : impl_ (impl.get ())
+ {
+ impl_->_inc_ref ();
+ }
result<T>
execute (bool cache = true)
@@ -68,16 +100,66 @@ namespace odb
return *impl_->stmt;
}
- typedef details::shared_ptr<prepared_query_impl>
- 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<prepared_query_impl> impl_;
+ prepared_query_impl* impl_;
};
namespace core