Skip to content

Commit

Permalink
Fix a crash when beginning an async write triggers notifications whic…
Browse files Browse the repository at this point in the history
…h close the Realm

Beginning a write transaction can result in the Realm being closed as it can
trigger notifications, and the async write loop was missing a check for this.
  • Loading branch information
tgoyne committed Apr 27, 2023
1 parent 44e1793 commit 370bac4
Show file tree
Hide file tree
Showing 3 changed files with 51 additions and 13 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
* <How do the end-user experience this issue? what was the impact?> ([#????](https://github.com/realm/realm-core/issues/????), since v?.?.?)
* Performing a query like "{1, 2, 3, ...} IN list" where the array is longer than 8 and all elements are smaller than some values in list, the program would crash ([#1183](https://github.com/realm/realm-kotlin/issues/1183), v12.5.0)
* Performing a large number of queries without ever performing a write resulted in steadily increasing memory usage, some of which was never fully freed due to an unbounded cache ([Swift #7978](https://github.com/realm/realm-swift/issues/7978), since v12.0.0)
* Fix a null pointer dereference if beginning an async write transaction refreshed the Realm and one of the notification handlers closed the Realm ([PR #6548](https://github.com/realm/realm-core/pull/6548), since v11.8.0).

### Breaking changes
* None.
Expand Down
6 changes: 6 additions & 0 deletions src/realm/object-store/shared_realm.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -796,6 +796,12 @@ void Realm::run_writes()

do_begin_transaction();

// Beginning the transaction may have delivered notifications, which
// then may have closed the Realm.
if (!m_transaction) {
return;
}

auto write_desc = std::move(m_async_write_q.front());
m_async_write_q.pop_front();

Expand Down
57 changes: 44 additions & 13 deletions test/object-store/realm.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1759,31 +1759,62 @@ TEST_CASE("SharedRealm: async writes") {
REQUIRE(complete_count == 1);
verify_persisted_count(1);
}
SECTION("within did_change()") {
struct Context : public BindingContext {
int i;
Context(int i)
: i(i)
{
}
void did_change(std::vector<ObserverState> const&, std::vector<void*> const&, bool) override
{
std::invoke(close_functions[i], *realm.lock());
}
};
realm->m_binding_context.reset(new Context(i));

struct Context : public BindingContext {
int i;
bool& called;
Context(int i, bool& called)
: i(i)
, called(called)
{
}
void did_change(std::vector<ObserverState> const&, std::vector<void*> const&, bool) override
{
called = true;
std::invoke(close_functions[i], *realm.lock());
}
};
SECTION("within did_change() after committing") {
bool called = false;
realm->m_binding_context.reset(new Context(i, called));
realm->m_binding_context->realm = realm;

realm->async_begin_transaction([&] {
table->create_object().set(col, 45);
CHECK_FALSE(called);
realm->async_commit_transaction([&](std::exception_ptr) {
CHECK(called);
done = true;
});
});

wait_for_done();
verify_persisted_count(1);
}

SECTION("within did_change() when beginning") {
realm->m_binding_context.reset(new Context(i, done));
realm->m_binding_context->realm = realm;

// Make a write on a different instance while autorefresh is
// off to ensure that beginning the transaction advances the
// read version and thus sends notifications
realm->set_auto_refresh(false);
auto realm2 = Realm::get_shared_realm(config);
realm2->begin_transaction();
realm2->commit_transaction();

bool called = false;
realm->async_begin_transaction([&] {
called = true;
});
wait_for_done();

// close() inside a notification closes the Realm, but invalidate()
// is a no-op. This means the write callback should be invoked
// if we're testing invalidate() but not if we're testing close().
REQUIRE(called == i);
}
}
}

Expand Down

0 comments on commit 370bac4

Please sign in to comment.