From b4825d95472f8d9e2b9892a56c585a035b6230ac Mon Sep 17 00:00:00 2001
From: Boris Kolpackov <boris@codesynthesis.com>
Date: Sat, 28 Sep 2013 10:51:12 +0200
Subject: Make schema version access (but not modification) thread-safe

Also cache the version in statements so that we don't have to lock
the mutex (slow) every time we need to check the version.
---
 odb/database.cxx | 25 ++++++++++++-------------
 odb/database.hxx | 28 ++++++++++++++++------------
 odb/database.ixx | 15 +--------------
 3 files changed, 29 insertions(+), 39 deletions(-)

diff --git a/odb/database.cxx b/odb/database.cxx
index c9692ba..5578abd 100644
--- a/odb/database.cxx
+++ b/odb/database.cxx
@@ -4,10 +4,14 @@
 
 #include <odb/database.hxx>
 
+#include <odb/details/lock.hxx>
+
 using namespace std;
 
 namespace odb
 {
+  using details::lock;
+
   database::
   ~database ()
   {
@@ -20,25 +24,23 @@ namespace odb
     return c.execute (st, n);
   }
 
-  const database::schema_version_info& database::
-  schema_version_migration_ (const string& name) const
+  const database::schema_version_migration_type& database::
+  schema_version_migration (const string& name) const
   {
+    lock l (mutex_); // Prevents concurrent loading.
+
     schema_version_map::const_iterator i (schema_version_map_.find (name));
-    const schema_version_info& svi (
-      i != schema_version_map_.end () && i->second.version != 0
+    return i != schema_version_map_.end () && i->second.version != 0
       ? i->second
-      : load_schema_version (name));
-
-    if (default_schema_version_ == 0 && name.empty ())
-      default_schema_version_ = &svi;
-
-    return svi;
+      : load_schema_version (name);
   }
 
   void database::
   schema_version_migration (const schema_version_migration_type& svm,
                             const string& name)
   {
+    // Note: no lock, not thread-safe.
+
     schema_version_info& svi (schema_version_map_[name]);
     if (svi.version != svm.version || svi.migration != svm.migration)
     {
@@ -46,8 +48,5 @@ namespace odb
       svi.migration = svm.migration;
       schema_version_seq_++;
     }
-
-    if (default_schema_version_ == 0 && name.empty ())
-      default_schema_version_ = &svi;
   }
 }
diff --git a/odb/database.hxx b/odb/database.hxx
index 7ddbecb..91d8e94 100644
--- a/odb/database.hxx
+++ b/odb/database.hxx
@@ -28,6 +28,7 @@
 #include <odb/exceptions.hxx>
 
 #include <odb/details/export.hxx>
+#include <odb/details/mutex.hxx>
 #include <odb/details/c-string.hxx>
 
 namespace odb
@@ -339,31 +340,34 @@ namespace odb
     typedef odb::schema_version_migration schema_version_migration_type;
 
     schema_version_type
-    schema_version (const std::string& schema_name = std::string ()) const;
+    schema_version (const std::string& schema_name = "") const;
 
     bool
-    schema_migration (const std::string& schema_name = std::string ()) const;
+    schema_migration (const std::string& schema_name = "") const;
 
     // Note that there is code that relies on the returned reference
     // being valid until the version is changed or the database instance
     // is destroyed.
     //
     const schema_version_migration_type&
-    schema_version_migration (
-      const std::string& schema_name = std::string ()) const;
+    schema_version_migration (const std::string& schema_name = "") const;
 
     // Set schema version and migration state manually.
     //
+    // Note that the modifier API is not thread-safe. That is, you should
+    // not modify the schema version while other threads may be accessing
+    // or modifying the same information.
+    //
     void
     schema_version_migration (schema_version_type,
                               bool migration,
-                              const std::string& schema_name = std::string ());
+                              const std::string& schema_name = "");
 
     void
     schema_version_migration (const schema_version_migration_type&,
-                              const std::string& schema_name = std::string ());
+                              const std::string& schema_name = "");
 
-    // Set default schema version table for all schema names. The table
+    // Set default schema version table for all the schema names. The table
     // name should already be quoted if necessary.
     //
     void
@@ -375,9 +379,9 @@ namespace odb
     schema_version_table (const std::string& table_name,
                           const std::string& schema_name);
 
-    // Schema version sequence number. It is incremented every time
-    // the schema version or migration flag is changed and can be
-    // used to detect version changes. The starting value is 1.
+    // Schema version sequence number. It is incremented every time the
+    // schema version or migration flag is changed and can be used to
+    // detect overall version changes. The starting value is 1.
     //
     unsigned int
     schema_version_sequence () const;
@@ -485,9 +489,9 @@ namespace odb
     tracer_type* tracer_;
     query_factory_map query_factory_map_;
 
-    std::string schema_version_table_;
+    mutable details::mutex mutex_;
     mutable schema_version_map schema_version_map_;
-    mutable const schema_version_info* default_schema_version_; // Cached.
+    std::string schema_version_table_;
     unsigned int schema_version_seq_;
   };
 }
diff --git a/odb/database.ixx b/odb/database.ixx
index 378a376..a15bb7d 100644
--- a/odb/database.ixx
+++ b/odb/database.ixx
@@ -11,10 +11,7 @@ namespace odb
 {
   inline database::
   database (database_id id)
-      : id_ (id),
-        tracer_ (0),
-        default_schema_version_ (0),
-        schema_version_seq_ (1)
+      : id_ (id), tracer_ (0), schema_version_seq_ (1)
   {
   }
 
@@ -36,16 +33,6 @@ namespace odb
     return schema_version_migration (name).migration;
   }
 
-  inline const database::schema_version_migration_type& database::
-  schema_version_migration (const std::string& name) const
-  {
-    return name.empty () &&
-      default_schema_version_ != 0 &&
-      default_schema_version_->version != 0
-      ? *default_schema_version_
-      : schema_version_migration_ (name);
-  }
-
   inline void database::
   schema_version_migration (schema_version_type v,
                             bool m,
-- 
cgit v1.1