Write a much better comment about thread stuff for AsyncQuery

This commit is contained in:
Thomas Goyne 2016-01-31 06:44:20 -08:00
parent ad46e307a2
commit aa67216574
1 changed files with 27 additions and 3 deletions

View File

@ -36,6 +36,9 @@ AsyncQuery::AsyncQuery(Results& target)
AsyncQuery::~AsyncQuery() AsyncQuery::~AsyncQuery()
{ {
// unregister() may have been called from a different thread than we're being
// destroyed on, so we need to synchronize access to the interesting fields
// modified there
std::lock_guard<std::mutex> lock(m_target_mutex); std::lock_guard<std::mutex> lock(m_target_mutex);
m_realm = nullptr; m_realm = nullptr;
} }
@ -114,13 +117,34 @@ bool AsyncQuery::is_alive() const noexcept
return m_target_results != nullptr; return m_target_results != nullptr;
} }
// Most of the inter-thread synchronization for run(), prepare_handover(),
// attach_to(), detach(), release_query() and deliver() is done by
// RealmCoordinator external to this code, which has some potentially
// non-obvious results on which members are and are not safe to use without
// holding a lock.
//
// attach_to(), detach(), run(), prepare_handover(), and release_query() are
// all only ever called on a single thread. call_callbacks() and deliver() are
// called on the same thread. Calls to prepare_handover() and deliver() are
// guarded by a lock.
//
// In total, this means that the safe data flow is as follows:
// - prepare_handover(), attach_to(), detach() and release_query() can read
// members written by each other
// - deliver() can read members written to in prepare_handover(), deliver(),
// and call_callbacks()
// - call_callbacks() and read members written to in deliver()
//
// Separately from this data flow for the query results, all uses of
// m_target_results, m_callbacks, and m_callback_index must be done with the
// appropriate mutex held to avoid race conditions when the Results object is
// destroyed while the background work is running, and to allow removing
// callbacks from any thread.
void AsyncQuery::run() void AsyncQuery::run()
{ {
REALM_ASSERT(m_sg); REALM_ASSERT(m_sg);
// This function must not touch any members touched in deliver(), as they
// may be called concurrently (as it'd be pretty bad for a running query to
// block the main thread trying to pick up the previous results)
{ {
std::lock_guard<std::mutex> target_lock(m_target_mutex); std::lock_guard<std::mutex> target_lock(m_target_mutex);
// Don't run the query if the results aren't actually going to be used // Don't run the query if the results aren't actually going to be used