Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

RJS-2784 Fix callback crashes when reloading with React Native #7963

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
27 changes: 27 additions & 0 deletions src/realm/object-store/sync/sync_session.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -402,6 +402,17 @@ SyncSession::SyncSession(Private, SyncClient& client, std::shared_ptr<DB> db, co

void SyncSession::detach_from_sync_manager()
{
// Unregister all callbacks when the App and SyncManager are destroyed.
{
util::CheckedLockGuard lk(m_state_mutex);
m_completion_callbacks.clear();
}
{
util::CheckedLockGuard lk(m_connection_state_mutex);
m_connection_change_notifier.remove_callbacks();
}
m_progress_notifier.unregister_callbacks();

shutdown_and_wait();
util::CheckedLockGuard lk(m_state_mutex);
m_sync_manager = nullptr;
Expand Down Expand Up @@ -1555,6 +1566,14 @@ void SyncProgressNotifier::unregister_callback(uint64_t token)
m_packages.erase(token);
}

void SyncProgressNotifier::unregister_callbacks()
{
std::lock_guard<std::mutex> lock(m_mutex);
m_packages.clear();
m_current_progress.reset();
m_local_transaction_version = 0;
}

void SyncProgressNotifier::update(uint64_t downloaded, uint64_t downloadable, uint64_t uploaded, uint64_t uploadable,
uint64_t snapshot_version, double download_estimate, double upload_estimate,
int64_t query_version)
Expand Down Expand Up @@ -1662,6 +1681,14 @@ void SyncSession::ConnectionChangeNotifier::remove_callback(uint64_t token)
}
}

void SyncSession::ConnectionChangeNotifier::remove_callbacks()
{
std::lock_guard<std::mutex> lock(m_callback_mutex);
m_callbacks.clear();
m_callback_count = -1;
m_callback_index = -1;
}

void SyncSession::ConnectionChangeNotifier::invoke_callbacks(ConnectionState old_state, ConnectionState new_state)
{
std::unique_lock lock(m_callback_mutex);
Expand Down
4 changes: 2 additions & 2 deletions src/realm/object-store/sync/sync_session.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,7 @@ class SyncProgressNotifier {
uint64_t register_callback(std::function<ProgressNotifierCallback>, NotifierType direction, bool is_streaming,
int64_t pending_query_version);
void unregister_callback(uint64_t);
void unregister_callbacks();

void set_local_version(uint64_t);
void update(uint64_t downloaded, uint64_t downloadable, uint64_t uploaded, uint64_t uploadable,
Expand Down Expand Up @@ -368,6 +369,7 @@ class SyncSession : public std::enable_shared_from_this<SyncSession> {
public:
uint64_t add_callback(std::function<ConnectionStateChangeCallback> callback);
void remove_callback(uint64_t token);
void remove_callbacks();
void invoke_callbacks(ConnectionState old_state, ConnectionState new_state);

private:
Expand All @@ -394,8 +396,6 @@ class SyncSession : public std::enable_shared_from_this<SyncSession> {
}
// }

std::shared_ptr<SyncManager> sync_manager() const REQUIRES(!m_state_mutex);

static util::UniqueFunction<void(std::optional<app::AppError>)>
handle_refresh(const std::shared_ptr<SyncSession>&, bool);

Expand Down
20 changes: 20 additions & 0 deletions test/object-store/sync/session/connection_change_notifications.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -117,4 +117,24 @@ TEST_CASE("sync: Connection state changes", "[sync][session][connection change]"
REQUIRE(listener1_call_cnt == 1); // Only called once before unregister
REQUIRE(listener2_called);
}

SECTION("Callback not invoked when SyncSession is detached from SyncManager") {
auto session = sync_session(
user, "/connection-state-changes-1", [](auto, auto) {}, SyncSessionStopPolicy::AfterChangesUploaded);

EventLoop::main().run_until([&] {
return sessions_are_active(*session);
});
EventLoop::main().run_until([&] {
return sessions_are_connected(*session);
});

bool listener_called = false;
session->register_connection_change_callback([&](SyncSession::ConnectionState, SyncSession::ConnectionState) {
listener_called = true;
});

session->detach_from_sync_manager();
REQUIRE_FALSE(listener_called);
}
}
Loading