Skip to content

Commit 62ca644

Browse files
committed
Adopt CheckedMutex in ClientImpl
1 parent 702189a commit 62ca644

File tree

3 files changed

+91
-75
lines changed

3 files changed

+91
-75
lines changed

src/realm/sync/client.cpp

Lines changed: 40 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -222,7 +222,8 @@ class SessionWrapper final : public util::AtomicRefCountBase, DB::CommitListener
222222

223223
bool m_suspended = false;
224224

225-
// Set when the session has been abandoned, but before it's been finalized.
225+
// Set when the session has been abandoned. After this point none of the
226+
// public API functions should be called again.
226227
bool m_abandoned = false;
227228
// Has the SessionWrapper been finalized?
228229
bool m_finalized = false;
@@ -439,7 +440,7 @@ bool ClientImpl::wait_for_session_terminations_or_client_stopped()
439440
// Thread safety required
440441

441442
{
442-
std::lock_guard lock{m_mutex};
443+
util::CheckedLockGuard lock{m_mutex};
443444
m_sessions_terminated = false;
444445
}
445446

@@ -466,16 +467,19 @@ bool ClientImpl::wait_for_session_terminations_or_client_stopped()
466467
else if (!status.is_ok())
467468
throw Exception(status);
468469

469-
std::lock_guard lock{m_mutex};
470-
m_sessions_terminated = true;
470+
{
471+
util::CheckedLockGuard lock{m_mutex};
472+
m_sessions_terminated = true;
473+
}
471474
m_wait_or_client_stopped_cond.notify_all();
472475
}); // Throws
473476

474477
bool completion_condition_was_satisfied;
475478
{
476-
std::unique_lock lock{m_mutex};
477-
while (!m_sessions_terminated && !m_stopped)
478-
m_wait_or_client_stopped_cond.wait(lock);
479+
util::CheckedUniqueLock lock{m_mutex};
480+
m_wait_or_client_stopped_cond.wait(lock.native_handle(), [&]() REQUIRES(m_mutex) {
481+
return m_sessions_terminated || m_stopped;
482+
});
479483
completion_condition_was_satisfied = !m_stopped;
480484
}
481485
return completion_condition_was_satisfied;
@@ -510,13 +514,13 @@ void ClientImpl::drain_connections_on_loop()
510514
void ClientImpl::shutdown_and_wait()
511515
{
512516
shutdown();
513-
std::unique_lock lock{m_drain_mutex};
517+
util::CheckedUniqueLock lock{m_drain_mutex};
514518
if (m_drained) {
515519
return;
516520
}
517521

518522
logger.debug("Waiting for %1 connections to drain", m_num_connections);
519-
m_drain_cv.wait(lock, [&] {
523+
m_drain_cv.wait(lock.native_handle(), [&]() REQUIRES(m_drain_mutex) {
520524
return m_num_connections == 0 && m_outstanding_posts == 0;
521525
});
522526

@@ -526,12 +530,12 @@ void ClientImpl::shutdown_and_wait()
526530
void ClientImpl::shutdown() noexcept
527531
{
528532
{
529-
std::lock_guard lock{m_mutex};
533+
util::CheckedLockGuard lock{m_mutex};
530534
if (m_stopped)
531535
return;
532536
m_stopped = true;
533-
m_wait_or_client_stopped_cond.notify_all();
534537
}
538+
m_wait_or_client_stopped_cond.notify_all();
535539

536540
drain_connections_on_loop();
537541
}
@@ -541,7 +545,7 @@ void ClientImpl::register_unactualized_session_wrapper(SessionWrapper* wrapper,
541545
{
542546
// Thread safety required.
543547
{
544-
std::lock_guard lock{m_mutex};
548+
util::CheckedLockGuard lock{m_mutex};
545549
REALM_ASSERT(m_actualize_and_finalize);
546550
wrapper->mark_initiated();
547551
m_unactualized_session_wrappers.emplace(wrapper, std::move(endpoint)); // Throws
@@ -554,7 +558,7 @@ void ClientImpl::register_abandoned_session_wrapper(util::bind_ptr<SessionWrappe
554558
{
555559
// Thread safety required.
556560
{
557-
std::lock_guard lock{m_mutex};
561+
util::CheckedLockGuard lock{m_mutex};
558562
REALM_ASSERT(m_actualize_and_finalize);
559563
wrapper->mark_abandoned();
560564

@@ -581,7 +585,7 @@ void ClientImpl::actualize_and_finalize_session_wrappers()
581585
SessionWrapperStack abandoned_session_wrappers;
582586
bool stopped;
583587
{
584-
std::lock_guard lock{m_mutex};
588+
util::CheckedLockGuard lock{m_mutex};
585589
swap(m_unactualized_session_wrappers, unactualized_session_wrappers);
586590
swap(m_abandoned_session_wrappers, abandoned_session_wrappers);
587591
stopped = m_stopped;
@@ -643,7 +647,7 @@ ClientImpl::Connection& ClientImpl::get_connection(ServerEndpoint endpoint,
643647
m_prev_connection_ident = ident;
644648
was_created = true;
645649
{
646-
std::lock_guard lk(m_drain_mutex);
650+
util::CheckedLockGuard lk(m_drain_mutex);
647651
++m_num_connections;
648652
}
649653
return conn;
@@ -671,10 +675,13 @@ void ClientImpl::remove_connection(ClientImpl::Connection& conn) noexcept
671675
server_slot.alt_connections.erase(j);
672676
}
673677

678+
bool notify;
674679
{
675-
std::lock_guard lk(m_drain_mutex);
680+
util::CheckedLockGuard lk(m_drain_mutex);
676681
REALM_ASSERT(m_num_connections);
677-
--m_num_connections;
682+
notify = --m_num_connections <= 0;
683+
}
684+
if (notify) {
678685
m_drain_cv.notify_all();
679686
}
680687
}
@@ -1480,7 +1487,7 @@ bool SessionWrapper::wait_for_upload_complete_or_client_stopped()
14801487

14811488
std::int_fast64_t target_mark;
14821489
{
1483-
std::lock_guard lock{m_client.m_mutex};
1490+
util::CheckedLockGuard lock{m_client.m_mutex};
14841491
target_mark = ++m_target_upload_mark;
14851492
}
14861493

@@ -1508,9 +1515,10 @@ bool SessionWrapper::wait_for_upload_complete_or_client_stopped()
15081515

15091516
bool completion_condition_was_satisfied;
15101517
{
1511-
std::unique_lock lock{m_client.m_mutex};
1512-
while (m_reached_upload_mark < target_mark && !m_client.m_stopped)
1513-
m_client.m_wait_or_client_stopped_cond.wait(lock);
1518+
util::CheckedUniqueLock lock{m_client.m_mutex};
1519+
m_client.m_wait_or_client_stopped_cond.wait(lock.native_handle(), [&]() REQUIRES(m_client.m_mutex) {
1520+
return m_reached_upload_mark >= target_mark || m_client.m_stopped;
1521+
});
15141522
completion_condition_was_satisfied = !m_client.m_stopped;
15151523
}
15161524
return completion_condition_was_satisfied;
@@ -1525,7 +1533,7 @@ bool SessionWrapper::wait_for_download_complete_or_client_stopped()
15251533

15261534
std::int_fast64_t target_mark;
15271535
{
1528-
std::lock_guard lock{m_client.m_mutex};
1536+
util::CheckedLockGuard lock{m_client.m_mutex};
15291537
target_mark = ++m_target_download_mark;
15301538
}
15311539

@@ -1553,9 +1561,10 @@ bool SessionWrapper::wait_for_download_complete_or_client_stopped()
15531561

15541562
bool completion_condition_was_satisfied;
15551563
{
1556-
std::unique_lock lock{m_client.m_mutex};
1557-
while (m_reached_download_mark < target_mark && !m_client.m_stopped)
1558-
m_client.m_wait_or_client_stopped_cond.wait(lock);
1564+
util::CheckedUniqueLock lock{m_client.m_mutex};
1565+
m_client.m_wait_or_client_stopped_cond.wait(lock.native_handle(), [&]() REQUIRES(m_client.m_mutex) {
1566+
return m_reached_download_mark >= target_mark || m_client.m_stopped;
1567+
});
15591568
completion_condition_was_satisfied = !m_client.m_stopped;
15601569
}
15611570
return completion_condition_was_satisfied;
@@ -1688,6 +1697,10 @@ void SessionWrapper::force_close()
16881697
}
16891698

16901699
// Must be called from event loop thread
1700+
//
1701+
// `m_client.m_mutex` is not held while this is called, but it is guaranteed to
1702+
// have been acquired at some point in between the final read or write ever made
1703+
// from a different thread and when this is called.
16911704
void SessionWrapper::finalize()
16921705
{
16931706
REALM_ASSERT(m_actualized);
@@ -1780,7 +1793,7 @@ void SessionWrapper::on_upload_completion()
17801793
m_download_completion_handlers.push_back(std::move(handler)); // Throws
17811794
m_sync_completion_handlers.pop_back();
17821795
}
1783-
std::lock_guard lock{m_client.m_mutex};
1796+
util::CheckedLockGuard lock{m_client.m_mutex};
17841797
if (m_staged_upload_mark > m_reached_upload_mark) {
17851798
m_reached_upload_mark = m_staged_upload_mark;
17861799
m_client.m_wait_or_client_stopped_cond.notify_all();
@@ -1808,7 +1821,7 @@ void SessionWrapper::on_download_completion()
18081821
m_flx_pending_mark_version = SubscriptionSet::EmptyVersion;
18091822
}
18101823

1811-
std::lock_guard lock{m_client.m_mutex};
1824+
util::CheckedLockGuard lock{m_client.m_mutex};
18121825
if (m_staged_download_mark > m_reached_download_mark) {
18131826
m_reached_download_mark = m_staged_download_mark;
18141827
m_client.m_wait_or_client_stopped_cond.notify_all();

src/realm/sync/noinst/client_impl_base.cpp

Lines changed: 23 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -230,21 +230,31 @@ ClientImpl::ClientImpl(ClientConfig config)
230230
});
231231
}
232232

233+
void ClientImpl::incr_outstanding_posts()
234+
{
235+
util::CheckedLockGuard lock(m_drain_mutex);
236+
++m_outstanding_posts;
237+
m_drained = false;
238+
}
239+
240+
void ClientImpl::decr_outstanding_posts()
241+
{
242+
util::CheckedLockGuard lock(m_drain_mutex);
243+
REALM_ASSERT(m_outstanding_posts);
244+
if (--m_outstanding_posts <= 0) {
245+
// Notify must happen with lock held or another thread could destroy
246+
// ClientImpl between when we release the lock and when we call notify
247+
m_drain_cv.notify_all();
248+
}
249+
}
233250

234251
void ClientImpl::post(SyncSocketProvider::FunctionHandler&& handler)
235252
{
236253
REALM_ASSERT(m_socket_provider);
237-
{
238-
std::lock_guard lock(m_drain_mutex);
239-
++m_outstanding_posts;
240-
m_drained = false;
241-
}
254+
incr_outstanding_posts();
242255
m_socket_provider->post([handler = std::move(handler), this](Status status) {
243256
auto decr_guard = util::make_scope_exit([&]() noexcept {
244-
std::lock_guard lock(m_drain_mutex);
245-
REALM_ASSERT(m_outstanding_posts);
246-
--m_outstanding_posts;
247-
m_drain_cv.notify_all();
257+
decr_outstanding_posts();
248258
});
249259
handler(status);
250260
});
@@ -274,18 +284,12 @@ SyncSocketProvider::SyncTimer ClientImpl::create_timer(std::chrono::milliseconds
274284
SyncSocketProvider::FunctionHandler&& handler)
275285
{
276286
REALM_ASSERT(m_socket_provider);
277-
{
278-
std::lock_guard lock(m_drain_mutex);
279-
++m_outstanding_posts;
280-
m_drained = false;
281-
}
287+
incr_outstanding_posts();
282288
return m_socket_provider->create_timer(delay, [handler = std::move(handler), this](Status status) {
289+
auto decr_guard = util::make_scope_exit([&]() noexcept {
290+
decr_outstanding_posts();
291+
});
283292
handler(status);
284-
285-
std::lock_guard lock(m_drain_mutex);
286-
REALM_ASSERT(m_outstanding_posts);
287-
--m_outstanding_posts;
288-
m_drain_cv.notify_all();
289293
});
290294
}
291295

src/realm/sync/noinst/client_impl_base.hpp

Lines changed: 28 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -16,8 +16,8 @@
1616
#include <realm/sync/subscriptions.hpp>
1717
#include <realm/sync/trigger.hpp>
1818
#include <realm/util/buffer_stream.hpp>
19+
#include <realm/util/checked_mutex.hpp>
1920
#include <realm/util/logger.hpp>
20-
#include <realm/util/optional.hpp>
2121
#include <realm/util/span.hpp>
2222

2323
#include <cstdint>
@@ -207,19 +207,19 @@ class ClientImpl {
207207

208208
static constexpr int get_oldest_supported_protocol_version() noexcept;
209209

210-
void shutdown() noexcept;
210+
void shutdown() noexcept REQUIRES(!m_mutex, !m_drain_mutex);
211211

212-
void shutdown_and_wait();
212+
void shutdown_and_wait() REQUIRES(!m_mutex, !m_drain_mutex);
213213

214214
const std::string& get_user_agent_string() const noexcept;
215215
ReconnectMode get_reconnect_mode() const noexcept;
216216
bool is_dry_run() const noexcept;
217217

218218
// Functions to post onto the event loop and create an event loop timer using the
219219
// SyncSocketProvider
220-
void post(SyncSocketProvider::FunctionHandler&& handler);
220+
void post(SyncSocketProvider::FunctionHandler&& handler) REQUIRES(!m_drain_mutex);
221221
SyncSocketProvider::SyncTimer create_timer(std::chrono::milliseconds delay,
222-
SyncSocketProvider::FunctionHandler&& handler);
222+
SyncSocketProvider::FunctionHandler&& handler) REQUIRES(!m_drain_mutex);
223223
using SyncTrigger = std::unique_ptr<Trigger<ClientImpl>>;
224224
SyncTrigger create_trigger(SyncSocketProvider::FunctionHandler&& handler);
225225

@@ -229,11 +229,11 @@ class ClientImpl {
229229
bool decompose_server_url(const std::string& url, ProtocolEnvelope& protocol, std::string& address,
230230
port_type& port, std::string& path) const;
231231

232-
void cancel_reconnect_delay();
233-
bool wait_for_session_terminations_or_client_stopped();
232+
void cancel_reconnect_delay() REQUIRES(!m_drain_mutex);
233+
bool wait_for_session_terminations_or_client_stopped() REQUIRES(!m_mutex, !m_drain_mutex);
234234
// Async version of wait_for_session_terminations_or_client_stopped().
235-
util::Future<void> notify_session_terminated();
236-
void voluntary_disconnect_all_connections();
235+
util::Future<void> notify_session_terminated() REQUIRES(!m_drain_mutex);
236+
void voluntary_disconnect_all_connections() REQUIRES(!m_drain_mutex);
237237

238238
private:
239239
using connection_ident_type = std::int_fast64_t;
@@ -283,36 +283,32 @@ class ClientImpl {
283283
// Must be accessed only by event loop thread
284284
connection_ident_type m_prev_connection_ident = 0;
285285

286-
std::mutex m_drain_mutex;
286+
util::CheckedMutex m_drain_mutex;
287287
std::condition_variable m_drain_cv;
288-
bool m_drained = false;
289-
uint64_t m_outstanding_posts = 0;
290-
uint64_t m_num_connections = 0;
288+
bool m_drained GUARDED_BY(m_drain_mutex) = false;
289+
uint64_t m_outstanding_posts GUARDED_BY(m_drain_mutex) = 0;
290+
uint64_t m_num_connections GUARDED_BY(m_drain_mutex) = 0;
291291

292-
std::mutex m_mutex;
292+
util::CheckedMutex m_mutex;
293293

294-
bool m_stopped = false; // Protected by `m_mutex`
295-
bool m_sessions_terminated = false; // Protected by `m_mutex`
294+
bool m_stopped GUARDED_BY(m_mutex) = false;
295+
bool m_sessions_terminated GUARDED_BY(m_mutex) = false;
296296

297297
// The set of session wrappers that are not yet wrapping a session object,
298298
// and are not yet abandoned (still referenced by the application).
299-
//
300-
// Protected by `m_mutex`.
301-
std::map<SessionWrapper*, ServerEndpoint> m_unactualized_session_wrappers;
299+
std::map<SessionWrapper*, ServerEndpoint> m_unactualized_session_wrappers GUARDED_BY(m_mutex);
302300

303301
// The set of session wrappers that were successfully actualized, but are
304302
// now abandoned (no longer referenced by the application), and have not yet
305303
// been finalized. Order in queue is immaterial.
306-
//
307-
// Protected by `m_mutex`.
308-
SessionWrapperStack m_abandoned_session_wrappers;
304+
SessionWrapperStack m_abandoned_session_wrappers GUARDED_BY(m_mutex);
309305

310-
// Protected by `m_mutex`.
306+
// Used with m_mutex
311307
std::condition_variable m_wait_or_client_stopped_cond;
312308

313-
void register_unactualized_session_wrapper(SessionWrapper*, ServerEndpoint);
314-
void register_abandoned_session_wrapper(util::bind_ptr<SessionWrapper>) noexcept;
315-
void actualize_and_finalize_session_wrappers();
309+
void register_unactualized_session_wrapper(SessionWrapper*, ServerEndpoint) REQUIRES(!m_mutex);
310+
void register_abandoned_session_wrapper(util::bind_ptr<SessionWrapper>) noexcept REQUIRES(!m_mutex);
311+
void actualize_and_finalize_session_wrappers() REQUIRES(!m_mutex);
316312

317313
// Get or create a connection. If a connection exists for the specified
318314
// endpoint, it will be returned, otherwise a new connection will be
@@ -340,13 +336,16 @@ class ClientImpl {
340336
bool verify_servers_ssl_certificate,
341337
util::Optional<std::string> ssl_trust_certificate_path,
342338
std::function<SyncConfig::SSLVerifyCallback>,
343-
util::Optional<SyncConfig::ProxyConfig>, bool& was_created);
339+
util::Optional<SyncConfig::ProxyConfig>, bool& was_created) REQUIRES(!m_drain_mutex);
344340

345341
// Destroys the specified connection.
346-
void remove_connection(ClientImpl::Connection&) noexcept;
342+
void remove_connection(ClientImpl::Connection&) noexcept REQUIRES(!m_drain_mutex);
347343

348344
void drain_connections();
349-
void drain_connections_on_loop();
345+
void drain_connections_on_loop() REQUIRES(!m_drain_mutex);
346+
347+
void incr_outstanding_posts() REQUIRES(!m_drain_mutex);
348+
void decr_outstanding_posts() REQUIRES(!m_drain_mutex);
350349

351350
session_ident_type get_next_session_ident() noexcept;
352351

0 commit comments

Comments
 (0)