Consider the following trivial C program,
#include <errno.h>
int
main(int argc, char* argv[]) {
return errno;
}
When compiled on Solaris, the behavior of this code is dependent on the presence of -D_REENTRANT
.
solaris$ cc -E test.c | grep return
return errno;
solaris$ cc -D_REENTRANT -E test.c | grep return
return ( * ( ___errno ( ) ) );
with the latter version being thread-safe. If we compile the same code on Linux, we get the same behavior independent of -D_REENTRANT
linux$ gcc -E test.c | grep return
return (*__errno_location ());
linux$ gcc -D_REENTRANT -E test.c | grep return
return (*__errno_location ());
Solaris' cc
has the option -mt
, which implies -D_REENTRANT
, as does gcc
's -pthread
. However, for a library, specifying these multi-threaded options seems poor, as it injects an unnecessary dependency on the threading runtime. However, if the library needs to be thread-safe (including errno), then the thread-safe semantics are needed at both compile time of the library and of the deriving code. On Linux, this is easy, because errno is always thread-local, but that's not guaranteed on other systems as just demonstrated.
That results in the question: how is a thread-safe library properly compiled and distributed with headers? One option would be to #define _REENTRANT
in the main header, but this would cause issues if #include <errno.h>
occurs before the library header inclusion. Another option is to compile the library with -D_REENTRANT
, and have the main header #error
if _REENTRANT
is not defined.
What's the correct/best way to make a thread-safe library and ensure it correctly inter-operates with the code it is linked with?
Software libraries can provide certain thread-safety guarantees. For example, concurrent reads might be guaranteed to be thread-safe, but concurrent writes might not be. Whether a program using such a library is thread-safe depends on whether it uses the library in a manner consistent with those guarantees.
The standard C printf() and scanf() functions use stdio so they are thread-safe. The standard C printf() function is susceptible to changes in the locale settings if called in a multithreaded program.
I don't have access to any Solaris machine at the moment, so I can't test this. But what happens when you put #define _POSIX_C_SOURCE 200112L
as the very first line in test.c
(before inclusion of <errno.h>
)? If your Solaris is POSIX-compliant, then that should make errno
expand to the thread-safe version. This is because POSIX defines errno
as below:
For each thread of a process, the value of errno shall not be affected by function calls or assignments to errno by other threads.
Accordingly, this is portable to any POSIX-compliant system. In fact, if you want to write POSIX-compliant application code, then you should always define _POSIX_C_SOURCE
to the value appropriate for the minimum version of POSIX you are targeting. The definition should be at the top of every source file before including any headers. From the 2001 version of the standard:
A Strictly Conforming POSIX Application is an application that requires only the facilities described in IEEE Std 1003.1-2001. Such an application:
...
8. For the C programming language, shall define _POSIX_C_SOURCE to be 200112L before any header is included
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