aboutsummaryrefslogtreecommitdiff
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
parent5b0430fdf4617b396e462872d438a663b174a3a8 (diff)
Implement early connection release
-rw-r--r--odb/connection.cxx55
-rw-r--r--odb/connection.hxx29
-rw-r--r--odb/connection.ixx6
-rw-r--r--odb/forward.hxx5
-rw-r--r--odb/no-id-object-result.hxx13
-rw-r--r--odb/polymorphic-object-result.hxx13
-rw-r--r--odb/polymorphic-object-result.txx4
-rw-r--r--odb/prepared-query.cxx28
-rw-r--r--odb/prepared-query.hxx96
-rw-r--r--odb/result.cxx28
-rw-r--r--odb/result.hxx28
-rw-r--r--odb/simple-object-result.hxx13
-rw-r--r--odb/simple-object-result.txx7
-rw-r--r--odb/statement.cxx9
-rw-r--r--odb/statement.hxx16
-rw-r--r--odb/view-result.hxx13
16 files changed, 269 insertions, 94 deletions
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 <odb/database.hxx>
#include <odb/connection.hxx>
+#include <odb/result.hxx>
+#include <odb/prepared-query.hxx>
#include <odb/exceptions.hxx> // 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<prepared_query_impl> 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<prepared_query_impl> 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<prepared_query_impl> ();
+ 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<prepared_query_impl> 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>
+ 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 <typename T> struct no_op_reference_cache_traits;
template <typename T> struct reference_cache_traits;
+ //
+ //
+ class result_impl;
+ class prepared_query_impl;
+
// Polymorphism support.
//
template <typename R>
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<const T, void, false>;
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<const T, id_type, true>;
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 <odb/connection.hxx>
#include <odb/prepared-query.hxx>
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<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
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 <odb/result.hxx>
+#include <odb/connection.hxx>
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 <typename T, class_kind kind>
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<const T, id_type, false>;
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 <cassert>
-
#include <odb/statement.hxx>
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<T, class_view>;
friend class result_iterator<const T, class_view>;
- 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_type> 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_;
};