diff --git a/realm_delegate.hpp b/binding_context.hpp similarity index 91% rename from realm_delegate.hpp rename to binding_context.hpp index 0d9e9dfe..4aa6dd94 100644 --- a/realm_delegate.hpp +++ b/binding_context.hpp @@ -16,8 +16,8 @@ // //////////////////////////////////////////////////////////////////////////// -#ifndef REALM_DELEGATE_HPP -#define REALM_DELEGATE_HPP +#ifndef BINDING_CONTEXT_HPP +#define BINDING_CONTEXT_HPP #include "index_set.hpp" @@ -25,15 +25,15 @@ #include namespace realm { -// RealmDelegate is the extension point for adding binding-specific behavior to +// BindingContext is the extension point for adding binding-specific behavior to // a SharedRealm. It can be used to store additonal data associated with the // Realm which is needed by the binding, and there are several methods which // can be overridden to receive notifications of state changes within the Realm. // -// A simple delegate implementation which lets the user register functions to be +// A simple implementation which lets the user register functions to be // called on refresh could look like the following: // -// class DelegateImplementation : public RealmDelegate { +// class BindingContextImplementation : public BindingContext { // public: // // A token returned from add_notification that can be used to remove the // // notification later @@ -66,9 +66,9 @@ namespace realm { // private: // std::list> m_registered_notifications; // }; -class RealmDelegate { +class BindingContext { public: - virtual ~RealmDelegate() = default; + virtual ~BindingContext() = default; // Called by the Realm when a write transaction is committed to the file by // a different Realm instance (possibly in a different process) @@ -147,8 +147,8 @@ public: }; }; -inline void RealmDelegate::will_change(std::vector const&, std::vector const&) { } -inline void RealmDelegate::did_change(std::vector const&, std::vector const&) { } +inline void BindingContext::will_change(std::vector const&, std::vector const&) { } +inline void BindingContext::did_change(std::vector const&, std::vector const&) { } } // namespace realm -#endif /* REALM_DELEGATE_HPP */ +#endif /* BINDING_CONTEXT_HPP */ diff --git a/impl/transact_log_handler.cpp b/impl/transact_log_handler.cpp index e1b95405..fba97129 100644 --- a/impl/transact_log_handler.cpp +++ b/impl/transact_log_handler.cpp @@ -18,7 +18,7 @@ #include "transact_log_handler.hpp" -#include "realm_delegate.hpp" +#include "../realm_binding_context.hpp" #include #include @@ -28,15 +28,15 @@ using namespace realm; namespace { class TransactLogHandler { - using ColumnInfo = RealmDelegate::ColumnInfo; - using ObserverState = RealmDelegate::ObserverState; + using ColumnInfo = RealmBindingContext::ColumnInfo; + using ObserverState = RealmBindingContext::ObserverState; // Observed table rows which need change information std::vector m_observers; // Userdata pointers for rows which have been deleted std::vector invalidated; // Delegate to send change information to - RealmDelegate* m_delegate; + RealmBindingContext* m_binding_context; // Index of currently selected table size_t m_current_table = 0; @@ -84,33 +84,33 @@ class TransactLogHandler { public: template - TransactLogHandler(RealmDelegate* delegate, SharedGroup& sg, Func&& func) - : m_delegate(delegate) + TransactLogHandler(RealmBindingContext* binding_context, SharedGroup& sg, Func&& func) + : m_binding_context(binding_context) { - if (!delegate) { + if (!binding_context) { func(); return; } - m_observers = delegate->get_observed_rows(); + m_observers = binding_context->get_observed_rows(); if (m_observers.empty()) { auto old_version = sg.get_version_of_current_transaction(); func(); if (old_version != sg.get_version_of_current_transaction()) { - delegate->did_change({}, {}); + binding_context->did_change({}, {}); } return; } func(*this); - delegate->did_change(m_observers, invalidated); + binding_context->did_change(m_observers, invalidated); } // Called at the end of the transaction log immediately before the version // is advanced void parse_complete() { - m_delegate->will_change(m_observers, invalidated); + m_binding_context->will_change(m_observers, invalidated); } // These would require having an observer before schema init @@ -175,7 +175,7 @@ public: return true; } - bool select_link_list(size_t col, size_t row) + bool select_link_list(size_t col, size_t row, size_t) { m_active_linklist = nullptr; for (auto& o : m_observers) { @@ -303,43 +303,50 @@ public: bool set_date_time(size_t col, size_t row, DateTime) { return mark_dirty(row, col); } bool set_table(size_t col, size_t row) { return mark_dirty(row, col); } bool set_mixed(size_t col, size_t row, const Mixed&) { return mark_dirty(row, col); } - bool set_link(size_t col, size_t row, size_t) { return mark_dirty(row, col); } + bool set_link(size_t col, size_t row, size_t, size_t) { return mark_dirty(row, col); } bool set_null(size_t col, size_t row) { return mark_dirty(row, col); } - bool nullify_link(size_t col, size_t row) { return mark_dirty(row, col); } + bool nullify_link(size_t col, size_t row, size_t) { return mark_dirty(row, col); } // Doesn't change any data bool optimize_table() { return true; } // Used for subtables, which we currently don't support bool select_descriptor(int, const size_t*) { return false; } + + // Not implemented + bool insert_substring(size_t, size_t, size_t, StringData) { return false; } + bool erase_substring(size_t, size_t, size_t, size_t) { return false; } + bool swap_rows(size_t, size_t) { return false; } + bool move_column(size_t, size_t) { return false; } + bool move_group_level_table(size_t, size_t) { return false; } }; } // anonymous namespace namespace realm { namespace _impl { namespace transaction { -void advance(SharedGroup& sg, ClientHistory& history, RealmDelegate* delegate) { - TransactLogHandler(delegate, sg, [&](auto&&... args) { +void advance(SharedGroup& sg, ClientHistory& history, RealmBindingContext* binding_context) { + TransactLogHandler(binding_context, sg, [&](auto&&... args) { LangBindHelper::advance_read(sg, history, std::move(args)...); }); } -void begin(SharedGroup& sg, ClientHistory& history, RealmDelegate* delegate) { - TransactLogHandler(delegate, sg, [&](auto&&... args) { +void begin(SharedGroup& sg, ClientHistory& history, RealmBindingContext* binding_context) { + TransactLogHandler(binding_context, sg, [&](auto&&... args) { LangBindHelper::promote_to_write(sg, history, std::move(args)...); }); } -void commit(SharedGroup& sg, ClientHistory&, RealmDelegate* delegate) { +void commit(SharedGroup& sg, ClientHistory&, RealmBindingContext* binding_context) { LangBindHelper::commit_and_continue_as_read(sg); - if (delegate) { - delegate->did_change({}, {}); + if (binding_context) { + binding_context->did_change({}, {}); } } -void cancel(SharedGroup& sg, ClientHistory& history, RealmDelegate* delegate) { - TransactLogHandler(delegate, sg, [&](auto&&... args) { +void cancel(SharedGroup& sg, ClientHistory& history, RealmBindingContext* binding_context) { + TransactLogHandler(binding_context, sg, [&](auto&&... args) { LangBindHelper::rollback_and_continue_as_read(sg, history, std::move(args)...); }); } diff --git a/impl/transact_log_handler.hpp b/impl/transact_log_handler.hpp index dbb54a53..321a67c9 100644 --- a/impl/transact_log_handler.hpp +++ b/impl/transact_log_handler.hpp @@ -20,7 +20,7 @@ #define REALM_TRANSACT_LOG_HANDLER_HPP namespace realm { -class RealmDelegate; +class RealmBindingContext; class SharedGroup; class ClientHistory; @@ -28,19 +28,19 @@ namespace _impl { namespace transaction { // Advance the read transaction version, with change notifications sent to delegate // Must not be called from within a write transaction. -void advance(SharedGroup& sg, ClientHistory& history, RealmDelegate* delegate); +void advance(SharedGroup& sg, ClientHistory& history, RealmBindingContext* binding_context); // Begin a write transaction // If the read transaction version is not up to date, will first advance to the // most recent read transaction and sent notifications to delegate -void begin(SharedGroup& sg, ClientHistory& history, RealmDelegate* delegate); +void begin(SharedGroup& sg, ClientHistory& history, RealmBindingContext* binding_context); // Commit a write transaction -void commit(SharedGroup& sg, ClientHistory& history, RealmDelegate* delegate); +void commit(SharedGroup& sg, ClientHistory& history, RealmBindingContext* binding_context); // Cancel a write transaction and roll back all changes, with change notifications // for reverting to the old values sent to delegate -void cancel(SharedGroup& sg, ClientHistory& history, RealmDelegate* delegate); +void cancel(SharedGroup& sg, ClientHistory& history, RealmBindingContext* binding_context); } // namespace transaction } // namespace _impl } // namespace realm diff --git a/object_schema.hpp b/object_schema.hpp index aca4af31..0ede3f5d 100644 --- a/object_schema.hpp +++ b/object_schema.hpp @@ -27,6 +27,7 @@ namespace realm { class Property; class Group; + struct Property; class ObjectSchema { public: diff --git a/object_store.cpp b/object_store.cpp index 1949c285..3e32b98c 100644 --- a/object_store.cpp +++ b/object_store.cpp @@ -502,7 +502,19 @@ void ObjectStore::delete_data_for_object(Group *group, const StringData &object_ } } - +bool ObjectStore::is_empty(const Group *group) { + for (size_t i = 0; i < group->size(); i++) { + ConstTableRef table = group->get_table(i); + std::string object_type = object_type_for_table_name(table->get_name()); + if (!object_type.length()) { + continue; + } + if (!table->is_empty()) { + return false; + } + } + return true; +} InvalidSchemaVersionException::InvalidSchemaVersionException(uint64_t old_version, uint64_t new_version) : m_old_version(old_version), m_new_version(new_version) diff --git a/object_store.hpp b/object_store.hpp index 9972b089..60671f55 100644 --- a/object_store.hpp +++ b/object_store.hpp @@ -70,7 +70,10 @@ namespace realm { // deletes the table for the given type static void delete_data_for_object(Group *group, const StringData &object_type); - private: + // indicates if this group contains any objects + static bool is_empty(const Group *group); + + private: // set a new schema version static void set_schema_version(Group *group, uint64_t version); diff --git a/schema.hpp b/schema.hpp index 0a7fa425..4d9c7bed 100644 --- a/schema.hpp +++ b/schema.hpp @@ -52,4 +52,4 @@ public: }; } -#endif /* defined(REALM_SCHEMA_HPP) */ \ No newline at end of file +#endif /* defined(REALM_SCHEMA_HPP) */ diff --git a/shared_realm.cpp b/shared_realm.cpp index 10c558c1..c87dd5d4 100644 --- a/shared_realm.cpp +++ b/shared_realm.cpp @@ -19,7 +19,7 @@ #include "shared_realm.hpp" #include "external_commit_helper.hpp" -#include "realm_delegate.hpp" +#include "binding_context.hpp" #include "schema.hpp" #include "transact_log_handler.hpp" @@ -73,18 +73,22 @@ Realm::Realm(Config config) } } catch (util::File::PermissionDenied const& ex) { - throw RealmFileException(RealmFileException::Kind::PermissionDenied, "Unable to open a realm at path '" + m_config.path + - "'. Please use a path where your app has " + (m_config.read_only ? "read" : "read-write") + " permissions."); + throw RealmFileException(RealmFileException::Kind::PermissionDenied, ex.get_path(), + "Unable to open a realm at path '" + ex.get_path() + + "'. Please use a path where your app has " + (m_config.read_only ? "read" : "read-write") + " permissions."); } catch (util::File::Exists const& ex) { - throw RealmFileException(RealmFileException::Kind::Exists, "Unable to open a realm at path '" + m_config.path + "'"); + throw RealmFileException(RealmFileException::Kind::Exists, ex.get_path(), + "File at path '" + ex.get_path() + "' already exists."); } catch (util::File::AccessError const& ex) { - throw RealmFileException(RealmFileException::Kind::AccessError, "Unable to open a realm at path '" + m_config.path + "'"); + throw RealmFileException(RealmFileException::Kind::AccessError, ex.get_path(), + "Unable to open a realm at path '" + ex.get_path() + "'"); } - catch (IncompatibleLockFile const&) { - throw RealmFileException(RealmFileException::Kind::IncompatibleLockFile, "Realm file is currently open in another process " - "which cannot share access with this process. All processes sharing a single file must be the same architecture."); + catch (IncompatibleLockFile const& ex) { + throw RealmFileException(RealmFileException::Kind::IncompatibleLockFile, m_config.path, + "Realm file is currently open in another process " + "which cannot share access with this process. All processes sharing a single file must be the same architecture."); } } @@ -199,9 +203,7 @@ bool Realm::update_schema(std::unique_ptr schema, uint64_t version) auto migration_function = [&](Group*, Schema&) { SharedRealm old_realm(new Realm(old_config)); auto updated_realm = shared_from_this(); - if (m_config.migration_function) { - m_config.migration_function(old_realm, updated_realm); - } + m_config.migration_function(old_realm, updated_realm); }; try { @@ -249,7 +251,7 @@ void Realm::begin_transaction() // make sure we have a read transaction read_group(); - transaction::begin(*m_shared_group, *m_history, m_delegate.get()); + transaction::begin(*m_shared_group, *m_history, m_binding_context.get()); m_in_transaction = true; } @@ -263,7 +265,7 @@ void Realm::commit_transaction() } m_in_transaction = false; - transaction::commit(*m_shared_group, *m_history, m_delegate.get()); + transaction::commit(*m_shared_group, *m_history, m_binding_context.get()); m_notifier->notify_others(); } @@ -277,7 +279,7 @@ void Realm::cancel_transaction() } m_in_transaction = false; - transaction::cancel(*m_shared_group, *m_history, m_delegate.get()); + transaction::cancel(*m_shared_group, *m_history, m_binding_context.get()); } void Realm::invalidate() @@ -296,16 +298,6 @@ void Realm::invalidate() m_group = nullptr; } -void Realm::close() -{ - invalidate(); - if (m_notifier) { - m_notifier->remove_realm(this); - m_notifier = nullptr; - } - m_delegate = nullptr; -} - bool Realm::compact() { verify_thread(); @@ -332,15 +324,15 @@ void Realm::notify() verify_thread(); if (m_shared_group->has_changed()) { // Throws - if (m_delegate) { - m_delegate->changes_available(); + if (m_binding_context) { + m_binding_context->changes_available(); } if (m_auto_refresh) { if (m_group) { - transaction::advance(*m_shared_group, *m_history, m_delegate.get()); + transaction::advance(*m_shared_group, *m_history, m_binding_context.get()); } - else if (m_delegate) { - m_delegate->did_change({}, {}); + else if (m_binding_context) { + m_binding_context->did_change({}, {}); } } } @@ -363,7 +355,7 @@ bool Realm::refresh() } if (m_group) { - transaction::advance(*m_shared_group, *m_history, m_delegate.get()); + transaction::advance(*m_shared_group, *m_history, m_binding_context.get()); } else { // Create the read transaction @@ -382,6 +374,22 @@ uint64_t Realm::get_schema_version(const realm::Realm::Config &config) return ObjectStore::get_schema_version(Realm(config).read_group()); } +void Realm::close() +{ + invalidate(); + + if (m_notifier) { + m_notifier->remove_realm(this); + } + + m_group = nullptr; + m_shared_group = nullptr; + m_history = nullptr; + m_read_only_group = nullptr; + m_notifier = nullptr; + m_binding_context = nullptr; +} + SharedRealm RealmCache::get_realm(const std::string &path, std::thread::id thread_id) { std::lock_guard lock(m_mutex); @@ -451,23 +459,16 @@ void RealmCache::cache_realm(SharedRealm &realm, std::thread::id thread_id) } } -void RealmCache::close_all(std::thread::id thread_id) +void RealmCache::clear() { std::lock_guard lock(m_mutex); - - for (auto &path_realms : m_cache) { - auto thread_realm = path_realms.second.find(thread_id); - if (thread_realm != path_realms.second.end()) { - if (auto realm = thread_realm->second.lock()) { + for (auto const& path : m_cache) { + for (auto const& thread : path.second) { + if (auto realm = thread.second.lock()) { realm->close(); } } } -} - -void RealmCache::clear() -{ - std::lock_guard lock(m_mutex); m_cache.clear(); } diff --git a/shared_realm.hpp b/shared_realm.hpp index 45561811..81b14bf7 100644 --- a/shared_realm.hpp +++ b/shared_realm.hpp @@ -30,7 +30,7 @@ namespace realm { class ClientHistory; class Realm; class RealmCache; - class RealmDelegate; + class BindingContext; typedef std::shared_ptr SharedRealm; typedef std::weak_ptr WeakRealm; @@ -97,11 +97,14 @@ namespace realm { void invalidate(); bool compact(); - void close(); std::thread::id thread_id() const { return m_thread_id; } void verify_thread() const; + // Close this Realm and remove it from the cache. Continuing to use a + // Realm after closing it will produce undefined behavior. + void close(); + ~Realm(); private: @@ -121,7 +124,7 @@ namespace realm { std::shared_ptr<_impl::ExternalCommitHelper> m_notifier; public: - std::unique_ptr m_delegate; + std::unique_ptr m_binding_context; // FIXME private Group *read_group(); @@ -135,7 +138,6 @@ namespace realm { SharedRealm get_any_realm(const std::string &path); void remove(const std::string &path, std::thread::id thread_id); void cache_realm(SharedRealm &realm, std::thread::id thread_id = std::this_thread::get_id()); - void close_all(std::thread::id thread_id = std::this_thread::get_id()); void clear(); private: @@ -153,7 +155,7 @@ namespace realm { /** Thrown if the user does not have permission to open or create the specified file in the specified access mode when the realm is opened. */ PermissionDenied, - /** Thrown if no_create was specified and the file did already exist when the realm is opened. */ + /** Thrown if create_Always was specified and the file did already exist when the realm is opened. */ Exists, /** Thrown if no_create was specified and the file was not found when the realm is opened. */ NotFound, @@ -162,11 +164,14 @@ namespace realm { architecture mismatch. */ IncompatibleLockFile, }; - RealmFileException(Kind kind, std::string message) : std::runtime_error(message), m_kind(kind) {} + RealmFileException(Kind kind, std::string path, std::string message) : + std::runtime_error(std::move(message)), m_kind(kind), m_path(std::move(path)) {} Kind kind() const { return m_kind; } + const std::string& path() const { return m_path; } private: Kind m_kind; + std::string m_path; }; class MismatchedConfigException : public std::runtime_error