@@ -34,7 +34,7 @@ AsyncOpenTask::AsyncOpenTask(std::shared_ptr<_impl::RealmCoordinator> coordinato
34
34
{
35
35
}
36
36
37
- void AsyncOpenTask::start (AsyncOpenCallback async_open_complete )
37
+ void AsyncOpenTask::start (AsyncOpenCallback callback )
38
38
{
39
39
util::CheckedUniqueLock lock (m_mutex);
40
40
if (!m_session)
@@ -43,8 +43,7 @@ void AsyncOpenTask::start(AsyncOpenCallback async_open_complete)
43
43
lock.unlock ();
44
44
45
45
std::shared_ptr<AsyncOpenTask> self (shared_from_this ());
46
- session->wait_for_download_completion ([async_open_complete = std::move (async_open_complete), self,
47
- this ](Status status) mutable {
46
+ session->wait_for_download_completion ([callback = std::move (callback), self, this ](Status status) mutable {
48
47
std::shared_ptr<_impl::RealmCoordinator> coordinator;
49
48
{
50
49
util::CheckedLockGuard lock (m_mutex);
@@ -56,18 +55,19 @@ void AsyncOpenTask::start(AsyncOpenCallback async_open_complete)
56
55
}
57
56
58
57
if (!status.is_ok ()) {
59
- self->async_open_complete (std::move (async_open_complete ), coordinator, status);
58
+ self->async_open_complete (std::move (callback ), coordinator, status);
60
59
return ;
61
60
}
62
61
63
- auto config = coordinator->get_config ();
64
- if (config.sync_config && config.sync_config ->flx_sync_requested &&
65
- config.sync_config ->subscription_initializer ) {
66
- const bool rerun_on_launch = config.sync_config ->rerun_init_subscription_on_open ;
67
- self->attach_to_subscription_initializer (std::move (async_open_complete), coordinator, rerun_on_launch);
68
- }
69
- else {
70
- self->async_open_complete (std::move (async_open_complete), coordinator, status);
62
+ self->migrate_schema_or_complete (std::move (callback), coordinator, status);
63
+ });
64
+ // The callback does not extend the lifetime of the task if it's never invoked.
65
+ SyncSession::Internal::set_sync_schema_migration_callback (*session, [weak_self = weak_from_this (), this ]() {
66
+ if (auto self = weak_self.lock ()) {
67
+ util::CheckedLockGuard lock (m_mutex);
68
+ if (!m_session)
69
+ return ;
70
+ m_sync_schema_migration_required = true ;
71
71
}
72
72
});
73
73
session->revive_if_needed ();
@@ -118,7 +118,7 @@ void AsyncOpenTask::unregister_download_progress_notifier(uint64_t token)
118
118
m_session->unregister_progress_notifier (token);
119
119
}
120
120
121
- void AsyncOpenTask::attach_to_subscription_initializer (AsyncOpenCallback&& async_open_callback ,
121
+ void AsyncOpenTask::attach_to_subscription_initializer (AsyncOpenCallback&& callback ,
122
122
std::shared_ptr<_impl::RealmCoordinator> coordinator,
123
123
bool rerun_on_launch)
124
124
{
@@ -136,13 +136,13 @@ void AsyncOpenTask::attach_to_subscription_initializer(AsyncOpenCallback&& async
136
136
// We need to wait until subscription initializer completes
137
137
std::shared_ptr<AsyncOpenTask> self (shared_from_this ());
138
138
init_subscription.get_state_change_notification (sync ::SubscriptionSet::State::Complete)
139
- .get_async ([self, coordinator, async_open_callback = std::move (async_open_callback )](
139
+ .get_async ([self, coordinator, callback = std::move (callback )](
140
140
StatusWith<realm::sync ::SubscriptionSet::State> state) mutable {
141
- self->async_open_complete (std::move (async_open_callback ), coordinator, state.get_status ());
141
+ self->async_open_complete (std::move (callback ), coordinator, state.get_status ());
142
142
});
143
143
}
144
144
else {
145
- async_open_complete (std::move (async_open_callback ), coordinator, Status::OK ());
145
+ async_open_complete (std::move (callback ), coordinator, Status::OK ());
146
146
}
147
147
}
148
148
@@ -151,6 +151,10 @@ void AsyncOpenTask::async_open_complete(AsyncOpenCallback&& callback,
151
151
{
152
152
{
153
153
util::CheckedLockGuard lock (m_mutex);
154
+ // 'Cancel' may have been called just before 'async_open_complete' is invoked.
155
+ if (!m_session)
156
+ return ;
157
+
154
158
for (auto token : m_registered_callbacks) {
155
159
m_session->unregister_progress_notifier (token);
156
160
}
@@ -170,4 +174,79 @@ void AsyncOpenTask::async_open_complete(AsyncOpenCallback&& callback,
170
174
return callback ({}, std::make_exception_ptr (Exception (status)));
171
175
}
172
176
177
+ void AsyncOpenTask::migrate_schema_or_complete (AsyncOpenCallback&& callback,
178
+ std::shared_ptr<_impl::RealmCoordinator> coordinator, Status status)
179
+ {
180
+ util::CheckedUniqueLock lock (m_mutex);
181
+ if (!m_session)
182
+ return ;
183
+ auto session = m_session;
184
+ auto migrate_schema = m_sync_schema_migration_required;
185
+ lock.unlock ();
186
+
187
+ if (!migrate_schema) {
188
+ wait_for_bootstrap_or_complete (std::move (callback), coordinator, status);
189
+ return ;
190
+ }
191
+
192
+ // Migrate the schema.
193
+ // * First upload the changes at the old schema version
194
+ // * Then delete the realm, reopen it, and rebootstrap at new schema version
195
+ // The lifetime of the task is extended until the bootstrap completes.
196
+ std::shared_ptr<AsyncOpenTask> self (shared_from_this ());
197
+ session->wait_for_upload_completion ([callback = std::move (callback), coordinator, session, self,
198
+ this ](Status status) mutable {
199
+ {
200
+ util::CheckedLockGuard lock (m_mutex);
201
+ if (!m_session)
202
+ return ; // Swallow all events if the task has been cancelled.
203
+ }
204
+
205
+ if (!status.is_ok ()) {
206
+ self->async_open_complete (std::move (callback), coordinator, status);
207
+ return ;
208
+ }
209
+
210
+ auto future = SyncSession::Internal::pause_async (*session);
211
+ // Wait until the SessionWrapper is done using the DBRef.
212
+ std::move (future).get_async ([callback = std::move (callback), coordinator, self, this ](Status status) mutable {
213
+ {
214
+ util::CheckedLockGuard lock (m_mutex);
215
+ if (!m_session)
216
+ return ; // Swallow all events if the task has been cancelled.
217
+ }
218
+
219
+ if (!status.is_ok ()) {
220
+ self->async_open_complete (std::move (callback), coordinator, status);
221
+ return ;
222
+ }
223
+
224
+ // Delete the realm file and reopen it.
225
+ {
226
+ util::CheckedLockGuard lock (m_mutex);
227
+ m_session = nullptr ;
228
+ coordinator->delete_and_reopen ();
229
+ m_session = coordinator->sync_session ();
230
+ }
231
+
232
+ self->wait_for_bootstrap_or_complete (std::move (callback), coordinator, status);
233
+ });
234
+ });
235
+ }
236
+
237
+ void AsyncOpenTask::wait_for_bootstrap_or_complete (AsyncOpenCallback&& callback,
238
+ std::shared_ptr<_impl::RealmCoordinator> coordinator,
239
+ Status status)
240
+ {
241
+ auto config = coordinator->get_config ();
242
+ if (config.sync_config && config.sync_config ->flx_sync_requested &&
243
+ config.sync_config ->subscription_initializer ) {
244
+ const bool rerun_on_launch = config.sync_config ->rerun_init_subscription_on_open ;
245
+ attach_to_subscription_initializer (std::move (callback), coordinator, rerun_on_launch);
246
+ }
247
+ else {
248
+ async_open_complete (std::move (callback), coordinator, status);
249
+ }
250
+ }
251
+
173
252
} // namespace realm
0 commit comments