aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--odb/sqlite/connection-factory.cxx24
-rw-r--r--odb/sqlite/connection-factory.hxx12
-rw-r--r--odb/sqlite/connection.cxx55
-rw-r--r--odb/sqlite/connection.hxx24
-rw-r--r--odb/sqlite/error.cxx11
-rw-r--r--odb/sqlite/makefile1
-rw-r--r--odb/sqlite/statement.cxx75
7 files changed, 173 insertions, 29 deletions
diff --git a/odb/sqlite/connection-factory.cxx b/odb/sqlite/connection-factory.cxx
index 4cae67f..11184bc 100644
--- a/odb/sqlite/connection-factory.cxx
+++ b/odb/sqlite/connection-factory.cxx
@@ -5,6 +5,7 @@
#include <odb/details/lock.hxx>
+#include <odb/sqlite/database.hxx>
#include <odb/sqlite/connection-factory.hxx>
using namespace std;
@@ -31,13 +32,19 @@ namespace odb
shared_ptr<connection> new_connection_factory::
connect ()
{
- return shared_ptr<connection> (new (shared) connection (*db_));
+ return shared_ptr<connection> (
+ new (shared) connection (*db_, extra_flags_));
}
void new_connection_factory::
database (database_type& db)
{
db_ = &db;
+
+ // Unless explicitly disabled, enable shared cache.
+ //
+ if ((db_->flags () & SQLITE_OPEN_PRIVATECACHE) == 0)
+ extra_flags_ |= SQLITE_OPEN_SHAREDCACHE;
}
//
@@ -82,7 +89,7 @@ namespace odb
if(max_ == 0 || in_use_ < max_)
{
shared_ptr<pooled_connection> c (
- new (shared) pooled_connection (*db_, this));
+ new (shared) pooled_connection (*db_, extra_flags_, this));
in_use_++;
return c;
}
@@ -100,6 +107,11 @@ namespace odb
{
db_ = &db;
+ // Unless explicitly disabled, enable shared cache.
+ //
+ if ((db_->flags () & SQLITE_OPEN_PRIVATECACHE) == 0)
+ extra_flags_ |= SQLITE_OPEN_SHAREDCACHE;
+
if (min_ > 0)
{
connections_.reserve (min_);
@@ -108,7 +120,7 @@ namespace odb
{
connections_.push_back (
shared_ptr<pooled_connection> (
- new (shared) pooled_connection (*db_, 0)));
+ new (shared) pooled_connection (*db_, extra_flags_, 0)));
}
}
}
@@ -143,8 +155,10 @@ namespace odb
//
connection_pool_factory::pooled_connection::
- pooled_connection (database_type& db, connection_pool_factory* pool)
- : connection (db), pool_ (pool)
+ pooled_connection (database_type& db,
+ int extra_flags,
+ connection_pool_factory* pool)
+ : connection (db, extra_flags), pool_ (pool)
{
callback_.arg = this;
callback_.zero_counter = &zero_counter;
diff --git a/odb/sqlite/connection-factory.hxx b/odb/sqlite/connection-factory.hxx
index 6a42c8f..76bdc11 100644
--- a/odb/sqlite/connection-factory.hxx
+++ b/odb/sqlite/connection-factory.hxx
@@ -44,10 +44,7 @@ namespace odb
public connection_factory
{
public:
- new_connection_factory ()
- : db_ (0)
- {
- }
+ new_connection_factory (): db_ (0), extra_flags_ (0) {}
virtual details::shared_ptr<connection>
connect ();
@@ -61,6 +58,7 @@ namespace odb
private:
database_type* db_;
+ int extra_flags_;
};
class LIBODB_SQLITE_EXPORT connection_pool_factory:
@@ -87,6 +85,7 @@ namespace odb
std::size_t min_connections = 0)
: max_ (max_connections),
min_ (min_connections),
+ extra_flags_ (0),
in_use_ (0),
waiters_ (0),
db_ (0),
@@ -114,7 +113,9 @@ namespace odb
public:
// NULL pool value indicates that the connection is not in use.
//
- pooled_connection (database_type&, connection_pool_factory*);
+ pooled_connection (database_type&,
+ int extra_flags,
+ connection_pool_factory*);
private:
static bool
@@ -139,6 +140,7 @@ namespace odb
private:
const std::size_t max_;
const std::size_t min_;
+ int extra_flags_;
std::size_t in_use_; // Number of connections currently in use.
std::size_t waiters_; // Number of threads waiting for a connection.
diff --git a/odb/sqlite/connection.cxx b/odb/sqlite/connection.cxx
index 9af5a3b..91b430a 100644
--- a/odb/sqlite/connection.cxx
+++ b/odb/sqlite/connection.cxx
@@ -7,14 +7,22 @@
#include <string>
#include <cassert>
+#include <odb/details/lock.hxx>
+
#include <odb/sqlite/database.hxx>
#include <odb/sqlite/connection.hxx>
#include <odb/sqlite/statement.hxx>
#include <odb/sqlite/statement-cache.hxx>
#include <odb/sqlite/error.hxx>
+#include <odb/sqlite/exceptions.hxx> // deadlock
+
+#include <odb/sqlite/details/config.hxx> // LIBODB_SQLITE_HAVE_UNLOCK_NOTIFY
using namespace std;
+extern "C" void
+odb_sqlite_connection_unlock_callback (void**, int);
+
namespace odb
{
namespace sqlite
@@ -29,10 +37,10 @@ namespace odb
}
connection::
- connection (database_type& db)
- : db_ (db), statements_ (0)
+ connection (database_type& db, int extra_flags)
+ : db_ (db), unlock_cond_ (unlock_mutex_), statements_ (0)
{
- int f (db.flags ());
+ int f (db.flags () | extra_flags);
const string& n (db.name ());
// If we are opening a temporary database, then add the create flag.
@@ -57,6 +65,41 @@ namespace odb
statement_cache_.reset (new statement_cache_type (*this));
}
+ inline void
+ connection_unlock_callback (void** args, int n)
+ {
+ for (int i (0); i < n; ++i)
+ {
+ connection* c (static_cast<connection*> (args[i]));
+ details::lock l (c->unlock_mutex_);
+ c->unlocked_ = true;
+ c->unlock_cond_.signal ();
+ }
+ }
+
+ void connection::
+ wait ()
+ {
+#ifdef LIBODB_SQLITE_HAVE_UNLOCK_NOTIFY
+ unlocked_ = false;
+
+ // unlock_notify() returns SQLITE_OK or SQLITE_LOCKED (deadlock).
+ //
+ int e (sqlite3_unlock_notify (handle_,
+ &odb_sqlite_connection_unlock_callback,
+ this));
+ if (e == SQLITE_LOCKED)
+ throw deadlock ();
+
+ details::lock l (unlock_mutex_);
+
+ while (!unlocked_)
+ unlock_cond_.wait ();
+#else
+ translate_error (SQLITE_LOCKED, *this);
+#endif
+ }
+
void connection::
clear ()
{
@@ -75,3 +118,9 @@ namespace odb
}
}
}
+
+extern "C" void
+odb_sqlite_connection_unlock_callback (void** args, int n)
+{
+ odb::sqlite::connection_unlock_callback (args, n);
+}
diff --git a/odb/sqlite/connection.hxx b/odb/sqlite/connection.hxx
index 5390609..3ae82e3 100644
--- a/odb/sqlite/connection.hxx
+++ b/odb/sqlite/connection.hxx
@@ -13,6 +13,8 @@
#include <memory> // std::auto_ptr
#include <odb/forward.hxx>
+#include <odb/details/mutex.hxx>
+#include <odb/details/condition.hxx>
#include <odb/details/shared-ptr.hxx>
#include <odb/sqlite/version.hxx>
@@ -35,7 +37,7 @@ namespace odb
virtual
~connection ();
- connection (database_type&);
+ connection (database_type&, int extra_flags = 0);
database_type&
database ()
@@ -56,6 +58,12 @@ namespace odb
return *statement_cache_;
}
+ // Wait for the locks to be released via unlock notification. Can
+ // be called after getting SQLITE_LOCKED_SHAREDCACHE.
+ //
+ void
+ wait ();
+
public:
// Reset active and finalize uncached statements.
//
@@ -67,14 +75,24 @@ namespace odb
connection& operator= (const connection&);
private:
- friend class statement;
-
database_type& db_;
sqlite3* handle_;
+ // Unlock notification machinery.
+ //
+ private:
+ bool unlocked_;
+ details::mutex unlock_mutex_;
+ details::condition unlock_cond_;
+
+ friend void
+ connection_unlock_callback (void**, int);
+
// Linked list of active and uncached statements currently associated
// with this connection.
//
+ private:
+ friend class statement;
statement* statements_;
std::auto_ptr<statement_cache_type> statement_cache_;
diff --git a/odb/sqlite/error.cxx b/odb/sqlite/error.cxx
index d223b4e..f84a6c0 100644
--- a/odb/sqlite/error.cxx
+++ b/odb/sqlite/error.cxx
@@ -39,8 +39,17 @@ namespace odb
m = "SQLite API misuse";
break;
}
- case SQLITE_BUSY:
case SQLITE_LOCKED:
+ {
+ if (ee != SQLITE_LOCKED_SHAREDCACHE)
+ throw deadlock (); // The DROP TABLE special case.
+
+ // Getting SQLITE_LOCKED_SHAREDCACHE here means we don't have
+ // the unlock notify support. Translate this to timeout.
+ //
+ throw timeout ();
+ }
+ case SQLITE_BUSY:
case SQLITE_IOERR:
{
if (e != SQLITE_IOERR || ee == SQLITE_IOERR_BLOCKED)
diff --git a/odb/sqlite/makefile b/odb/sqlite/makefile
index 2d51d7d..feb62eb 100644
--- a/odb/sqlite/makefile
+++ b/odb/sqlite/makefile
@@ -73,6 +73,7 @@ $(out_base)/details/config.h:
@echo '#ifndef ODB_SQLITE_DETAILS_CONFIG_H' >>$@
@echo '#define ODB_SQLITE_DETAILS_CONFIG_H' >>$@
@echo '' >>$@
+ @echo '#define LIBODB_SQLITE_HAVE_UNLOCK_NOTIFY 1' >>$@
@echo '' >>$@
@echo '#endif /* ODB_SQLITE_DETAILS_CONFIG_H */' >>$@
diff --git a/odb/sqlite/statement.cxx b/odb/sqlite/statement.cxx
index 0975545..f503144 100644
--- a/odb/sqlite/statement.cxx
+++ b/odb/sqlite/statement.cxx
@@ -30,16 +30,19 @@ namespace odb
void statement::
init (const char* s, std::size_t n)
{
- if (int e = sqlite3_prepare_v2 (
- conn_.handle (),
- s,
- static_cast<int> (n),
- &stmt_,
- 0))
+ int e;
+ while ((e = sqlite3_prepare_v2 (conn_.handle (),
+ s,
+ static_cast<int> (n),
+ &stmt_,
+ 0)) == SQLITE_LOCKED)
{
- translate_error (e, conn_);
+ conn_.wait ();
}
+ if (e != SQLITE_OK)
+ translate_error (e, conn_);
+
active_ = false;
cached_ = false;
@@ -194,8 +197,20 @@ namespace odb
unsigned long long r (0);
+ // Only the first call to sqlite3_step() can return SQLITE_LOCKED.
+ //
int e;
- for (e = sqlite3_step (stmt_); e == SQLITE_ROW; e = sqlite3_step (stmt_))
+ sqlite3* h (conn_.handle ());
+ while ((e = sqlite3_step (stmt_)) == SQLITE_LOCKED)
+ {
+ if (sqlite3_extended_errcode (h) != SQLITE_LOCKED_SHAREDCACHE)
+ break;
+
+ sqlite3_reset (stmt_);
+ conn_.wait ();
+ }
+
+ for (; e == SQLITE_ROW; e = sqlite3_step (stmt_))
r++;
sqlite3_reset (stmt_);
@@ -245,7 +260,16 @@ namespace odb
{
if (!done_)
{
- int e (sqlite3_step (stmt_));
+ int e;
+ sqlite3* h (conn_.handle ());
+ while ((e = sqlite3_step (stmt_)) == SQLITE_LOCKED)
+ {
+ if (sqlite3_extended_errcode (h) != SQLITE_LOCKED_SHAREDCACHE)
+ break;
+
+ sqlite3_reset (stmt_);
+ conn_.wait ();
+ }
if (e != SQLITE_ROW)
{
@@ -292,7 +316,16 @@ namespace odb
{
bind_param (data_.bind, data_.count);
- int e (sqlite3_step (stmt_));
+ int e;
+ sqlite3* h (conn_.handle ());
+ while ((e = sqlite3_step (stmt_)) == SQLITE_LOCKED)
+ {
+ if (sqlite3_extended_errcode (h) != SQLITE_LOCKED_SHAREDCACHE)
+ break;
+
+ sqlite3_reset (stmt_);
+ conn_.wait ();
+ }
sqlite3_reset (stmt_);
@@ -337,7 +370,16 @@ namespace odb
bind_param (data_.bind, data_.count);
bind_param (cond_.bind, cond_.count, data_.count);
- int e (sqlite3_step (stmt_));
+ int e;
+ sqlite3* h (conn_.handle ());
+ while ((e = sqlite3_step (stmt_)) == SQLITE_LOCKED)
+ {
+ if (sqlite3_extended_errcode (h) != SQLITE_LOCKED_SHAREDCACHE)
+ break;
+
+ sqlite3_reset (stmt_);
+ conn_.wait ();
+ }
sqlite3_reset (stmt_);
@@ -362,7 +404,16 @@ namespace odb
{
bind_param (cond_.bind, cond_.count);
- int e (sqlite3_step (stmt_));
+ int e;
+ sqlite3* h (conn_.handle ());
+ while ((e = sqlite3_step (stmt_)) == SQLITE_LOCKED)
+ {
+ if (sqlite3_extended_errcode (h) != SQLITE_LOCKED_SHAREDCACHE)
+ break;
+
+ sqlite3_reset (stmt_);
+ conn_.wait ();
+ }
sqlite3_reset (stmt_);