From 766178d30d21ece705f4844bb6b0ba349847fd76 Mon Sep 17 00:00:00 2001 From: Thomas Goyne Date: Wed, 2 Sep 2015 13:51:28 -0700 Subject: [PATCH] Shuffle stuff around and clean some things up --- index_set.cpp | 40 +--- index_set.hpp | 42 ++-- realm_delegate.hpp | 22 +- src/object-store/shared_realm.cpp | 298 +------------------------ transact_log_handler.cpp | 347 ++++++++++++++++++++++++++++++ transact_log_handler.hpp | 46 ++++ 6 files changed, 441 insertions(+), 354 deletions(-) create mode 100644 transact_log_handler.cpp create mode 100644 transact_log_handler.hpp diff --git a/index_set.cpp b/index_set.cpp index 3fe27a46..b7e4961c 100644 --- a/index_set.cpp +++ b/index_set.cpp @@ -20,16 +20,7 @@ using namespace realm; -size_t IndexSet::size() const -{ - size_t size = 0; - for (auto const& range : m_ranges) { - size += range.second - range.first; - } - return size; -} - -std::vector::iterator IndexSet::find(size_t index) +IndexSet::iterator IndexSet::find(size_t index) { for (auto it = m_ranges.begin(), end = m_ranges.end(); it != end; ++it) { if (it->second > index) @@ -43,14 +34,14 @@ void IndexSet::add(size_t index) do_add(find(index), index); } -void IndexSet::do_add(std::vector::iterator it, size_t index) +void IndexSet::do_add(iterator it, size_t index) { bool more_before = it != m_ranges.begin(), valid = it != m_ranges.end(); if (valid && it->first <= index && it->second > index) { // index is already in set } else if (more_before && (it - 1)->second == index) { - // index is immediate after an existing range + // index is immediately after an existing range ++(it - 1)->second; } else if (more_before && valid && (it - 1)->second == it->first) { @@ -99,28 +90,3 @@ void IndexSet::add_shifted(size_t index) } do_add(it, index); } - -size_t IndexSet::iterator::operator*() const -{ - return m_data->first + m_offset; -} - -IndexSet::iterator& IndexSet::iterator::operator++() -{ - ++m_offset; - if (m_offset + m_data->first == m_data->second) { - ++m_data; - m_offset = 0; - } - return *this; -} - -bool IndexSet::iterator::operator==(iterator other) const -{ - return m_data == other.m_data && m_offset == other.m_offset; -} - -bool IndexSet::iterator::operator!=(iterator other) const -{ - return m_data != other.m_data || m_offset != other.m_offset; -} diff --git a/index_set.hpp b/index_set.hpp index 03d036fe..7a229177 100644 --- a/index_set.hpp +++ b/index_set.hpp @@ -24,40 +24,38 @@ namespace realm { class IndexSet { public: - struct iterator { - size_t operator*() const; - iterator& operator++(); - bool operator==(iterator) const; - bool operator!=(iterator) const; + using value_type = std::pair; + using iterator = std::vector::iterator; + using const_iterator = std::vector::const_iterator; - iterator(std::pair* data) noexcept : m_data(data) { } - - private: - std::pair* m_data; - size_t m_offset = 0; - }; - - iterator begin() { return iterator(&m_ranges[0]); } - iterator end() { return iterator(&m_ranges[m_ranges.size()]); } - - size_t size() const; + const_iterator begin() const { return m_ranges.begin(); } + const_iterator end() const { return m_ranges.end(); } + bool empty() const { return m_ranges.empty(); } + size_t size() const { return m_ranges.size(); } // Add an index to the set, doing nothing if it's already present void add(size_t index); - // Set the index set to a single range starting at 0 with length `len` + + // Remove all indexes from the set and then add a single range starting from + // zero with the given length void set(size_t len); - // Insert an index at the given position, shifting existing indexes back + + // Insert an index at the given position, shifting existing indexes at or + // after that point back by one void insert_at(size_t index); + + // Add an index which has had all of the ranges in the set before it removed void add_shifted(size_t index); private: - using Range = std::pair; - std::vector m_ranges; + std::vector m_ranges; // Find the range which contains the index, or the first one after it if // none do - std::vector::iterator find(size_t index); - void do_add(std::vector::iterator pos, size_t index); + iterator find(size_t index); + // Insert the index before the given position, combining existing ranges as + // applicable + void do_add(iterator pos, size_t index); }; } // namespace realm diff --git a/realm_delegate.hpp b/realm_delegate.hpp index 2b4f5ba6..c3a41d04 100644 --- a/realm_delegate.hpp +++ b/realm_delegate.hpp @@ -29,6 +29,7 @@ class RealmDelegate { public: virtual ~RealmDelegate() = default; + // Change information for a single field of a row struct ColumnInfo { bool changed = false; enum class Kind { @@ -41,10 +42,19 @@ public: IndexSet indices; }; + // Information about an observed row in a table struct ObserverState { + // Initial table and row which is observed + // May be updated by row insertions and removals size_t table_ndx; size_t row_ndx; - void* info; // opaque user info + + // Opaque userdata for the delegate's use + void* info; + + // Populated with information about which columns were changed + // May be shorter than the actual number of columns if the later columns + // are not modified std::vector changes; // Simple lexographic ordering @@ -68,10 +78,16 @@ public: // The Realm's read version will change // Only called if get_observed_row() returned a non-empty array. - virtual void will_change(std::vector const&, std::vector const&) = 0; + // observers is the vector returned from get_observed_rows() + // invalidated is the `info` pointers for each observed object which was deleted + virtual void will_change(std::vector const& observers, + std::vector const& invalidated) = 0; // The Realm's read version has changed - virtual void did_change(std::vector const&, std::vector const&) = 0; + // observers is the vector returned from get_observed_rows() + // invalidated is the `info` pointers for each observed object which was deleted + virtual void did_change(std::vector const& observers, + std::vector const& invalidated) = 0; }; } // namespace realm diff --git a/src/object-store/shared_realm.cpp b/src/object-store/shared_realm.cpp index 8f3e416b..7d270cc2 100644 --- a/src/object-store/shared_realm.cpp +++ b/src/object-store/shared_realm.cpp @@ -19,288 +19,15 @@ #include "shared_realm.hpp" #include "realm_delegate.hpp" +#include "transact_log_handler.hpp" #include #include -#include #include -#include using namespace realm; -namespace { -class TransactLogHandler { - using ColumnInfo = RealmDelegate::ColumnInfo; - using ObserverState = RealmDelegate::ObserverState; - - size_t currentTable = 0; - std::vector observers; - std::vector invalidated; - ColumnInfo *activeLinkList = nullptr; - RealmDelegate* m_delegate; - - // Get the change info for the given column, creating it if needed - static ColumnInfo& get_change(ObserverState& state, size_t i) - { - if (state.changes.size() <= i) { - state.changes.resize(std::max(state.changes.size() * 2, i + 1)); - } - return state.changes[i]; - } - - // Loop over the columns which were changed in an observer state - template - static void for_each(ObserverState& state, Func&& f) - { - for (size_t i = 0; i < state.changes.size(); ++i) { - auto const& change = state.changes[i]; - if (change.changed) { - f(i, change); - } - } - } - - // Mark the given row/col as needing notifications sent - bool mark_dirty(size_t row_ndx, size_t col_ndx) - { - auto it = lower_bound(begin(observers), end(observers), ObserverState{currentTable, row_ndx, nullptr}); - if (it != end(observers) && it->table_ndx == currentTable && it->row_ndx == row_ndx) { - get_change(*it, col_ndx).changed = true; - } - return true; - } - - // Remove the given observer from the list of observed objects and add it - // to the listed of invalidated objects - void invalidate(ObserverState *o) - { - invalidated.push_back(o->info); - observers.erase(observers.begin() + (o - &observers[0])); - } - -public: - template - TransactLogHandler(RealmDelegate* delegate, SharedGroup& sg, Func&& func) - : m_delegate(delegate) - { - if (!delegate) { - func(); - return; - } - - observers = delegate->get_observed_rows(); - if (observers.empty()) { - auto old_version = sg.get_version_of_current_transaction(); - func(); - if (old_version != sg.get_version_of_current_transaction()) { - delegate->did_change({}, {}); - } - return; - } - - func(*this); - delegate->did_change(observers, invalidated); - } - - // Called at the end of the transaction log immediately before the version - // is advanced - void parse_complete() - { - m_delegate->will_change(observers, invalidated); - } - - // These would require having an observer before schema init - // Maybe do something here to throw an error when multiple processes have different schemas? - bool insert_group_level_table(size_t, size_t, StringData) noexcept { return false; } - bool erase_group_level_table(size_t, size_t) noexcept { return false; } - bool rename_group_level_table(size_t, StringData) noexcept { return false; } - bool insert_column(size_t, DataType, StringData, bool) { return false; } - bool insert_link_column(size_t, DataType, StringData, size_t, size_t) { return false; } - bool erase_column(size_t) { return false; } - bool erase_link_column(size_t, size_t, size_t) { return false; } - bool rename_column(size_t, StringData) { return false; } - bool add_search_index(size_t) { return false; } - bool remove_search_index(size_t) { return false; } - bool add_primary_key(size_t) { return false; } - bool remove_primary_key() { return false; } - bool set_link_type(size_t, LinkType) { return false; } - - bool select_table(size_t group_level_ndx, int, const size_t*) noexcept { - currentTable = group_level_ndx; - return true; - } - - bool insert_empty_rows(size_t, size_t, size_t, bool) { - // rows are only inserted at the end, so no need to do anything - return true; - } - - bool erase_rows(size_t row_ndx, size_t, size_t last_row_ndx, bool unordered) noexcept { - for (size_t i = 0; i < observers.size(); ++i) { - auto& o = observers[i]; - if (o.table_ndx == currentTable) { - if (o.row_ndx == row_ndx) { - invalidate(&o); - --i; - } - else if (unordered && o.row_ndx == last_row_ndx) { - o.row_ndx = row_ndx; - } - else if (!unordered && o.row_ndx > row_ndx) { - o.row_ndx -= 1; - } - } - } - return true; - } - - bool clear_table() noexcept { - for (size_t i = 0; i < observers.size(); ) { - auto& o = observers[i]; - if (o.table_ndx == currentTable) { - invalidate(&o); - } - else { - ++i; - } - } - return true; - } - - bool select_link_list(size_t col, size_t row) { - activeLinkList = nullptr; - for (auto& o : observers) { - if (o.table_ndx == currentTable && o.row_ndx == row) { - activeLinkList = &get_change(o, col); - break; - } - } - return true; - } - - void append_link_list_change(ColumnInfo::Kind kind, size_t index) { - ColumnInfo *o = activeLinkList; - if (!o || o->kind == ColumnInfo::Kind::SetAll) { - // Active LinkList isn't observed or already has multiple kinds of changes - return; - } - - if (o->kind == ColumnInfo::Kind::None) { - o->kind = kind; - o->changed = true; - o->indices.add(index); - } - else if (o->kind == kind) { - if (kind == ColumnInfo::Kind::Remove) { - o->indices.add_shifted(index); - } - else if (kind == ColumnInfo::Kind::Insert) { - o->indices.insert_at(index); - } - else { - o->indices.add(index); - } - } - else { - // Array KVO can only send a single kind of change at a time, so - // if there's multiple just give up and send "Set" - o->indices.set(0); - o->kind = ColumnInfo::Kind::SetAll; - } - } - - bool link_list_set(size_t index, size_t) { - append_link_list_change(ColumnInfo::Kind::Set, index); - return true; - } - - bool link_list_insert(size_t index, size_t) { - append_link_list_change(ColumnInfo::Kind::Insert, index); - return true; - } - - bool link_list_erase(size_t index) { - append_link_list_change(ColumnInfo::Kind::Remove, index); - return true; - } - - bool link_list_nullify(size_t index) { - append_link_list_change(ColumnInfo::Kind::Remove, index); - return true; - } - - bool link_list_swap(size_t index1, size_t index2) { - append_link_list_change(ColumnInfo::Kind::Set, index1); - append_link_list_change(ColumnInfo::Kind::Set, index2); - return true; - } - - bool link_list_clear(size_t old_size) { - ColumnInfo *o = activeLinkList; - if (!o || o->kind == ColumnInfo::Kind::SetAll) { - return true; - } - - if (o->kind == ColumnInfo::Kind::Remove) - old_size += o->indices.size(); - else if (o->kind == ColumnInfo::Kind::Insert) - old_size -= o->indices.size(); - - o->indices.set(old_size); - - o->kind = ColumnInfo::Kind::Remove; - o->changed = true; - return true; - } - - bool link_list_move(size_t from, size_t to) { - ColumnInfo *o = activeLinkList; - if (!o || o->kind == ColumnInfo::Kind::SetAll) { - return true; - } - if (from > to) { - std::swap(from, to); - } - - if (o->kind == ColumnInfo::Kind::None) { - o->kind = ColumnInfo::Kind::Set; - o->changed = true; - } - if (o->kind == ColumnInfo::Kind::Set) { - for (size_t i = from; i <= to; ++i) - o->indices.add(i); - } - else { - o->indices.set(0); - o->kind = ColumnInfo::Kind::SetAll; - } - return true; - } - - // Things that just mark the field as modified - bool set_int(size_t col, size_t row, int_fast64_t) { return mark_dirty(row, col); } - bool set_bool(size_t col, size_t row, bool) { return mark_dirty(row, col); } - bool set_float(size_t col, size_t row, float) { return mark_dirty(row, col); } - bool set_double(size_t col, size_t row, double) { return mark_dirty(row, col); } - bool set_string(size_t col, size_t row, StringData) { return mark_dirty(row, col); } - bool set_binary(size_t col, size_t row, BinaryData) { return mark_dirty(row, col); } - 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_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); } - - // Things we don't need to do anything for - bool optimize_table() { return false; } - - // Things that we don't do in the binding - bool select_descriptor(int, const size_t*) { return true; } - bool add_int_to_column(size_t, int_fast64_t) { return false; } -}; -} - RealmCache Realm::s_global_cache; Realm::Config::Config(const Config& c) @@ -492,9 +219,7 @@ void Realm::begin_transaction() // make sure we have a read transaction read_group(); - TransactLogHandler(m_delegate.get(), *m_shared_group, [&](auto&&... args) { - LangBindHelper::promote_to_write(*m_shared_group, *m_history, std::move(args)...); - }); + transaction::begin(*m_shared_group, *m_history, m_delegate.get()); m_in_transaction = true; } @@ -507,12 +232,8 @@ void Realm::commit_transaction() throw InvalidTransactionException("Can't commit a non-existing write transaction"); } - LangBindHelper::commit_and_continue_as_read(*m_shared_group); m_in_transaction = false; - - if (m_delegate) { - m_delegate->transaction_committed(); - } + transaction::commit(*m_shared_group, *m_history, m_delegate.get()); } void Realm::cancel_transaction() @@ -524,13 +245,10 @@ void Realm::cancel_transaction() throw InvalidTransactionException("Can't cancel a non-existing write transaction"); } - TransactLogHandler(m_delegate.get(), *m_shared_group, [&](auto&&... args) { - LangBindHelper::rollback_and_continue_as_read(*m_shared_group, *m_history, std::move(args)...); - }); m_in_transaction = false; + transaction::cancel(*m_shared_group, *m_history, m_delegate.get()); } - void Realm::invalidate() { verify_thread(); @@ -574,9 +292,7 @@ void Realm::notify() if (m_shared_group->has_changed()) { // Throws if (m_auto_refresh) { if (m_group) { - TransactLogHandler(m_delegate.get(), *m_shared_group, [&](auto&&... args) { - LangBindHelper::advance_read(*m_shared_group, *m_history, std::move(args)...); - }); + transaction::advance(*m_shared_group, *m_history, m_delegate.get()); } else if (m_delegate) { m_delegate->did_change({}, {}); @@ -605,9 +321,7 @@ bool Realm::refresh() } if (m_group) { - TransactLogHandler(m_delegate.get(), *m_shared_group, [&](auto&&... args) { - LangBindHelper::advance_read(*m_shared_group, *m_history, std::move(args)...); - }); + transaction::advance(*m_shared_group, *m_history, m_delegate.get()); } else { // Create the read transaction diff --git a/transact_log_handler.cpp b/transact_log_handler.cpp new file mode 100644 index 00000000..b02fdcbd --- /dev/null +++ b/transact_log_handler.cpp @@ -0,0 +1,347 @@ +//////////////////////////////////////////////////////////////////////////// +// +// Copyright 2015 Realm Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +//////////////////////////////////////////////////////////////////////////// + +#include "transact_log_handler.hpp" + +#include "realm_delegate.hpp" + +#include +#include +#include + +using namespace realm; + +namespace { +class TransactLogHandler { + using ColumnInfo = RealmDelegate::ColumnInfo; + using ObserverState = RealmDelegate::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; + + // Index of currently selected table + size_t m_current_table = 0; + // Change information for the currently selected LinkList, if any + ColumnInfo* m_active_linklist = nullptr; + + // Get the change info for the given column, creating it if needed + static ColumnInfo& get_change(ObserverState& state, size_t i) + { + if (state.changes.size() <= i) { + state.changes.resize(std::max(state.changes.size() * 2, i + 1)); + } + return state.changes[i]; + } + + // Loop over the columns which were changed in an observer state + template + static void for_each(ObserverState& state, Func&& f) + { + for (size_t i = 0; i < state.changes.size(); ++i) { + auto const& change = state.changes[i]; + if (change.changed) { + f(i, change); + } + } + } + + // Mark the given row/col as needing notifications sent + bool mark_dirty(size_t row_ndx, size_t col_ndx) + { + auto it = lower_bound(begin(m_observers), end(m_observers), ObserverState{m_current_table, row_ndx, nullptr}); + if (it != end(m_observers) && it->table_ndx == m_current_table && it->row_ndx == row_ndx) { + get_change(*it, col_ndx).changed = true; + } + return true; + } + + // Remove the given observer from the list of observed objects and add it + // to the listed of invalidated objects + void invalidate(ObserverState *o) + { + invalidated.push_back(o->info); + m_observers.erase(m_observers.begin() + (o - &m_observers[0])); + } + +public: + template + TransactLogHandler(RealmDelegate* delegate, SharedGroup& sg, Func&& func) + : m_delegate(delegate) + { + if (!delegate) { + func(); + return; + } + + m_observers = delegate->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({}, {}); + } + return; + } + + func(*this); + delegate->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); + } + + // These would require having an observer before schema init + // Maybe do something here to throw an error when multiple processes have different schemas? + bool insert_group_level_table(size_t, size_t, StringData) { return false; } + bool erase_group_level_table(size_t, size_t) { return false; } + bool rename_group_level_table(size_t, StringData) { return false; } + bool insert_column(size_t, DataType, StringData, bool) { return false; } + bool insert_link_column(size_t, DataType, StringData, size_t, size_t) { return false; } + bool erase_column(size_t) { return false; } + bool erase_link_column(size_t, size_t, size_t) { return false; } + bool rename_column(size_t, StringData) { return false; } + bool add_search_index(size_t) { return false; } + bool remove_search_index(size_t) { return false; } + bool add_primary_key(size_t) { return false; } + bool remove_primary_key() { return false; } + bool set_link_type(size_t, LinkType) { return false; } + + bool select_table(size_t group_level_ndx, int, const size_t*) noexcept + { + m_current_table = group_level_ndx; + return true; + } + + bool insert_empty_rows(size_t, size_t, size_t, bool) + { + // rows are only inserted at the end, so no need to do anything + return true; + } + + bool erase_rows(size_t row_ndx, size_t, size_t last_row_ndx, bool unordered) + { + for (size_t i = 0; i < m_observers.size(); ++i) { + auto& o = m_observers[i]; + if (o.table_ndx == m_current_table) { + if (o.row_ndx == row_ndx) { + invalidate(&o); + --i; + } + else if (unordered && o.row_ndx == last_row_ndx) { + o.row_ndx = row_ndx; + } + else if (!unordered && o.row_ndx > row_ndx) { + o.row_ndx -= 1; + } + } + } + return true; + } + + bool clear_table() + { + for (size_t i = 0; i < m_observers.size(); ) { + auto& o = m_observers[i]; + if (o.table_ndx == m_current_table) { + invalidate(&o); + } + else { + ++i; + } + } + return true; + } + + bool select_link_list(size_t col, size_t row) + { + m_active_linklist = nullptr; + for (auto& o : m_observers) { + if (o.table_ndx == m_current_table && o.row_ndx == row) { + m_active_linklist = &get_change(o, col); + break; + } + } + return true; + } + + void append_link_list_change(ColumnInfo::Kind kind, size_t index) { + ColumnInfo *o = m_active_linklist; + if (!o || o->kind == ColumnInfo::Kind::SetAll) { + // Active LinkList isn't observed or already has multiple kinds of changes + return; + } + + if (o->kind == ColumnInfo::Kind::None) { + o->kind = kind; + o->changed = true; + o->indices.add(index); + } + else if (o->kind == kind) { + if (kind == ColumnInfo::Kind::Remove) { + o->indices.add_shifted(index); + } + else if (kind == ColumnInfo::Kind::Insert) { + o->indices.insert_at(index); + } + else { + o->indices.add(index); + } + } + else { + // Array KVO can only send a single kind of change at a time, so + // if there's multiple just give up and send "Set" + o->indices.set(0); + o->kind = ColumnInfo::Kind::SetAll; + } + } + + bool link_list_set(size_t index, size_t) + { + append_link_list_change(ColumnInfo::Kind::Set, index); + return true; + } + + bool link_list_insert(size_t index, size_t) + { + append_link_list_change(ColumnInfo::Kind::Insert, index); + return true; + } + + bool link_list_erase(size_t index) + { + append_link_list_change(ColumnInfo::Kind::Remove, index); + return true; + } + + bool link_list_nullify(size_t index) + { + append_link_list_change(ColumnInfo::Kind::Remove, index); + return true; + } + + bool link_list_swap(size_t index1, size_t index2) + { + append_link_list_change(ColumnInfo::Kind::Set, index1); + append_link_list_change(ColumnInfo::Kind::Set, index2); + return true; + } + + bool link_list_clear(size_t old_size) + { + ColumnInfo *o = m_active_linklist; + if (!o || o->kind == ColumnInfo::Kind::SetAll) { + return true; + } + + if (o->kind == ColumnInfo::Kind::Remove) + old_size += o->indices.size(); + else if (o->kind == ColumnInfo::Kind::Insert) + old_size -= o->indices.size(); + + o->indices.set(old_size); + + o->kind = ColumnInfo::Kind::Remove; + o->changed = true; + return true; + } + + bool link_list_move(size_t from, size_t to) + { + ColumnInfo *o = m_active_linklist; + if (!o || o->kind == ColumnInfo::Kind::SetAll) { + return true; + } + if (from > to) { + std::swap(from, to); + } + + if (o->kind == ColumnInfo::Kind::None) { + o->kind = ColumnInfo::Kind::Set; + o->changed = true; + } + if (o->kind == ColumnInfo::Kind::Set) { + for (size_t i = from; i <= to; ++i) + o->indices.add(i); + } + else { + o->indices.set(0); + o->kind = ColumnInfo::Kind::SetAll; + } + return true; + } + + // Things that just mark the field as modified + bool set_int(size_t col, size_t row, int_fast64_t) { return mark_dirty(row, col); } + bool set_bool(size_t col, size_t row, bool) { return mark_dirty(row, col); } + bool set_float(size_t col, size_t row, float) { return mark_dirty(row, col); } + bool set_double(size_t col, size_t row, double) { return mark_dirty(row, col); } + bool set_string(size_t col, size_t row, StringData) { return mark_dirty(row, col); } + bool set_binary(size_t col, size_t row, BinaryData) { return mark_dirty(row, col); } + 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_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); } + + // 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; } +}; +} // anonymous namespace + +namespace realm { +namespace transaction { +void advance(SharedGroup& sg, ClientHistory& history, RealmDelegate* delegate) { + TransactLogHandler(delegate, sg, [&](auto&&... args) { + LangBindHelper::advance_read(sg, history, std::move(args)...); + }); +} + +void begin(SharedGroup& sg, ClientHistory& history, RealmDelegate* delegate) { + TransactLogHandler(delegate, sg, [&](auto&&... args) { + LangBindHelper::promote_to_write(sg, history, std::move(args)...); + }); +} + +void commit(SharedGroup& sg, ClientHistory&, RealmDelegate* delegate) { + LangBindHelper::commit_and_continue_as_read(sg); + + if (delegate) { + delegate->transaction_committed(); + } +} + +void cancel(SharedGroup& sg, ClientHistory& history, RealmDelegate* delegate) { + TransactLogHandler(delegate, sg, [&](auto&&... args) { + LangBindHelper::rollback_and_continue_as_read(sg, history, std::move(args)...); + }); +} + +} // namespace transaction +} // namespace realm diff --git a/transact_log_handler.hpp b/transact_log_handler.hpp new file mode 100644 index 00000000..3a77848f --- /dev/null +++ b/transact_log_handler.hpp @@ -0,0 +1,46 @@ +//////////////////////////////////////////////////////////////////////////// +// +// Copyright 2015 Realm Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +//////////////////////////////////////////////////////////////////////////// + +#ifndef REALM_TRANSACT_LOG_HANDLER_HPP +#define REALM_TRANSACT_LOG_HANDLER_HPP + +namespace realm { +class RealmDelegate; +class SharedGroup; +class ClientHistory; + +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); + +// 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); + +// Commit a write transaction +void commit(SharedGroup& sg, ClientHistory& history, RealmDelegate* delegate); + +// 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); +} // namespace transaction +} // namespace realm + +#endif /* REALM_TRANSACT_LOG_HANDLER_HPP */