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

realm / realm-core / github_pull_request_281750

30 Oct 2023 03:37PM UTC coverage: 90.528% (-1.0%) from 91.571%
github_pull_request_281750

Pull #6073

Evergreen

jedelbo
Log free space and history sizes when opening file
Pull Request #6073: Merge next-major

95488 of 175952 branches covered (0.0%)

8973 of 12277 new or added lines in 149 files covered. (73.09%)

622 existing lines in 51 files now uncovered.

233503 of 257934 relevant lines covered (90.53%)

6533720.56 hits per line

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

87.53
/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,602✔
36
    // Check if any of the tables accessible from the root table were actually modified.
2,291✔
37
    // This includes insertions which need to be checked to catch modifications via a backlink.
2,291✔
38
    auto check_related_table = [&](auto& related_table) {
5,756✔
39
        auto it = info.tables.find(related_table.table_key);
5,756✔
40
        return it != info.tables.end() && (!it->second.modifications_empty() || !it->second.insertions_empty());
5,756✔
41
    };
5,756✔
42
    return any_of(begin(m_related_tables), end(m_related_tables), check_related_table);
4,602✔
43
}
4,602✔
44

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

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

1,468✔
63
    // If the table in question has no outgoing links it will be the only entry in `m_related_tables`.
1,468✔
64
    // In this case we do not need a `DeepChangeChecker` and check the modifications using the
1,468✔
65
    // `ObjectChangeSet` within the `TransactionChangeInfo` for this table directly.
1,468✔
66
    if (m_related_tables.size() == 1 && !all_callbacks_filtered()) {
2,956✔
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

613✔
74
    if (all_callbacks_filtered()) {
1,226✔
75
        return CollectionKeyPathChangeChecker(info, *root_table, m_related_tables, m_key_path_array,
130✔
76
                                              m_all_callbacks_filtered);
130✔
77
    }
130✔
78
    else if (any_callbacks_filtered()) {
1,096✔
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

533✔
91
    return DeepChangeChecker(info, *root_table, m_related_tables, m_key_path_array, m_all_callbacks_filtered);
1,066✔
92
}
1,066✔
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
{
8,824✔
103
    m_all_callbacks_filtered = true;
8,824✔
104
    m_any_callbacks_filtered = false;
8,824✔
105
    m_key_path_array.clear();
8,824✔
106
    for (const auto& callback : m_callbacks) {
8,881✔
107
        if (!callback.key_path_array) {
8,881✔
108
            m_all_callbacks_filtered = false;
8,623✔
109
        }
8,623✔
110
        else {
258✔
111
            m_any_callbacks_filtered = true;
258✔
112
            for (const auto& key_path : *callback.key_path_array) {
233✔
113
                m_key_path_array.push_back(key_path);
208✔
114
            }
208✔
115
        }
258✔
116
    }
8,881✔
117
}
8,824✔
118

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

124
bool CollectionNotifier::all_callbacks_filtered() const noexcept
125
{
3,034✔
126
    return m_all_callbacks_filtered;
3,034✔
127
}
3,034✔
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
{
9,925✔
133
    if (auto logger = m_transaction->get_logger()) {
9,925✔
134
        // We only have logging at debug and trace levels
4,967✔
135
        if (logger->would_log(util::LogCategory::notification, util::Logger::Level::debug)) {
9,925✔
NEW
136
            m_logger = logger;
×
NEW
137
        }
×
138
    }
9,925✔
139
}
9,925✔
140

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

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

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

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

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

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

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

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

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

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

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

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

5,072✔
220
        m_have_callbacks = !m_callbacks.empty();
10,135✔
221
    }
10,135✔
222
}
10,135✔
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,189✔
250
    REALM_ASSERT(m_callbacks.size() > 0);
10,189✔
251

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

260
void CollectionNotifier::unregister() noexcept
261
{
19,850✔
262
    std::lock_guard<std::mutex> lock(m_realm_mutex);
19,850✔
263
    m_realm = nullptr;
19,850✔
264
}
19,850✔
265

266
bool CollectionNotifier::is_alive() const noexcept
267
{
76,926✔
268
    std::lock_guard<std::mutex> lock(m_realm_mutex);
76,926✔
269
    return m_realm != nullptr;
76,926✔
270
}
76,926✔
271

272
std::unique_lock<std::mutex> CollectionNotifier::lock_target()
273
{
16,522✔
274
    return std::unique_lock<std::mutex>{m_realm_mutex};
16,522✔
275
}
16,522✔
276

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

7,431✔
283
    // Create an entry in the `TransactionChangeInfo` for every table in `m_related_tables`.
7,431✔
284
    info.tables.reserve(m_related_tables.size());
13,131✔
285
    for (auto& tbl : m_related_tables)
13,131✔
286
        info.tables[tbl.table_key];
16,157✔
287
}
13,131✔
288

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

299
void CollectionNotifier::prepare_handover()
300
{
26,950✔
301
    REALM_ASSERT(m_transaction);
26,950✔
302
    do_prepare_handover(*m_transaction);
26,950✔
303
    add_changes(std::move(m_change));
26,950✔
304
    m_change = {};
26,950✔
305
    REALM_ASSERT(m_change.empty());
26,950✔
306
    m_has_run = true;
26,950✔
307

14,345✔
308
#ifdef REALM_DEBUG
26,950✔
309
    util::CheckedLockGuard lock(m_callback_mutex);
26,950✔
310
    for (auto& callback : m_callbacks)
26,950✔
311
        REALM_ASSERT(!callback.skip_next);
26,950✔
312
#endif
26,950✔
313
}
26,950✔
314

315
void CollectionNotifier::before_advance()
316
{
9,893✔
317
    for_each_callback([&](auto& lock, auto& callback) {
9,882✔
318
        if (callback.changes_to_deliver.empty()) {
9,871✔
319
            return;
2,749✔
320
        }
2,749✔
321

3,951✔
322
        auto changes = callback.changes_to_deliver;
7,122✔
323
        // acquire a local reference to the callback so that removing the
3,951✔
324
        // callback from within it can't result in a dangling pointer
3,951✔
325
        auto cb = callback.fn;
7,122✔
326
        lock.unlock_unchecked();
7,122✔
327
        cb.before(changes);
7,122✔
328
    });
7,122✔
329
}
9,893✔
330

331
void CollectionNotifier::after_advance()
332
{
29,788✔
333
    using namespace std::chrono;
29,788✔
334
    auto t1 = steady_clock::now();
29,788✔
335

15,245✔
336
    for_each_callback([&](auto& lock, auto& callback) {
30,700✔
337
        if (callback.initial_delivered && callback.changes_to_deliver.empty()) {
30,700✔
338
            return;
9,952✔
339
        }
9,952✔
340
        callback.initial_delivered = true;
20,748✔
341

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

396
bool CollectionNotifier::is_for_realm(Realm& realm) const noexcept
397
{
49,517✔
398
    std::lock_guard<std::mutex> lock(m_realm_mutex);
49,517✔
399
    return m_realm.get() == &realm;
49,517✔
400
}
49,517✔
401

402
bool CollectionNotifier::package_for_delivery()
403
{
31,820✔
404
    if (!prepare_to_deliver())
31,820✔
405
        return false;
×
406
    util::CheckedLockGuard lock(m_callback_mutex);
31,820✔
407
    for (auto& callback : m_callbacks) {
32,678✔
408
        // changes_to_deliver will normally be empty here. If it's non-empty
16,690✔
409
        // then that means package_for_delivery() was called multiple times
16,690✔
410
        // without the notification actually being delivered, which can happen
16,690✔
411
        // if the Realm was refreshed from within a notification callback.
16,690✔
412
        callback.changes_to_deliver.merge(std::move(callback.accumulated_changes));
32,678✔
413
        callback.accumulated_changes = {};
32,678✔
414
    }
32,678✔
415
    m_callback_count = m_callbacks.size();
31,820✔
416
    return true;
31,820✔
417
}
31,820✔
418

419
template <typename Fn>
420
void CollectionNotifier::for_each_callback(Fn&& fn)
421
{
39,681✔
422
    util::CheckedUniqueLock callback_lock(m_callback_mutex);
39,681✔
423
    // If this fails then package_for_delivery() was not called or returned false
20,570✔
424
    REALM_ASSERT_DEBUG(m_callback_count <= m_callbacks.size());
39,681✔
425
    for (m_callback_index = 0; m_callback_index < m_callback_count; ++m_callback_index) {
80,252✔
426
        fn(callback_lock, m_callbacks[m_callback_index]);
40,571✔
427
        if (!callback_lock.owns_lock())
40,571✔
428
            callback_lock.lock_unchecked();
27,870✔
429
    }
40,571✔
430

20,570✔
431
    m_callback_index = npos;
39,681✔
432
}
39,681✔
433

434
void CollectionNotifier::set_initial_transaction(
435
    const std::vector<std::shared_ptr<CollectionNotifier>>& other_notifiers)
436
{
9,925✔
437
    for (auto& other : other_notifiers) {
7,469✔
438
        if (version() == other->version()) {
5,005✔
439
            attach_to(other->m_transaction);
4,991✔
440
            return;
4,991✔
441
        }
4,991✔
442
    }
5,005✔
443
    attach_to(m_transaction->duplicate());
7,430✔
444
}
4,934✔
445

446

447
void CollectionNotifier::attach_to(std::shared_ptr<Transaction> tr)
448
{
19,778✔
449
    REALM_ASSERT(!m_has_run);
19,778✔
450
    // Keep the old transaction alive until the end of the function
9,898✔
451
    m_transaction.swap(tr);
19,778✔
452
    reattach();
19,778✔
453
}
19,778✔
454

455
Transaction& CollectionNotifier::source_shared_group()
456
{
5,322✔
457
    return Realm::Internal::get_transaction(*m_realm);
5,322✔
458
}
5,322✔
459

460
void CollectionNotifier::report_collection_root_is_deleted()
461
{
1,056✔
462
    if (!m_has_delivered_root_deletion_event) {
1,056✔
463
        m_change.collection_root_was_deleted = true;
790✔
464
        m_has_delivered_root_deletion_event = true;
790✔
465
    }
790✔
466
}
1,056✔
467

468
void CollectionNotifier::add_changes(CollectionChangeBuilder change)
469
{
26,950✔
470
    util::CheckedLockGuard lock(m_callback_mutex);
26,950✔
471
    for (auto& callback : m_callbacks) {
27,448✔
472
        if (callback.skip_next) {
27,448✔
473
            // Only the first commit in a batched set of transactions can be
26✔
474
            // skipped, so if we already have some changes something went wrong.
26✔
475
            REALM_ASSERT_DEBUG(callback.accumulated_changes.empty());
52✔
476
            callback.skip_next = false;
52✔
477
        }
52✔
478
        else {
27,396✔
479
            // Only copy the changeset if there's more callbacks that need it
14,567✔
480
            if (&callback == &m_callbacks.back())
27,396✔
481
                callback.accumulated_changes.merge(std::move(change));
26,802✔
482
            else
594✔
483
                callback.accumulated_changes.merge(CollectionChangeBuilder(change));
594✔
484
        }
27,396✔
485
    }
27,448✔
486
}
26,950✔
487

488
NotifierPackage::NotifierPackage(std::vector<std::shared_ptr<CollectionNotifier>> notifiers,
489
                                 RealmCoordinator* coordinator)
490
    : m_notifiers(std::move(notifiers))
491
    , m_coordinator(coordinator)
492
{
64,605✔
493
}
64,605✔
494

495
NotifierPackage::NotifierPackage(std::vector<std::shared_ptr<CollectionNotifier>> notifiers,
496
                                 std::shared_ptr<Transaction> pin_tr)
497
    : m_pin_tr(pin_tr)
498
    , m_notifiers(std::move(notifiers))
499
{
36,860✔
500
}
36,860✔
501

502
std::optional<VersionID> NotifierPackage::version() const noexcept
503
{
48,601✔
504
    if (m_pin_tr)
48,601✔
505
        return m_pin_tr->get_version_of_current_transaction();
9,346✔
506
    return std::nullopt;
39,255✔
507
}
39,255✔
508

509
void NotifierPackage::package_and_wait(VersionID::version_type target_version)
510
{
6,736✔
511
    if (!m_coordinator || !*this)
6,736✔
512
        return;
246✔
513

3,235✔
514
    m_pin_tr = m_coordinator->package_notifiers(m_notifiers, target_version);
6,490✔
515
    if (m_pin_tr && m_pin_tr->get_version_of_current_transaction().version < target_version)
6,490✔
516
        m_pin_tr.reset();
×
517
}
6,490✔
518

519
void NotifierPackage::before_advance()
520
{
101,628✔
521
    for (auto& notifier : m_notifiers)
101,628✔
522
        notifier->before_advance();
9,893✔
523
}
101,628✔
524

525
void NotifierPackage::after_advance()
526
{
104,057✔
527
    for (auto& notifier : m_notifiers)
104,057✔
528
        notifier->after_advance();
15,159✔
529
}
104,057✔
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