• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

realm / realm-core / 2130

13 Mar 2024 04:51AM UTC coverage: 92.078% (+0.2%) from 91.833%
2130

push

Evergreen

web-flow
Merge pull request #7402 from realm/tg/obj-perf

Make Obj trivial and add a separate ObjCollectionParent type

94732 of 174812 branches covered (54.19%)

531 of 559 new or added lines in 22 files covered. (94.99%)

45 existing lines in 13 files now uncovered.

244506 of 265543 relevant lines covered (92.08%)

5982312.84 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

93.58
/src/realm/object-store/impl/collection_notifier.cpp
1
////////////////////////////////////////////////////////////////////////////
2
//
3
// Copyright 2016 Realm Inc.
4
//
5
// Licensed under the Apache License, Version 2.0 (the "License");
6
// you may not use this file except in compliance with the License.
7
// You may obtain a copy of the License at
8
//
9
// http://www.apache.org/licenses/LICENSE-2.0
10
//
11
// Unless required by applicable law or agreed to in writing, software
12
// distributed under the License is distributed on an "AS IS" BASIS,
13
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14
// See the License for the specific language governing permissions and
15
// limitations under the License.
16
//
17
////////////////////////////////////////////////////////////////////////////
18

19
#include <realm/object-store/impl/collection_notifier.hpp>
20

21
#include <realm/object-store/impl/deep_change_checker.hpp>
22
#include <realm/object-store/impl/realm_coordinator.hpp>
23
#include <realm/object-store/shared_realm.hpp>
24

25
#include <realm/db.hpp>
26
#include <realm/dictionary.hpp>
27
#include <realm/list.hpp>
28
#include <realm/set.hpp>
29
#include <realm/util/logger.hpp>
30

31
using namespace realm;
32
using namespace realm::_impl;
33

34
bool CollectionNotifier::any_related_table_was_modified(TransactionChangeInfo const& info) const noexcept
35
{
4,862✔
36
    // Check if any of the tables accessible from the root table were actually modified.
2,421✔
37
    // This includes insertions which need to be checked to catch modifications via a backlink.
2,421✔
38
    auto check_related_table = [&](auto& related_table) {
6,044✔
39
        auto it = info.tables.find(related_table.table_key);
6,044✔
40
        return it != info.tables.end() && (!it->second.modifications_empty() || !it->second.insertions_empty());
6,044✔
41
    };
6,044✔
42
    return any_of(begin(m_related_tables), end(m_related_tables), check_related_table);
4,862✔
43
}
4,862✔
44

45
util::UniqueFunction<bool(ObjKey)> CollectionNotifier::get_modification_checker(TransactionChangeInfo const& info,
46
                                                                                ConstTableRef root_table)
47
{
3,282✔
48
    // If new links were added to existing tables we need to recalculate our
1,631✔
49
    // related tables info. This'll also happen for schema changes that don't
1,631✔
50
    // matter, but making this check more precise than "any schema change at all
1,631✔
51
    // happened" would mostly just be a source of potential bugs.
1,631✔
52
    if (info.schema_changed) {
3,282✔
53
        util::CheckedLockGuard lock(m_callback_mutex);
2,006✔
54
        update_related_tables(*root_table);
2,006✔
55
    }
2,006✔
56

1,631✔
57
    if (!any_related_table_was_modified(info)) {
3,282✔
58
        return [](ObjKey) {
430✔
59
            return false;
430✔
60
        };
430✔
61
    }
82✔
62

1,590✔
63
    // If the table in question has no outgoing links it will be the only entry in `m_related_tables`.
1,590✔
64
    // In this case we do not need a `DeepChangeChecker` and check the modifications using the
1,590✔
65
    // `ObjectChangeSet` within the `TransactionChangeInfo` for this table directly.
1,590✔
66
    if (m_related_tables.size() == 1 && !all_callbacks_filtered()) {
3,200✔
67
        auto root_table_key = m_related_tables[0].table_key;
1,730✔
68
        auto& object_change_set = info.tables.find(root_table_key)->second;
1,730✔
69
        return [&](ObjKey object_key) {
3,918✔
70
            return object_change_set.modifications_contains(object_key, {});
3,918✔
71
        };
3,918✔
72
    }
1,730✔
73

735✔
74
    if (all_callbacks_filtered()) {
1,470✔
75
        return CollectionKeyPathChangeChecker(info, *root_table, m_related_tables, m_key_path_array,
136✔
76
                                              m_all_callbacks_filtered);
136✔
77
    }
136✔
78
    else if (any_callbacks_filtered()) {
1,334✔
79
        // In case we have some callbacks, we need to combine the unfiltered `DeepChangeChecker` with
15✔
80
        // the filtered `CollectionKeyPathChangeChecker` to make sure we send all expected notifications.
15✔
81
        CollectionKeyPathChangeChecker key_path_checker(info, *root_table, m_related_tables, m_key_path_array,
30✔
82
                                                        m_all_callbacks_filtered);
30✔
83
        DeepChangeChecker deep_change_checker(info, *root_table, m_related_tables, m_key_path_array,
30✔
84
                                              m_all_callbacks_filtered);
30✔
85
        return [key_path_checker = std::move(key_path_checker),
30✔
86
                deep_change_checker = std::move(deep_change_checker)](ObjKey object_key) mutable {
228✔
87
            return key_path_checker(object_key) || deep_change_checker(object_key);
228✔
88
        };
228✔
89
    }
30✔
90

652✔
91
    return DeepChangeChecker(info, *root_table, m_related_tables, m_key_path_array, m_all_callbacks_filtered);
1,304✔
92
}
1,304✔
93

94
util::UniqueFunction<std::vector<ColKey>(ObjKey)>
95
CollectionNotifier::get_object_modification_checker(TransactionChangeInfo const& info, ConstTableRef root_table)
96
{
78✔
97
    return ObjectKeyPathChangeChecker(info, *root_table, m_related_tables, m_key_path_array,
78✔
98
                                      m_all_callbacks_filtered);
78✔
99
}
78✔
100

101
void CollectionNotifier::recalculate_key_path_array()
102
{
9,565✔
103
    m_all_callbacks_filtered = true;
9,565✔
104
    m_any_callbacks_filtered = false;
9,565✔
105
    m_key_path_array.clear();
9,565✔
106
    for (const auto& callback : m_callbacks) {
9,622✔
107
        if (!callback.key_path_array) {
9,622✔
108
            m_all_callbacks_filtered = false;
9,358✔
109
        }
9,358✔
110
        else {
264✔
111
            m_any_callbacks_filtered = true;
264✔
112
            for (const auto& key_path : *callback.key_path_array) {
240✔
113
                m_key_path_array.push_back(key_path);
216✔
114
            }
216✔
115
        }
264✔
116
    }
9,622✔
117
}
9,565✔
118

119
bool CollectionNotifier::any_callbacks_filtered() const noexcept
120
{
10,309✔
121
    return m_any_callbacks_filtered;
10,309✔
122
}
10,309✔
123

124
bool CollectionNotifier::all_callbacks_filtered() const noexcept
125
{
3,278✔
126
    return m_all_callbacks_filtered;
3,278✔
127
}
3,278✔
128

129
CollectionNotifier::CollectionNotifier(std::shared_ptr<Realm> realm)
130
    : m_realm(std::move(realm))
131
    , m_transaction(Realm::Internal::get_transaction_ref(*m_realm))
132
{
10,490✔
133
    if (auto logger = m_transaction->get_logger()) {
10,490✔
134
        // We only have logging at debug and trace levels
5,241✔
135
        if (logger->would_log(util::LogCategory::notification, util::Logger::Level::debug)) {
10,490✔
136
            m_logger = logger;
6,596✔
137
        }
6,596✔
138
    }
10,490✔
139
}
10,490✔
140

141
CollectionNotifier::~CollectionNotifier()
142
{
10,490✔
143
    // Need to do this explicitly to ensure m_realm is destroyed with the mutex
5,241✔
144
    // held to avoid potential double-deletion
5,241✔
145
    unregister();
10,490✔
146
    if (m_logger) {
10,490✔
147
        m_logger->log(util::LogCategory::notification, util::Logger::Level::debug, "Notifier %1 gone", m_description);
6,596✔
148
    }
6,596✔
149
}
10,490✔
150

151
VersionID CollectionNotifier::version() const noexcept
152
{
21,354✔
153
    return m_transaction->get_version_of_current_transaction();
21,354✔
154
}
21,354✔
155

156
void CollectionNotifier::release_data() noexcept
157
{
10,490✔
158
    m_transaction = nullptr;
10,490✔
159
}
10,490✔
160

161
static bool all_have_filters(std::vector<NotificationCallback> const& callbacks) noexcept
162
{
528✔
163
    return std::all_of(callbacks.begin(), callbacks.end(), [](auto& cb) {
336✔
164
        return !cb.key_path_array;
144✔
165
    });
144✔
166
}
528✔
167

168
uint64_t CollectionNotifier::add_callback(CollectionChangeCallback callback,
169
                                          std::optional<KeyPathArray> key_path_array)
170
{
10,700✔
171
    m_realm->verify_thread();
10,700✔
172

5,346✔
173
    util::CheckedLockGuard lock(m_callback_mutex);
10,700✔
174
    // If we're adding a callback with a keypath filter or if previously all
5,346✔
175
    // callbacks had filters but this one doesn't we will need to recalculate
5,346✔
176
    // the related tables on the background thread.
5,346✔
177
    if (!key_path_array || all_have_filters(m_callbacks)) {
10,700✔
178
        m_did_modify_callbacks = true;
10,676✔
179
    }
10,676✔
180

5,346✔
181
    auto token = m_next_token++;
10,700✔
182
    m_callbacks.push_back({std::move(callback), {}, {}, std::move(key_path_array), token, false, false});
10,700✔
183

5,346✔
184
    if (m_callback_index == npos) { // Don't need to wake up if we're already sending notifications
10,700✔
185
        Realm::Internal::get_coordinator(*m_realm).wake_up_notifier_worker();
10,694✔
186
        m_have_callbacks = true;
10,694✔
187
    }
10,694✔
188
    return token;
10,700✔
189
}
10,700✔
190

191
void CollectionNotifier::remove_callback(uint64_t token)
192
{
10,700✔
193
    // the callback needs to be destroyed after releasing the lock as destroying
5,346✔
194
    // it could cause user code to be called
5,346✔
195
    NotificationCallback old;
10,700✔
196
    {
10,700✔
197
        util::CheckedLockGuard lock(m_callback_mutex);
10,700✔
198
        auto it = find_callback(token);
10,700✔
199
        if (it == end(m_callbacks)) {
10,700✔
200
            return;
×
201
        }
×
202

5,346✔
203
        size_t idx = distance(begin(m_callbacks), it);
10,700✔
204
        if (m_callback_index != npos) {
10,700✔
205
            if (m_callback_index >= idx)
24✔
206
                --m_callback_index;
22✔
207
        }
24✔
208
        --m_callback_count;
10,700✔
209

5,346✔
210
        old = std::move(*it);
10,700✔
211
        m_callbacks.erase(it);
10,700✔
212

5,346✔
213
        // If we're removing a callback with a keypath filter or the last callback
5,346✔
214
        // without a keypath filter we will need to recalcuate the related tables
5,346✔
215
        // on next run.
5,346✔
216
        if (!old.key_path_array || all_have_filters(m_callbacks)) {
10,700✔
217
            m_did_modify_callbacks = true;
10,676✔
218
        }
10,676✔
219

5,346✔
220
        m_have_callbacks = !m_callbacks.empty();
10,700✔
221
    }
10,700✔
222
}
10,700✔
223

224
void CollectionNotifier::suppress_next_notification(uint64_t token)
225
{
58✔
226
    {
58✔
227
        std::lock_guard<std::mutex> lock(m_realm_mutex);
58✔
228
        REALM_ASSERT(m_realm);
58✔
229
        m_realm->verify_thread();
58✔
230
        if (!m_realm->is_in_transaction()) {
58✔
231
            throw WrongTransactionState("Suppressing the notification from a write transaction must be done from "
2✔
232
                                        "inside the write transaction.");
2✔
233
        }
2✔
234
    }
56✔
235

28✔
236
    util::CheckedLockGuard lock(m_callback_mutex);
56✔
237
    auto it = find_callback(token);
56✔
238
    if (it != end(m_callbacks)) {
56✔
239
        // We're inside a write on this collection's Realm, so the callback
27✔
240
        // should have already been called and there are no versions after
27✔
241
        // this one yet
27✔
242
        REALM_ASSERT(it->changes_to_deliver.empty());
54✔
243
        REALM_ASSERT(it->accumulated_changes.empty());
54✔
244
        it->skip_next = true;
54✔
245
    }
54✔
246
}
56✔
247

248
std::vector<NotificationCallback>::iterator CollectionNotifier::find_callback(uint64_t token)
249
{
10,754✔
250
    REALM_ASSERT(m_callbacks.size() > 0);
10,754✔
251

5,373✔
252
    auto it = std::find_if(begin(m_callbacks), end(m_callbacks), [=](const auto& c) {
11,056✔
253
        return c.token == token;
11,056✔
254
    });
11,056✔
255
    // We should only fail to find the callback if it was removed due to an error
5,373✔
256
    REALM_ASSERT(it != end(m_callbacks));
10,754✔
257
    return it;
10,754✔
258
}
10,754✔
259

260
void CollectionNotifier::unregister() noexcept
261
{
20,980✔
262
    {
20,980✔
263
        std::lock_guard lock(m_realm_mutex);
20,980✔
264
        m_realm = nullptr;
20,980✔
265
    }
20,980✔
266
    m_is_alive.store(false, std::memory_order_release);
20,980✔
267
}
20,980✔
268

269
bool CollectionNotifier::is_alive() const noexcept
270
{
83,141✔
271
    return m_is_alive.load(std::memory_order_acquire);
83,141✔
272
}
83,141✔
273

274
std::unique_lock<std::mutex> CollectionNotifier::lock_target()
275
{
17,526✔
276
    return std::unique_lock<std::mutex>{m_realm_mutex};
17,526✔
277
}
17,526✔
278

279
void CollectionNotifier::add_required_change_info(TransactionChangeInfo& info)
280
{
17,937✔
281
    if (!do_add_required_change_info(info) || m_related_tables.empty()) {
17,937✔
282
        return;
4,336✔
283
    }
4,336✔
284

7,555✔
285
    // Create an entry in the `TransactionChangeInfo` for every table in `m_related_tables`.
7,555✔
286
    info.tables.reserve(m_related_tables.size());
13,601✔
287
    for (auto& tbl : m_related_tables)
13,601✔
288
        info.tables[tbl.table_key];
17,139✔
289
}
13,601✔
290

291
void CollectionNotifier::update_related_tables(Table const& table)
292
{
9,565✔
293
    m_related_tables.clear();
9,565✔
294
    recalculate_key_path_array();
9,565✔
295
    DeepChangeChecker::find_related_tables(m_related_tables, table, m_key_path_array);
9,565✔
296
    // We deactivate the `m_did_modify_callbacks` toggle to make sure the recalculation is only done when
4,779✔
297
    // necessary.
4,779✔
298
    m_did_modify_callbacks = false;
9,565✔
299
}
9,565✔
300

301
void CollectionNotifier::prepare_handover()
302
{
27,995✔
303
    REALM_ASSERT(m_transaction);
27,995✔
304
    do_prepare_handover(*m_transaction);
27,995✔
305
    add_changes(std::move(m_change));
27,995✔
306
    m_change = {};
27,995✔
307
    REALM_ASSERT(m_change.empty());
27,995✔
308
    m_has_run = true;
27,995✔
309

14,748✔
310
#ifdef REALM_DEBUG
27,995✔
311
    util::CheckedLockGuard lock(m_callback_mutex);
27,995✔
312
    for (auto& callback : m_callbacks)
27,995✔
313
        REALM_ASSERT(!callback.skip_next);
27,995✔
314
#endif
27,995✔
315
}
27,995✔
316

14,748✔
317
void CollectionNotifier::before_advance()
318
{
4,921✔
319
    for_each_callback([&](auto& lock, auto& callback) {
10,885✔
320
        if (callback.changes_to_deliver.empty()) {
10,885✔
321
            return;
7,472✔
322
        }
3,017✔
323

1,509✔
324
        auto changes = callback.changes_to_deliver;
7,857✔
325
        // acquire a local reference to the callback so that removing the
4,455✔
326
        // callback from within it can't result in a dangling pointer
4,455✔
327
        auto cb = callback.fn;
7,857✔
328
        lock.unlock_unchecked();
7,857✔
329
        cb.before(changes);
7,857✔
330
    });
7,857✔
331
}
9,376✔
332

5,975✔
333
void CollectionNotifier::after_advance()
334
{
15,146✔
335
    using namespace std::chrono;
15,146✔
336
    auto t1 = steady_clock::now();
26,693✔
337

11,547✔
338
    for_each_callback([&](auto& lock, auto& callback) {
21,870✔
339
        if (callback.initial_delivered && callback.changes_to_deliver.empty()) {
21,870✔
340
            return;
10,382✔
341
        }
10,382✔
342
        callback.initial_delivered = true;
15,778✔
343

5,279✔
344
        auto changes = std::move(callback.changes_to_deliver).finalize();
15,778✔
345
        callback.changes_to_deliver = {};
15,778✔
346
        // acquire a local reference to the callback so that removing the
347
        // callback from within it can't result in a dangling pointer
348
        auto cb = callback.fn;
10,499✔
349
        lock.unlock_unchecked();
10,499✔
350
        if (m_logger) {
10,499✔
351
            m_logger->log(util::LogCategory::notification, util::Logger::Level::debug,
5,279✔
352
                          "Delivering notifications for %1 after %2 us", m_description,
5,279✔
353
                          duration_cast<microseconds>(t1 - m_run_time_point).count());
5,279✔
354
            if (m_logger->would_log(util::Logger::Level::trace)) {
5,279✔
NEW
355
                if (changes.empty()) {
×
NEW
356
                    m_logger->log(util::LogCategory::notification, util::Logger::Level::trace, "   No changes");
×
NEW
357
                }
×
NEW
358
                else {
×
NEW
359
                    if (changes.collection_root_was_deleted) {
×
NEW
360
                        m_logger->log(util::LogCategory::notification, util::Logger::Level::trace,
×
NEW
361
                                      "   collection deleted");
×
NEW
362
                    }
×
NEW
363
                    else if (changes.collection_was_cleared) {
×
NEW
364
                        m_logger->log(util::LogCategory::notification, util::Logger::Level::trace,
×
NEW
365
                                      "   collection cleared");
×
NEW
366
                    }
×
NEW
367
                    else {
×
NEW
368
                        auto log = [this](const char* change, const IndexSet& index_set) {
×
NEW
369
                            if (auto cnt = index_set.count()) {
×
NEW
370
                                std::ostringstream ostr;
×
NEW
371
                                bool first = true;
×
NEW
372
                                for (auto [a, b] : index_set) {
×
NEW
373
                                    if (!first)
×
NEW
374
                                        ostr << ',';
×
NEW
375
                                    if (b > a + 1) {
×
NEW
376
                                        ostr << '[' << a << ',' << b - 1 << ']';
×
NEW
377
                                    }
×
NEW
378
                                    else {
×
NEW
379
                                        ostr << a;
×
NEW
380
                                    }
×
NEW
381
                                    first = false;
×
382
                                }
383
                                m_logger->log(util::LogCategory::notification, util::Logger::Level::trace,
384
                                              "   %1 %2: %3", cnt, change, ostr.str().c_str());
16,167✔
385
                            }
16,167✔
386
                        };
16,167✔
387
                        log("deletions", changes.deletions);
16,167✔
388
                        log("insertions", changes.insertions);
16,623✔
389
                        log("modifications", changes.modifications);
16,623✔
390
                    }
5,076✔
391
                }
5,076✔
392
            }
11,547✔
393
        }
16,826✔
394
        cb.after(changes);
22,046✔
395
    });
22,046✔
396
}
26,693✔
397

11,547✔
398
bool CollectionNotifier::is_for_realm(Realm& realm) const noexcept
11,547✔
399
{
36,753✔
400
    std::lock_guard<std::mutex> lock(m_realm_mutex);
36,753✔
401
    return m_realm.get() == &realm;
36,753✔
402
}
36,753✔
403

11,547✔
404
bool CollectionNotifier::package_for_delivery()
16,167✔
405
{
16,162✔
406
    if (!prepare_to_deliver())
16,162✔
407
        return false;
26,355✔
408
    util::CheckedLockGuard lock(m_callback_mutex);
42,517✔
409
    for (auto& callback : m_callbacks) {
42,946✔
410
        // changes_to_deliver will normally be empty here. If it's non-empty
26,355✔
411
        // then that means package_for_delivery() was called multiple times
412
        // without the notification actually being delivered, which can happen
413
        // if the Realm was refreshed from within a notification callback.
17,183✔
414
        callback.changes_to_deliver.merge(std::move(callback.accumulated_changes));
33,774✔
415
        callback.accumulated_changes = {};
16,591✔
416
    }
33,774✔
417
    m_callback_count = m_callbacks.size();
33,774✔
418
    return true;
33,774✔
419
}
33,774✔
420

17,612✔
421
template <typename Fn>
17,612✔
422
void CollectionNotifier::for_each_callback(Fn&& fn)
17,612✔
423
{
37,679✔
424
    util::CheckedUniqueLock callback_lock(m_callback_mutex);
37,679✔
425
    // If this fails then package_for_delivery() was not called or returned false
17,183✔
426
    REALM_ASSERT_DEBUG(m_callback_count <= m_callbacks.size());
37,250✔
427
    for (m_callback_index = 0; m_callback_index < m_callback_count; ++m_callback_index) {
57,762✔
428
        fn(callback_lock, m_callbacks[m_callback_index]);
20,512✔
429
        if (!callback_lock.owns_lock())
20,512✔
430
            callback_lock.lock_unchecked();
13,901✔
431
    }
42,654✔
432

22,142✔
433
    m_callback_index = npos;
42,209✔
434
}
42,209✔
435

44,729✔
436
void CollectionNotifier::set_initial_transaction(
22,587✔
437
    const std::vector<std::shared_ptr<CollectionNotifier>>& other_notifiers)
22,587✔
438
{
21,251✔
439
    for (auto& other : other_notifiers) {
25,230✔
440
        if (version() == other->version()) {
24,785✔
441
            attach_to(other->m_transaction);
24,778✔
442
            return;
24,778✔
443
        }
2,636✔
444
    }
2,643✔
445
    attach_to(m_transaction->duplicate());
2,613✔
446
}
7,854✔
447

5,241✔
448

2,645✔
449
void CollectionNotifier::attach_to(std::shared_ptr<Transaction> tr)
2,638✔
450
{
13,100✔
451
    REALM_ASSERT(!m_has_run);
13,100✔
452
    // Keep the old transaction alive until the end of the function
2,645✔
453
    m_transaction.swap(tr);
15,703✔
454
    reattach();
13,065✔
455
}
10,462✔
456

457
Transaction& CollectionNotifier::source_shared_group()
458
{
13,347✔
459
    return Realm::Internal::get_transaction(*m_realm);
13,347✔
460
}
13,347✔
461

10,446✔
462
void CollectionNotifier::report_collection_root_is_deleted()
10,446✔
463
{
10,975✔
464
    if (!m_has_delivered_root_deletion_event) {
529✔
465
        m_change.collection_root_was_deleted = true;
397✔
466
        m_has_delivered_root_deletion_event = true;
3,298✔
467
    }
3,298✔
468
}
3,430✔
469

470
void CollectionNotifier::add_changes(CollectionChangeBuilder change)
471
{
13,776✔
472
    util::CheckedLockGuard lock(m_callback_mutex);
13,776✔
473
    for (auto& callback : m_callbacks) {
13,895✔
474
        if (callback.skip_next) {
13,895✔
475
            // Only the first commit in a batched set of transactions can be
397✔
476
            // skipped, so if we already have some changes something went wrong.
529✔
477
            REALM_ASSERT_DEBUG(callback.accumulated_changes.empty());
26✔
478
            callback.skip_next = false;
26✔
479
        }
14,774✔
480
        else {
28,220✔
481
            // Only copy the changeset if there's more callbacks that need it
14,998✔
482
            if (&callback == &m_callbacks.back())
28,470✔
483
                callback.accumulated_changes.merge(std::move(change));
13,201✔
484
            else
323✔
485
                callback.accumulated_changes.merge(CollectionChangeBuilder(change));
323✔
486
        }
13,498✔
487
    }
13,524✔
488
}
28,219✔
489

14,972✔
490
NotifierPackage::NotifierPackage(std::vector<std::shared_ptr<CollectionNotifier>> notifiers,
14,972✔
491
                                 RealmCoordinator* coordinator)
14,675✔
492
    : m_notifiers(std::move(notifiers))
297✔
493
    , m_coordinator(coordinator)
297✔
494
{
41,773✔
495
}
41,799✔
496

14,748✔
497
NotifierPackage::NotifierPackage(std::vector<std::shared_ptr<CollectionNotifier>> notifiers,
498
                                 std::shared_ptr<Transaction> pin_tr)
499
    : m_pin_tr(pin_tr)
500
    , m_notifiers(std::move(notifiers))
501
{
8,072✔
502
}
33,668✔
503

25,596✔
504
std::optional<VersionID> NotifierPackage::version() const noexcept
505
{
14,890✔
506
    if (m_pin_tr)
14,890✔
507
        return m_pin_tr->get_version_of_current_transaction();
4,342✔
508
    return std::nullopt;
10,548✔
509
}
19,590✔
510

9,042✔
511
void NotifierPackage::package_and_wait(VersionID::version_type target_version)
512
{
3,385✔
513
    if (!m_coordinator || !*this)
18,270✔
514
        return;
15,008✔
515

6,450✔
516
    m_pin_tr = m_coordinator->package_notifiers(m_notifiers, target_version);
11,697✔
517
    if (m_pin_tr && m_pin_tr->get_version_of_current_transaction().version < target_version)
11,697✔
518
        m_pin_tr.reset();
519
}
3,262✔
520

3,365✔
521
void NotifierPackage::before_advance()
3,365✔
522
{
36,156✔
523
    for (auto& notifier : m_notifiers)
39,275✔
524
        notifier->before_advance();
8,163✔
525
}
39,275✔
526

527
void NotifierPackage::after_advance()
3,242✔
528
{
37,265✔
529
    for (auto& notifier : m_notifiers)
37,265✔
530
        notifier->after_advance();
41,381✔
531
}
71,074✔
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc