@@ -534,6 +534,10 @@ void ProxyServerBase<Interface, Impl>::invokeDestroy()
534
534
CleanupRun (m_context.cleanup_fns );
535
535
}
536
536
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.
537
541
using ConnThreads = std::map<Connection*, ProxyClient<Thread>>;
538
542
using ConnThread = ConnThreads::iterator;
539
543
@@ -542,20 +546,58 @@ using ConnThread = ConnThreads::iterator;
542
546
// inserted bool.
543
547
std::tuple<ConnThread, bool > SetThread (GuardedRef<ConnThreads> threads, Connection* connection, const std::function<Thread::Client()>& make_thread);
544
548
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.
545
562
struct ThreadContext
546
563
{
547
564
// ! Identifying string for debug.
548
565
std::string thread_name;
549
566
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.
553
582
std::unique_ptr<Waiter> waiter = nullptr ;
554
583
555
584
// ! When client is making a request to a server, this is the
556
585
// ! `callbackThread` argument it passes in the request, used by the server
557
586
// ! in case it needs to make callbacks into the client that need to execute
558
587
// ! 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.
559
601
ConnThreads callback_threads MP_GUARDED_BY (waiter->m_mutex);
560
602
561
603
// ! When client is making a request to a server, this is the `thread`
@@ -565,6 +607,8 @@ struct ThreadContext
565
607
// ! by makeThread. If a client call is being made from a thread currently
566
608
// ! handling a server request, this will be set to the `callbackThread`
567
609
// ! request thread argument passed in that request.
610
+ // !
611
+ // ! Synchronization note: \ref callback_threads note applies here as well.
568
612
ConnThreads request_threads MP_GUARDED_BY (waiter->m_mutex);
569
613
570
614
// ! Whether this thread is a capnp event loop thread. Not really used except
0 commit comments