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

realm / realm-core / github_pull_request_312964

19 Feb 2025 07:31PM UTC coverage: 90.814% (-0.3%) from 91.119%
github_pull_request_312964

Pull #8071

Evergreen

web-flow
Bump serialize-javascript and mocha

Bumps [serialize-javascript](https://github.com/yahoo/serialize-javascript) to 6.0.2 and updates ancestor dependency [mocha](https://github.com/mochajs/mocha). These dependencies need to be updated together.


Updates `serialize-javascript` from 6.0.0 to 6.0.2
- [Release notes](https://github.com/yahoo/serialize-javascript/releases)
- [Commits](https://github.com/yahoo/serialize-javascript/compare/v6.0.0...v6.0.2)

Updates `mocha` from 10.2.0 to 10.8.2
- [Release notes](https://github.com/mochajs/mocha/releases)
- [Changelog](https://github.com/mochajs/mocha/blob/main/CHANGELOG.md)
- [Commits](https://github.com/mochajs/mocha/compare/v10.2.0...v10.8.2)

---
updated-dependencies:
- dependency-name: serialize-javascript
  dependency-type: indirect
- dependency-name: mocha
  dependency-type: direct:development
...

Signed-off-by: dependabot[bot] <support@github.com>
Pull Request #8071: Bump serialize-javascript and mocha

96552 of 179126 branches covered (53.9%)

212672 of 234185 relevant lines covered (90.81%)

3115802.0 hits per line

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

70.47
/src/realm/object-store/sync/sync_manager.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/sync/sync_manager.hpp>
20

21
#include <realm/object-store/sync/impl/sync_client.hpp>
22
#include <realm/object-store/sync/impl/sync_file.hpp>
23
#include <realm/object-store/sync/impl/app_metadata.hpp>
24
#include <realm/object-store/sync/sync_session.hpp>
25
#include <realm/object-store/sync/sync_user.hpp>
26
#include <realm/object-store/sync/app.hpp>
27
#include <realm/object-store/util/uuid.hpp>
28

29
#include <realm/util/sha_crypto.hpp>
30
#include <realm/util/hex_dump.hpp>
31

32
#include <realm/exceptions.hpp>
33

34
using namespace realm;
35
using namespace realm::_impl;
36

37
SyncClientTimeouts::SyncClientTimeouts()
38
    : connect_timeout(sync::default_connect_timeout)
2,341✔
39
    , connection_linger_time(sync::default_connection_linger_time)
2,341✔
40
    , ping_keepalive_period(sync::default_ping_keepalive_period)
2,341✔
41
    , pong_keepalive_timeout(sync::default_pong_keepalive_timeout)
2,341✔
42
    , fast_reconnect_limit(sync::default_fast_reconnect_limit)
2,341✔
43
{
2,341✔
44
}
2,341✔
45

46
std::shared_ptr<SyncManager> SyncManager::create(const SyncClientConfig& config)
47
{
2,286✔
48
    return std::make_shared<SyncManager>(Private(), config);
2,286✔
49
}
2,286✔
50

51
SyncManager::SyncManager(Private, const SyncClientConfig& config)
52
    : m_config(config)
2,286✔
53
{
2,286✔
54
    // create the initial logger - if the logger_factory is updated later, a new
55
    // logger will be created at that time.
56
    do_make_logger();
2,286✔
57
}
2,286✔
58

59
void SyncManager::tear_down_for_testing()
60
{
2,261✔
61
    close_all_sessions();
2,261✔
62

63
    {
2,261✔
64
        util::CheckedLockGuard lock(m_mutex);
2,261✔
65
        // Stop the client. This will abort any uploads that inactive sessions are waiting for.
66
        if (m_sync_client)
2,261✔
67
            m_sync_client->stop();
2,261✔
68
    }
2,261✔
69

70
    {
2,261✔
71
        util::CheckedUniqueLock lock(m_session_mutex);
2,261✔
72

73
        bool no_sessions = !do_has_existing_sessions();
2,261✔
74
        // There's a race between this function and sessions tearing themselves down waiting for m_session_mutex.
75
        // So we give up to a 5 second grace period for any sessions being torn down to unregister themselves.
76
        auto since_poll_start = [start = std::chrono::steady_clock::now()] {
2,261✔
77
            return std::chrono::steady_clock::now() - start;
×
78
        };
×
79
        for (; !no_sessions && since_poll_start() < std::chrono::seconds(5);
2,261!
80
             no_sessions = !do_has_existing_sessions()) {
2,261✔
81
            lock.unlock();
×
82
            std::this_thread::sleep_for(std::chrono::milliseconds(10));
×
83
            lock.lock();
×
84
        }
×
85
        // Callers of `SyncManager::tear_down_for_testing` should ensure there are no existing sessions
86
        // prior to calling `tear_down_for_testing`.
87
        if (!no_sessions) {
2,261✔
88
            util::CheckedLockGuard lock(m_mutex);
×
89
            for (auto session : m_sessions) {
×
90
                m_logger_ptr->error("open session at path '%1'", session.first);
×
91
            }
×
92
        }
×
93
        REALM_ASSERT_RELEASE(no_sessions);
2,261✔
94

95
        // Destroy any inactive sessions.
96
        // FIXME: We shouldn't have any inactive sessions at this point! Sessions are expected to
97
        // remain inactive until their final upload completes, at which point they are unregistered
98
        // and destroyed. Our call to `sync::Client::stop` above aborts all uploads, so all sessions
99
        // should have already been destroyed.
100
        m_sessions.clear();
2,261✔
101
    }
2,261✔
102

103
    {
2,261✔
104
        util::CheckedLockGuard lock(m_mutex);
2,261✔
105
        // Destroy the client now that we have no remaining sessions.
106
        m_sync_client.reset();
2,261✔
107
        m_logger_ptr.reset();
2,261✔
108
    }
2,261✔
109
}
2,261✔
110

111
void SyncManager::set_log_level(util::Logger::Level level) noexcept
112
{
×
113
    util::CheckedLockGuard lock(m_mutex);
×
114
    m_config.log_level = level;
×
115
    // Update the level threshold in the already created logger
116
    if (m_logger_ptr) {
×
117
        m_logger_ptr->set_level_threshold(level);
×
118
    }
×
119
}
×
120

121
void SyncManager::set_logger_factory(SyncClientConfig::LoggerFactory factory)
122
{
×
123
    util::CheckedLockGuard lock(m_mutex);
×
124
    m_config.logger_factory = std::move(factory);
×
125

126
    if (m_sync_client)
×
127
        throw LogicError(ErrorCodes::IllegalOperation,
×
128
                         "Cannot set the logger factory after creating the sync client");
×
129

130
    // Create a new logger using the new factory
131
    do_make_logger();
×
132
}
×
133

134
void SyncManager::do_make_logger()
135
{
2,286✔
136
    if (m_config.logger_factory) {
2,286✔
137
        m_logger_ptr = m_config.logger_factory(m_config.log_level);
1✔
138
    }
1✔
139
    else {
2,285✔
140
        m_logger_ptr = util::Logger::get_default_logger();
2,285✔
141
    }
2,285✔
142
    REALM_ASSERT(m_logger_ptr);
2,286✔
143
}
2,286✔
144

145
std::shared_ptr<util::Logger> SyncManager::get_logger() const
146
{
2,187✔
147
    util::CheckedLockGuard lock(m_mutex);
2,187✔
148
    return m_logger_ptr;
2,187✔
149
}
2,187✔
150

151
void SyncManager::set_user_agent(std::string user_agent)
152
{
×
153
    util::CheckedLockGuard lock(m_mutex);
×
154
    m_config.user_agent_application_info = std::move(user_agent);
×
155
}
×
156

157
void SyncManager::set_timeouts(SyncClientTimeouts timeouts)
158
{
×
159
    util::CheckedLockGuard lock(m_mutex);
×
160
    m_config.timeouts = timeouts;
×
161
}
×
162

163
void SyncManager::reconnect() const
164
{
×
165
    util::CheckedLockGuard lock(m_session_mutex);
×
166
    for (auto& it : m_sessions) {
×
167
        it.second->handle_reconnect();
×
168
    }
×
169
}
×
170

171
util::Logger::Level SyncManager::log_level() const noexcept
172
{
×
173
    util::CheckedLockGuard lock(m_mutex);
×
174
    return m_config.log_level;
×
175
}
×
176

177
SyncManager::~SyncManager() NO_THREAD_SAFETY_ANALYSIS
178
{
2,286✔
179
    // Grab the current sessions under a lock so we can shut them down. We have to
180
    // release the lock before calling them as shutdown_and_wait() will call
181
    // back into us.
182
    decltype(m_sessions) current_sessions;
2,286✔
183
    {
2,286✔
184
        util::CheckedLockGuard lk(m_session_mutex);
2,286✔
185
        m_sessions.swap(current_sessions);
2,286✔
186
    }
2,286✔
187

188
    for (auto& [_, session] : current_sessions) {
2,286✔
189
        session->detach_from_sync_manager();
×
190
    }
×
191

192
    {
2,286✔
193
        util::CheckedLockGuard lk(m_mutex);
2,286✔
194
        // Stop the client. This will abort any uploads that inactive sessions are waiting for.
195
        if (m_sync_client)
2,286✔
196
            m_sync_client->stop();
1✔
197
    }
2,286✔
198
}
2,286✔
199

200
std::vector<std::shared_ptr<SyncSession>> SyncManager::get_all_sessions() const
201
{
×
202
    util::CheckedLockGuard lock(m_session_mutex);
×
203
    std::vector<std::shared_ptr<SyncSession>> sessions;
×
204
    for (auto& [_, session] : m_sessions) {
×
205
        if (auto external_reference = session->existing_external_reference())
×
206
            sessions.push_back(std::move(external_reference));
×
207
    }
×
208
    return sessions;
×
209
}
×
210

211
std::vector<std::shared_ptr<SyncSession>> SyncManager::get_all_sessions_for(const SyncUser& user) const
212
{
7,319✔
213
    util::CheckedLockGuard lock(m_session_mutex);
7,319✔
214
    std::vector<std::shared_ptr<SyncSession>> sessions;
7,319✔
215
    for (auto& [_, session] : m_sessions) {
7,319✔
216
        if (session->user().get() == &user) {
133✔
217
            if (auto external_reference = session->existing_external_reference())
52✔
218
                sessions.push_back(std::move(external_reference));
52✔
219
        }
52✔
220
    }
133✔
221
    return sessions;
7,319✔
222
}
7,319✔
223

224
std::shared_ptr<SyncSession> SyncManager::get_existing_active_session(const std::string& path) const
225
{
×
226
    util::CheckedLockGuard lock(m_session_mutex);
×
227
    if (auto session = get_existing_session_locked(path)) {
×
228
        if (auto external_reference = session->existing_external_reference())
×
229
            return external_reference;
×
230
    }
×
231
    return nullptr;
×
232
}
×
233

234
std::shared_ptr<SyncSession> SyncManager::get_existing_session_locked(const std::string& path) const
235
{
1,575✔
236
    auto it = m_sessions.find(path);
1,575✔
237
    return it == m_sessions.end() ? nullptr : it->second;
1,575✔
238
}
1,575✔
239

240
std::shared_ptr<SyncSession> SyncManager::get_existing_session(const std::string& path) const
241
{
792✔
242
    util::CheckedLockGuard lock(m_session_mutex);
792✔
243
    if (auto session = get_existing_session_locked(path))
792✔
244
        return session->external_reference();
113✔
245

246
    return nullptr;
679✔
247
}
792✔
248

249
std::shared_ptr<SyncSession> SyncManager::get_session(std::shared_ptr<DB> db, const RealmConfig& config)
250
{
783✔
251
    auto& client = get_sync_client(); // Throws
783✔
252
#ifndef __EMSCRIPTEN__
783✔
253
    auto path = db->get_path();
783✔
254
    REALM_ASSERT_EX(path == config.path, path, config.path);
783✔
255
#else
256
    auto path = config.path;
257
#endif
258
    REALM_ASSERT(config.sync_config);
783✔
259

260
    util::CheckedUniqueLock lock(m_session_mutex);
783✔
261
    if (auto session = get_existing_session_locked(path)) {
783✔
262
        return session->external_reference();
5✔
263
    }
5✔
264

265
    auto shared_session = SyncSession::create(client, std::move(db), config, this);
778✔
266
    m_sessions[path] = shared_session;
778✔
267

268
    // Create the external reference immediately to ensure that the session will become
269
    // inactive if an exception is thrown in the following code.
270
    return shared_session->external_reference();
778✔
271
}
783✔
272

273
bool SyncManager::has_existing_sessions()
274
{
6✔
275
    util::CheckedLockGuard lock(m_session_mutex);
6✔
276
    return do_has_existing_sessions();
6✔
277
}
6✔
278

279
bool SyncManager::do_has_existing_sessions()
280
{
2,267✔
281
    return std::any_of(m_sessions.begin(), m_sessions.end(), [](auto& element) {
2,267✔
282
        return element.second->existing_external_reference();
1✔
283
    });
1✔
284
}
2,267✔
285

286
void SyncManager::wait_for_sessions_to_terminate()
287
{
5✔
288
    auto& client = get_sync_client(); // Throws
5✔
289
    client.wait_for_session_terminations();
5✔
290
}
5✔
291

292
void SyncManager::unregister_session(const std::string& path)
293
{
1,200✔
294
    util::CheckedUniqueLock lock(m_session_mutex);
1,200✔
295
    auto it = m_sessions.find(path);
1,200✔
296
    if (it == m_sessions.end()) {
1,200✔
297
        // The session may already be unregistered. This always happens in the
298
        // SyncManager destructor, and can also happen due to multiple threads
299
        // tearing things down at once.
300
        return;
40✔
301
    }
40✔
302

303
    // Sync session teardown calls this function, so we need to be careful with
304
    // locking here. We need to unlock `m_session_mutex` before we do anything
305
    // which could result in a re-entrant call or we'll deadlock, which in this
306
    // function means unlocking before we destroy a `shared_ptr<SyncSession>`
307
    // (either the external reference or internal reference versions).
308
    // The external reference version will only be the final reference if
309
    // another thread drops a reference while we're in this function.
310
    // Dropping the final internal reference does not appear to ever actually
311
    // result in a recursive call to this function at the time this comment was
312
    // written, but releasing the lock in that case as well is still safer.
313

314
    if (auto existing_session = it->second->existing_external_reference()) {
1,160✔
315
        // We got here because the session entered the inactive state, but
316
        // there's still someone referencing it so we should leave it be. This
317
        // can happen if the user was logged out, or if all Realms using the
318
        // session were destroyed but the SDK user is holding onto the session.
319

320
        // Explicit unlock so that `existing_session`'s destructor runs after
321
        // the unlock for the reasons noted above
322
        lock.unlock();
420✔
323
        return;
420✔
324
    }
420✔
325

326
    // Remove the session from the map while holding the lock, but then defer
327
    // destroying it until after we unlock the mutex for the reasons noted above.
328
    auto session = m_sessions.extract(it);
740✔
329
    lock.unlock();
740✔
330
}
740✔
331

332
void SyncManager::update_sessions_for(SyncUser& user, SyncUser::State old_state, SyncUser::State new_state,
333
                                      std::string_view new_access_token)
334
{
7,309✔
335
    bool should_revive = old_state != SyncUser::State::LoggedIn && new_state == SyncUser::State::LoggedIn;
7,309✔
336
    bool should_stop = old_state == SyncUser::State::LoggedIn && new_state != SyncUser::State::LoggedIn;
7,309✔
337

338
    auto sessions = get_all_sessions_for(user);
7,309✔
339
    if (new_access_token.size()) {
7,309✔
340
        for (auto& session : sessions) {
4,818✔
341
            session->update_access_token(new_access_token);
12✔
342
        }
12✔
343
    }
4,818✔
344
    else if (should_revive) {
2,491✔
345
        for (auto& session : sessions) {
×
346
            session->revive_if_needed();
×
347
        }
×
348
    }
×
349
    else if (should_stop) {
2,491✔
350
        for (auto& session : sessions) {
43✔
351
            session->force_close();
15✔
352
        }
15✔
353
    }
43✔
354
}
7,309✔
355

356
void SyncManager::set_session_multiplexing(bool allowed)
357
{
2✔
358
    util::CheckedLockGuard lock(m_mutex);
2✔
359
    if (m_config.multiplex_sessions == allowed)
2✔
360
        return; // Already enabled, we can ignore
1✔
361

362
    if (m_sync_client)
1✔
363
        throw LogicError(ErrorCodes::IllegalOperation,
×
364
                         "Cannot enable session multiplexing after creating the sync client");
×
365

366
    m_config.multiplex_sessions = allowed;
1✔
367
}
1✔
368

369
SyncClient& SyncManager::get_sync_client() const
370
{
3,411✔
371
    util::CheckedLockGuard lock(m_mutex);
3,411✔
372
    if (!m_sync_client)
3,411✔
373
        m_sync_client = create_sync_client(); // Throws
2,262✔
374
    return *m_sync_client;
3,411✔
375
}
3,411✔
376

377
std::unique_ptr<SyncClient> SyncManager::create_sync_client() const
378
{
2,262✔
379
    REALM_ASSERT(m_logger_ptr);
2,262✔
380
    return std::make_unique<SyncClient>(m_logger_ptr, m_config, weak_from_this());
2,262✔
381
}
2,262✔
382

383
void SyncManager::close_all_sessions()
384
{
2,261✔
385
    // force_close() will call unregister_session(), which requires m_session_mutex,
386
    // so we need to iterate over them without holding the lock.
387
    decltype(m_sessions) sessions;
2,261✔
388
    {
2,261✔
389
        util::CheckedLockGuard lk(m_session_mutex);
2,261✔
390
        m_sessions.swap(sessions);
2,261✔
391
    }
2,261✔
392

393
    for (auto& [_, session] : sessions) {
2,261✔
394
        session->force_close();
38✔
395
    }
38✔
396

397
    get_sync_client().wait_for_session_terminations();
2,261✔
398
}
2,261✔
399

400
void SyncManager::set_sync_route(std::string sync_route, bool verified)
401
{
4,473✔
402
    REALM_ASSERT(!sync_route.empty()); // Cannot be set to empty string
4,473✔
403
    {
4,473✔
404
        util::CheckedLockGuard lock(m_mutex);
4,473✔
405
        m_sync_route = sync_route;
4,473✔
406
        m_sync_route_verified = verified;
4,473✔
407
    }
4,473✔
408
}
4,473✔
409

410
void SyncManager::restart_all_sessions()
411
{
×
412
    // Restart the sessions that are currently active
413
    auto sessions = get_all_sessions();
×
414
    for (auto& session : sessions) {
×
415
        session->restart_session();
×
416
    }
×
417
}
×
418

419
void SyncManager::OnlyForTesting::voluntary_disconnect_all_connections(SyncManager& mgr)
420
{
2✔
421
    mgr.get_sync_client().voluntary_disconnect_all_connections();
2✔
422
}
2✔
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