Internally IPRT keeps track of threads by means of the RTTHREADINT structure. All the RTTHREADINT structures are kept in a AVL tree which is protected by a read/write lock for efficient access. A thread is inserted into the tree in three places in the code. The main thread is 'adopted' by IPRT on RTR3Init() by rtThreadAdopt(). When creating a new thread there the child and the parent race inserting the thread, this is rtThreadMain() and RTThreadCreate.
RTTHREADINT objects are using reference counting as a mean of sticking around till no-one needs them any longer. Waitable threads is created with one extra reference so they won't go away until they are waited on. This introduces a major problem if we use the host thread identifier as key in the AVL tree - the host may reuse the thread identifier before the thread was waited on. So, on most platforms we are using the RTTHREADINT pointer as key and not the thread id. RTThreadSelf() then have to be implemented using a pointer stored in thread local storage (TLS).
In Ring-0 we only try keep track of kernel threads created by RTThreadCreate at the moment. There we really only need the 'join' feature, but doing things the same way allow us to name threads and similar stuff.