Skip to content

Commit 35112b9

Browse files
committed
doc: describe ThreadContext struct and synchronization requirements
1 parent 6d6096a commit 35112b9

File tree

1 file changed

+47
-3
lines changed

1 file changed

+47
-3
lines changed

include/mp/proxy-io.h

Lines changed: 47 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -534,6 +534,10 @@ void ProxyServerBase<Interface, Impl>::invokeDestroy()
534534
CleanupRun(m_context.cleanup_fns);
535535
}
536536

537+
//! Map from Connection to local or remote thread handle which will be used over
538+
//! that connection. This map will typically only contain one entry, but can
539+
//! contain multiple if a single thread makes IPC calls over multiple
540+
//! connections.
537541
using ConnThreads = std::map<Connection*, ProxyClient<Thread>>;
538542
using ConnThread = ConnThreads::iterator;
539543

@@ -542,20 +546,58 @@ using ConnThread = ConnThreads::iterator;
542546
// inserted bool.
543547
std::tuple<ConnThread, bool> SetThread(GuardedRef<ConnThreads> threads, Connection* connection, const std::function<Thread::Client()>& make_thread);
544548

549+
//! The thread_local ThreadContext g_thread_context struct provides information
550+
//! about individual threads and a way of communicating between them. Because
551+
//! it's a thread local struct, each ThreadContext instance is initialized by
552+
//! the thread that owns it.
553+
//!
554+
//! ThreadContext is used for any client threads created externally which make
555+
//! IPC calls, and for server threads created by
556+
//! ProxyServer<ThreadMap>::makeThread() which execute IPC calls for clients.
557+
//!
558+
//! In both cases, the struct holds information like the thread name, and a
559+
//! Waiter object where the EventLoop can post incoming IPC requests to execute
560+
//! on the thread. The struct also holds ConnThread maps associating the thread
561+
//! with local and remote ProxyClient<Thread> objects.
545562
struct ThreadContext
546563
{
547564
//! Identifying string for debug.
548565
std::string thread_name;
549566

550-
//! Waiter object used to allow client threads blocked waiting for a server
551-
//! response to execute callbacks made from the client's corresponding
552-
//! server thread.
567+
//! Waiter object used to allow remote clients to execute code on this
568+
//! thread. For server threads created by
569+
//! ProxyServer<ThreadMap>::makeThread(), this is initialized in that
570+
//! function. Otherwise, for client threads created externally, this is
571+
//! initialized the first time the thread tries to make an IPC call. Having
572+
//! a waiter is necessary for threads making IPC calls in case a server they
573+
//! are calling expects them to execute a callback during the call, before
574+
//! it sends a response.
575+
//!
576+
//! For IPC client threads, the Waiter pointer is never cleared and the Waiter
577+
//! just gets destroyed when the thread does. For server threads created by
578+
//! makeThread(), this pointer is set to null in the ~ProxyServer<Thread> as
579+
//! a signal for the thread to exit and destroy itself. In both cases, the
580+
//! same Waiter object is used across different calls and only created and
581+
//! destroyed once for the lifetime of the thread.
553582
std::unique_ptr<Waiter> waiter = nullptr;
554583

555584
//! When client is making a request to a server, this is the
556585
//! `callbackThread` argument it passes in the request, used by the server
557586
//! in case it needs to make callbacks into the client that need to execute
558587
//! while the client is waiting. This will be set to a local thread object.
588+
//!
589+
//! Synchronization note: The callback_thread and request_thread maps are
590+
//! only ever accessed internally by this thread's destructor and externally
591+
//! by Cap'n Proto event loop threads. Since it's possible for IPC client
592+
//! threads to make calls over different connections that could have
593+
//! different event loops, these maps are guarded by Waiter::m_mutex in case
594+
//! different event loop threads add or remove map entries simultaneously.
595+
//! However, individual ProxyClient<Thread> objects in the maps will only be
596+
//! associated with one event loop and guarded by EventLoop::m_mutex. So
597+
//! Waiter::m_mutex does not need to be held while accessing individual
598+
//! ProxyClient<Thread> instances, and may even need to be released to
599+
//! respect lock order and avoid locking Waiter::m_mutex before
600+
//! EventLoop::m_mutex.
559601
ConnThreads callback_threads MP_GUARDED_BY(waiter->m_mutex);
560602

561603
//! When client is making a request to a server, this is the `thread`
@@ -565,6 +607,8 @@ struct ThreadContext
565607
//! by makeThread. If a client call is being made from a thread currently
566608
//! handling a server request, this will be set to the `callbackThread`
567609
//! request thread argument passed in that request.
610+
//!
611+
//! Synchronization note: \ref callback_threads note applies here as well.
568612
ConnThreads request_threads MP_GUARDED_BY(waiter->m_mutex);
569613

570614
//! Whether this thread is a capnp event loop thread. Not really used except

0 commit comments

Comments
 (0)