Refactor the transaction log parsers to eliminate some duplication

This commit is contained in:
Thomas Goyne 2016-03-04 10:54:13 -08:00
parent 8c94cd1b2c
commit d46f2c65ba
2 changed files with 75 additions and 88 deletions

View File

@ -29,9 +29,30 @@
using namespace realm;
namespace {
// A transaction log handler that just validates that all operations made are
// ones supported by the object store
class TransactLogValidator {
template<typename Derived>
struct MarkDirtyMixin {
bool mark_dirty(size_t row, size_t col) { static_cast<Derived *>(this)->mark_dirty(row, col); return true; }
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_olddatetime(size_t col, size_t row, OldDateTime) { return mark_dirty(row, col); }
bool set_timestamp(size_t col, size_t row, Timestamp) { 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, 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, size_t) { return mark_dirty(row, col); }
bool set_int_unique(size_t col, size_t row, size_t, int_fast64_t) { return mark_dirty(row, col); }
bool set_string_unique(size_t col, size_t row, size_t, StringData) { return mark_dirty(row, col); }
bool insert_substring(size_t col, size_t row, size_t, StringData) { return mark_dirty(row, col); }
bool erase_substring(size_t col, size_t row, size_t, size_t) { return mark_dirty(row, col); }
};
class TransactLogValidationMixin {
// Index of currently selected table
size_t m_current_table = 0;
@ -120,30 +141,20 @@ public:
bool link_list_clear(size_t) { return true; }
bool link_list_move(size_t, size_t) { return true; }
bool link_list_swap(size_t, size_t) { return true; }
bool set_int(size_t, size_t, int_fast64_t) { return true; }
bool set_bool(size_t, size_t, bool) { return true; }
bool set_float(size_t, size_t, float) { return true; }
bool set_double(size_t, size_t, double) { return true; }
bool set_string(size_t, size_t, StringData) { return true; }
bool set_binary(size_t, size_t, BinaryData) { return true; }
bool set_olddatetime(size_t, size_t, OldDateTime) { return true; }
bool set_timestamp(size_t, size_t, Timestamp) { return true; }
bool set_table(size_t, size_t) { return true; }
bool set_mixed(size_t, size_t, const Mixed&) { return true; }
bool set_link(size_t, size_t, size_t, size_t) { return true; }
bool set_null(size_t, size_t) { return true; }
bool nullify_link(size_t, size_t, size_t) { return true; }
bool insert_substring(size_t, size_t, size_t, StringData) { return true; }
bool erase_substring(size_t, size_t, size_t, size_t) { return true; }
bool optimize_table() { return true; }
bool set_int_unique(size_t, size_t, size_t, int_fast64_t) { return true; }
bool set_string_unique(size_t, size_t, size_t, StringData) { return true; }
bool change_link_targets(size_t, size_t) { return true; }
bool optimize_table() { return true; }
};
// A transaction log handler that just validates that all operations made are
// ones supported by the object store
struct TransactLogValidator : public TransactLogValidationMixin, public MarkDirtyMixin<TransactLogValidator> {
void mark_dirty(size_t, size_t) { }
};
// Extends TransactLogValidator to also track changes and report it to the
// binding context if any properties are being observed
class TransactLogObserver : public TransactLogValidator {
class TransactLogObserver : public TransactLogValidationMixin, public MarkDirtyMixin<TransactLogObserver> {
using ColumnInfo = BindingContext::ColumnInfo;
using ObserverState = BindingContext::ObserverState;
@ -182,16 +193,6 @@ class TransactLogObserver : public TransactLogValidator {
}
}
// 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{current_table(), row_ndx, nullptr});
if (it != end(m_observers) && it->table_ndx == 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)
@ -207,10 +208,7 @@ public:
{
if (!context) {
if (validate_schema_changes) {
// The handler functions are non-virtual, so the parent class's
// versions are called if we don't need to track changes to observed
// objects
func(static_cast<TransactLogValidator&>(*this));
func(TransactLogValidator());
}
else {
func();
@ -222,7 +220,7 @@ public:
if (m_observers.empty()) {
auto old_version = sg.get_version_of_current_transaction();
if (validate_schema_changes) {
func(static_cast<TransactLogValidator&>(*this));
func(TransactLogValidator());
}
else {
func();
@ -237,6 +235,15 @@ public:
context->did_change(m_observers, invalidated);
}
// Mark the given row/col as needing notifications sent
void mark_dirty(size_t row_ndx, size_t col_ndx)
{
auto it = lower_bound(begin(m_observers), end(m_observers), ObserverState{current_table(), row_ndx, nullptr});
if (it != end(m_observers) && it->table_ndx == current_table() && it->row_ndx == row_ndx) {
get_change(*it, col_ndx).changed = true;
}
}
// Called at the end of the transaction log immediately before the version
// is advanced
void parse_complete()
@ -250,7 +257,7 @@ public:
if (observer.table_ndx >= table_ndx)
++observer.table_ndx;
}
TransactLogValidator::insert_group_level_table(table_ndx, prior_size, name);
TransactLogValidationMixin::insert_group_level_table(table_ndx, prior_size, name);
return true;
}
@ -411,29 +418,10 @@ public:
}
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_olddatetime(size_t col, size_t row, OldDateTime) { return mark_dirty(row, col); }
bool set_timestamp(size_t col, size_t row, Timestamp) { 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, 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, size_t) { return mark_dirty(row, col); }
bool set_int_unique(size_t col, size_t row, size_t, int_fast64_t) { return mark_dirty(row, col); }
bool set_string_unique(size_t col, size_t row, size_t, StringData) { return mark_dirty(row, col); }
bool insert_substring(size_t col, size_t row, size_t, StringData) { return mark_dirty(row, col); }
bool erase_substring(size_t col, size_t row, size_t, size_t) { return mark_dirty(row, col); }
};
// Extends TransactLogValidator to track changes made to LinkViews
class LinkViewObserver : public TransactLogValidator {
class LinkViewObserver : public TransactLogValidationMixin, public MarkDirtyMixin<LinkViewObserver> {
_impl::TransactionChangeInfo& m_info;
CollectionChangeIndices* m_active = nullptr;
@ -448,17 +436,16 @@ class LinkViewObserver : public TransactLogValidator {
return &m_info.tables[tbl_ndx];
}
bool mark_dirty(size_t row, __unused size_t col)
{
if (auto change = get_change())
change->modify(row);
return true;
}
public:
LinkViewObserver(_impl::TransactionChangeInfo& info)
: m_info(info) { }
void mark_dirty(size_t row, __unused size_t col)
{
if (auto change = get_change())
change->modify(row);
}
bool select_link_list(size_t col, size_t row, size_t)
{
mark_dirty(row, col);
@ -563,25 +550,6 @@ public:
change->clear(0); // FIXME
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_olddatetime(size_t col, size_t row, OldDateTime) { return mark_dirty(row, col); }
bool set_timestamp(size_t col, size_t row, Timestamp) { 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, 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, size_t) { return mark_dirty(row, col); }
bool insert_substring(size_t col, size_t row, size_t, StringData) { return mark_dirty(row, col); }
bool erase_substring(size_t col, size_t row, size_t, size_t) { return mark_dirty(row, col); }
bool set_int_unique(size_t col, size_t row, size_t, int_fast64_t) { return mark_dirty(row, col); }
bool set_string_unique(size_t col, size_t row, size_t, StringData) { return mark_dirty(row, col); }
};
} // anonymous namespace

View File

@ -363,8 +363,28 @@ TEST_CASE("Async Results error handling") {
auto coordinator = _impl::RealmCoordinator::get_existing_coordinator(config.path);
Results results(r, *config.schema->find("object"), *r->read_group()->get_table("class_object"));
class OpenFileLimiter {
public:
OpenFileLimiter()
{
// Set the max open files to zero so that opening new files will fail
getrlimit(RLIMIT_NOFILE, &m_old);
rlimit rl = m_old;
rl.rlim_cur = 0;
setrlimit(RLIMIT_NOFILE, &rl);
}
~OpenFileLimiter()
{
setrlimit(RLIMIT_NOFILE, &m_old);
}
private:
rlimit m_old;
};
SECTION("error when opening the advancer SG") {
unlink(config.path.c_str());
OpenFileLimiter limiter;
SECTION("error is delivered asynchronously") {
bool called = false;
@ -412,7 +432,7 @@ TEST_CASE("Async Results error handling") {
REQUIRE(err);
called = true;
});
unlink(config.path.c_str());
OpenFileLimiter limiter;
REQUIRE(!called);
coordinator->on_change();
@ -428,7 +448,7 @@ TEST_CASE("Async Results error handling") {
REQUIRE_FALSE(called);
called = true;
});
unlink(config.path.c_str());
OpenFileLimiter limiter;
coordinator->on_change();
r->notify();
@ -445,6 +465,5 @@ TEST_CASE("Async Results error handling") {
REQUIRE(called2);
}
}
}