Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

pthread_self returns a large integer or 0, depending on whether libpthread is present or not

#include <pthread.h>
#include <iostream>
 int main(){
     std::cout<<pthread_self()<<std::endl;
}

When compiled with the command g++ 1.cpp -o 1 and executed, the programs output is 0, if compiled with the command g++ 1.cpp -o 1 -lpthread the output is a large number like 140062699310912.

I don't understand why.

# g++ 1.cpp -o 1 && ./1
0
# g++ 1.cpp -o 1 -lpthread && ./1
140268785461056
like image 746
yi bu Avatar asked Sep 20 '25 02:09

yi bu


1 Answers

The POSIX standard does not specify the actual type that pthread_t corresponds to. In glibc, it is unsigned long int, which is why C++ streams can print its value. (This is not portable.)

Why does pthread_self return zero, but only when not linking with -lpthread?

Historically, glibc supported two different thread library implementations, LinuxThreads and NPTL. Some of the core libpthread functions have stubs within glibc itself that forward to the actual implementation if any is loaded, or perform some dummy fallback operation if no thread library is present in the process. For example, the dummy implementation of pthread_mutex_lock and pthread_mutex_unlock do nothing.

In the case of pthread_self, the dummy implementation has to return some value, and this value is zero. (POSIX does not reserve any pthread_t values to indicate failures or other corner cases, so this is fine.) When linking with -lpthread, the non-dummy implementation is used, which returns a pointer to the internal thread data structure (converted to unsigned long), and this value is never zero, not even for the initial (main) thread. This is why the presence of -lpthread on the linker command line matters.

Others could not reproduce this behavior because they were either using non-glibc systems, or more recent versions of glibc. Nowadays, there is only one threading library implementation for glibc, the one that started out as NPTL, and it is tightly integrated with the rest of glibc. This means the forwarders are not necessary anymore, and in glibc 2.27, pthread_self never returns zero. Incidentally, this change helps with implementing std::thread, where the value zero returned by get_id(): std::thread::id{} means that there is no ID, not that the thread is the main thread (as in older glibc versions without libpthread loaded). libstdc++ therefore needs a kludge to support glibc 2.26 and older.

The forwarders had another benefit: They enable performance optimizations, and not just for pthread_mutex_lock (by doing nothing if libpthread is not linked in). Some libpthread functionality is available without -lpthread. This means that applications can use synchronization and they can check for the presence of symbols such as pthread_create or pthread_cancel (using a weak symbol reference). If these weak symbols are undefined, it is clear that the process must be single-threaded, as libpthread is absent, which means that no threads can be created. In this case, it is not necessary to use atomic instructions for reference counting, for example. (std::shared_ptr in libstdc++ uses such an optimization.) But this optimization is increasingly ineffective because most process images contain libpthread these days: some library requires it, so it is loaded as an indirect dependency. This means that the forwarders have lost this other purpose as well, and they are gradually being removed from glibc. (glibc 2.32 added __libc_single_threaded to enable the single-threaded optimization even if libpthread is loaded, but no threads have been created.)

like image 143
Florian Weimer Avatar answered Sep 21 '25 20:09

Florian Weimer