Right now, it's tricky from reading the code and comments to figure out exactly what the semantics of the Client Lock are (when does it need to held? what exactly does it protect? etc). After digging into this I arrived at an understanding that:
1) The thread bound to a client can always read that client's data members/internals without taking the lock
2) If the thread bound to a client wants to write to data members/internals that might be read by other threads first, it must take the lock first
3) Other threads (i.e. those unbound to the client) that are reading the client's internals must take the lock before doing so, both to keep those members synchronized and to protect the Client from being destroyed mid-read
This leaves open the further question: what data members of the client exactly can be read by un-bound threads, and therefore need to be protected by the client lock? Specifically, are there any such data other than the contents of CurOp – historically, only data in the Client's CurOp decoration was legally accessable from other threads, but it's unclear if this is still true.
We should try and answer this open question, and clarify what we do know at least with a comment in the code. For example, this comment https://github.com/mongodb/mongo/blob/9cf1509ad2664830d37a7a128d43752dcac8556a/src/mongo/db/client.h#L143 describing the Client lock appears to be out of date and is confusing – we should replace it I think with what we've settled on above at least.
We could also use this ticket to investigate opportunities to use not just comments but code (especially the type system) to see if we could make it clear what could be read by unbound threads and what cannot (and therefore just when the client lock needs to be held)