Skip to content

Commit

Permalink
Fix DiscardLocal client reset failure with unset callbacks (#5228)
Browse files Browse the repository at this point in the history
* fix DiscardLocal failure with unset callbacks

* extract function to reduce complexity
  • Loading branch information
ironage authored Feb 8, 2022
1 parent e95ba38 commit b7520e1
Show file tree
Hide file tree
Showing 3 changed files with 50 additions and 32 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
* Calling `Realm::close()` or `Realm::invalidate()` from the async write callbacks could result in crashes (since v11.8.0).
* Asynchronous writes did not work with queue-confined Realms (since v11.8.0).
* Releasing all references to a Realm while an asynchronous write was in progress would sometimes result in use-after-frees (since v11.8.0).
* Fixed a fatal sync error "Automatic recovery failed" during DiscardLocal client reset if the reset notifier callbacks were not set to something. ([#5223](https://github.com/realm/realm-core/issues/5223), since v11.5.0)
* Throwing exceptions from asynchronous write callbacks would result in crashes or the Realm being in an invalid state (since v11.8.0).
* Fix an error when compiling a watchOS Simulator target not supporting Thread-local storage ([#7623](https://github.com/realm/realm-swift/issues/7623), since v11.7.0)

Expand Down
73 changes: 41 additions & 32 deletions src/realm/object-store/sync/sync_session.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -529,6 +529,46 @@ void SyncSession::create_sync_session()
do_create_sync_session();
}

sync::Session::Config::ClientReset make_client_reset_config(SyncConfig& session_config, DBRef&& fresh_copy)
{
sync::Session::Config::ClientReset config;
config.discard_local = (session_config.client_resync_mode == ClientResyncMode::DiscardLocal);
config.notify_after_client_reset = [&session_config](std::string local_path, VersionID previous_version) {
REALM_ASSERT(!local_path.empty());
SharedRealm frozen_before, active_after;
if (auto local_coordinator = RealmCoordinator::get_existing_coordinator(local_path)) {
auto local_config = local_coordinator->get_config();
active_after = local_coordinator->get_realm(local_config, util::none);
local_config.scheduler = nullptr;
frozen_before = local_coordinator->get_realm(local_config, previous_version);
REALM_ASSERT(active_after);
REALM_ASSERT(!active_after->is_frozen());
REALM_ASSERT(frozen_before);
REALM_ASSERT(frozen_before->is_frozen());
}
if (session_config.notify_after_client_reset) {
session_config.notify_after_client_reset(frozen_before, active_after);
}
};
config.notify_before_client_reset = [&session_config](std::string local_path) {
REALM_ASSERT(!local_path.empty());
SharedRealm frozen_local;
Realm::Config local_config;
if (auto local_coordinator = RealmCoordinator::get_existing_coordinator(local_path)) {
local_config = local_coordinator->get_config();
local_config.scheduler = nullptr;
frozen_local = local_coordinator->get_realm(local_config, VersionID());
REALM_ASSERT(frozen_local);
REALM_ASSERT(frozen_local->is_frozen());
}
if (session_config.notify_before_client_reset) {
session_config.notify_before_client_reset(frozen_local);
}
};
config.fresh_copy = std::move(fresh_copy);
return config;
}

void SyncSession::do_create_sync_session()
{
sync::Session::Config session_config;
Expand Down Expand Up @@ -558,38 +598,7 @@ void SyncSession::do_create_sync_session()
session_config.custom_http_headers = m_config.custom_http_headers;

if (m_force_client_reset) {
sync::Session::Config::ClientReset config;
config.discard_local = (m_config.client_resync_mode == ClientResyncMode::DiscardLocal);
config.notify_after_client_reset = [this](std::string local_path, VersionID previous_version) {
REALM_ASSERT(!local_path.empty());
SharedRealm frozen_before, active_after;
if (auto local_coordinator = RealmCoordinator::get_existing_coordinator(local_path)) {
auto local_config = local_coordinator->get_config();
active_after = local_coordinator->get_realm(local_config, util::none);
local_config.scheduler = nullptr;
frozen_before = local_coordinator->get_realm(local_config, previous_version);
REALM_ASSERT(active_after);
REALM_ASSERT(!active_after->is_frozen());
REALM_ASSERT(frozen_before);
REALM_ASSERT(frozen_before->is_frozen());
}
m_config.notify_after_client_reset(frozen_before, active_after);
};
config.notify_before_client_reset = [this](std::string local_path) {
REALM_ASSERT(!local_path.empty());
SharedRealm frozen_local;
Realm::Config local_config;
if (auto local_coordinator = RealmCoordinator::get_existing_coordinator(local_path)) {
local_config = local_coordinator->get_config();
local_config.scheduler = nullptr;
frozen_local = local_coordinator->get_realm(local_config, VersionID());
REALM_ASSERT(frozen_local);
REALM_ASSERT(frozen_local->is_frozen());
}
m_config.notify_before_client_reset(frozen_local);
};
config.fresh_copy = std::move(m_client_reset_fresh_copy);
session_config.client_reset_config = std::move(config);
session_config.client_reset_config = make_client_reset_config(m_config, std::move(m_client_reset_fresh_copy));
m_force_client_reset = false;
}

Expand Down
8 changes: 8 additions & 0 deletions test/object-store/sync/client_reset.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -341,6 +341,14 @@ TEST_CASE("sync: client reset", "[client reset]") {
}
}

SECTION("can be reset without notifiers") {
local_config.sync_config->notify_before_client_reset = nullptr;
local_config.sync_config->notify_after_client_reset = nullptr;
make_reset(local_config, remote_config)->run();
REQUIRE(before_callback_invoctions == 0);
REQUIRE(after_callback_invocations == 0);
}

SECTION("an interrupted reset can recover on the next session") {
struct SessionInterruption : public std::runtime_error {
using std::runtime_error::runtime_error;
Expand Down

0 comments on commit b7520e1

Please sign in to comment.