#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
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.)
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With