213

In errno.h, this variable is declared as extern int errno; so my question is, is it safe to check errno value after some calls or use perror() in multi-threaded code. Is this a thread safe variable? If not, then whats the alternative ?

I am using linux with gcc on x86 architecture.

unwind
  • 391,730
  • 64
  • 469
  • 606
Vinit Dhatrak
  • 6,814
  • 8
  • 27
  • 30
  • 1
    Possible duplicate of [Is there a way to use errno safely in a multi-threaded application?](https://stackoverflow.com/q/449778/608639) – jww Jun 16 '18 at 15:00

8 Answers8

210

Yes, it is thread safe. On Linux, the global errno variable is thread-specific. POSIX requires that errno be threadsafe.

See http://www.unix.org/whitepapers/reentrant.html

In POSIX.1, errno is defined as an external global variable. But this definition is unacceptable in a multithreaded environment, because its use can result in nondeterministic results. The problem is that two or more threads can encounter errors, all causing the same errno to be set. Under these circumstances, a thread might end up checking errno after it has already been updated by another thread.

To circumvent the resulting nondeterminism, POSIX.1c redefines errno as a service that can access the per-thread error number as follows (ISO/IEC 9945:1-1996, §2.4):

Some functions may provide the error number in a variable accessed through the symbol errno. The symbol errno is defined by including the header , as specified by the C Standard ... For each thread of a process, the value of errno shall not be affected by function calls or assignments to errno by other threads.

Also see http://linux.die.net/man/3/errno

errno is thread-local; setting it in one thread does not affect its value in any other thread.

Charles Salvia
  • 52,325
  • 13
  • 128
  • 140
  • 14
    Really? When did they do that? When I was doing C programming, trusting errno was a big problem. – Paul Tomblin Nov 07 '09 at 19:43
  • ok but in errno.h file, its still simple extern int variable. I am confused. – Vinit Dhatrak Nov 07 '09 at 19:49
  • 5
    @vinit: errno is actually defined in bits/errno.h. Read the comments in the include file. It says: "Declare the `errno' variable, unless it's defined as a macro by bits/errno.h. This is the case in GNU, where it is a per-thread variable. This redeclaration using the macro still works, but it will be a function declaration without a prototype and may trigger a -Wstrict-prototypes warning." – Charles Salvia Nov 07 '09 at 19:53
  • Cool, so how can I declare that macro instead of a variable ? – Vinit Dhatrak Nov 07 '09 at 19:55
  • 1
    @vinit, it's already declared for you. You don't need to do anything. The `extern int errno` variable declaration is wrapped in an `#ifndef errno` condition, which will be false because errno is already defined as a macro in bits/errno.h. – Charles Salvia Nov 07 '09 at 19:57
  • I mean is it some compile time option or I will have to declare it in my source code? – Vinit Dhatrak Nov 07 '09 at 19:57
  • 2
    If you are using Linux 2.6, you don't need to do anything. Just start programming. :-) – Charles Salvia Nov 07 '09 at 19:59
  • 2
    hmmm .. but errno macro is defined only if `defined _LIBC_REENTRANT` is true. So I guess, when we compile code with `lpthread`, only then this macro is getting enabled. Correct me if I am wrong here. – Vinit Dhatrak Nov 07 '09 at 20:08
  • 4
    @vinit dhatrak There should be `# if !defined _LIBC || defined _LIBC_REENTRANT` , _LIBC is not defined when compiling normal programs. Anyway, run echo `#include ' | gcc -E -dM -xc - ` and look at the difference with and without -pthread. errno is `#define errno (*__errno_location ())` in both cases. – nos Nov 28 '11 at 15:47
  • 2
    Consider adding an extract from the C11 standard (not earlier), `7.5 Errors `: It's guaranteed thread-safe there too. – Deduplicator Jul 23 '14 at 00:08
  • How to change this to keep errno global shared for all threads. Is it possible. If I like to do changes in kernel do implement system call and dig little bit deeper. May be keep something like errno can u please tell? Is it result from some interrupt in linux – user786 Aug 02 '21 at 08:32
  • I like mutex on shared errno or semaphore – user786 Aug 02 '21 at 08:34
64

Yes


Errno isn't a simple variable anymore, it's something complex behind the scenes, specifically for it to be thread-safe.

See $ man 3 errno:

ERRNO(3)                   Linux Programmer’s Manual                  ERRNO(3)

NAME
       errno - number of last error

SYNOPSIS
       #include <errno.h>

DESCRIPTION

      ...
       errno is defined by the ISO C standard to be  a  modifiable  lvalue  of
       type  int,  and  must not be explicitly declared; errno may be a macro.
       errno is thread-local; setting it in one thread  does  not  affect  its
       value in any other thread.

We can double-check:

$ cat > test.c
#include <errno.h>
f() { g(errno); }
$ cc -E test.c | grep ^f
f() { g((*__errno_location ())); }
$ 
DigitalRoss
  • 143,651
  • 25
  • 248
  • 329
16

yes, as it is explained in the errno man page and the other replies, errno is a thread local variable.

However, there is a silly detail which could be easily forgotten. Programs should save and restore the errno on any signal handler executing a system call. This is because the signal will be handled by one of the process threads which could overwrite its value.

Therefore, the signal handlers should save and restore errno. Something like:

void sig_alarm(int signo)
{
 int errno_save;

 errno_save = errno;

 //whatever with a system call

 errno = errno_save;
}
marcmagransdeabril
  • 1,445
  • 16
  • 27
  • forbidden→forgotten I presume. Can you provide a reference for this system call save/restore detail? – Craig McQueen Jul 24 '13 at 00:49
  • Hi Craig, thanks for the info about the typo, now is corrected. Regarding the other issue, I am not sure if I understand correctly what you are asking for. Whatever call that modifies errno in the signal handler could interfere with the errno being used by the same thread that was interrupted (e.g. using strtol inside sig_alarm). right? – marcmagransdeabril Jul 24 '13 at 13:12
15

In errno.h, this variable is declared as extern int errno;

Here is what the C standard says:

The macro errno need not be the identifier of an object. It might expand to a modifiable lvalue resulting from a function call (for example, *errno()).

Generally, errno is a macro which calls a function returning the address of the error number for the current thread, then dereferences it.

Here is what I have on Linux, in /usr/include/bits/errno.h:

/* Function to get address of global `errno' variable.  */
extern int *__errno_location (void) __THROW __attribute__ ((__const__));

#  if !defined _LIBC || defined _LIBC_REENTRANT
/* When using threads, errno is a per-thread value.  */
#   define errno (*__errno_location ())
#  endif

In the end, it generates this kind of code:

> cat essai.c
#include <errno.h>

int
main(void)
{
    errno = 0;

    return 0;
}
> gcc -c -Wall -Wextra -pedantic essai.c
> objdump -d -M intel essai.o

essai.o:     file format elf32-i386


Disassembly of section .text:

00000000 <main>:
   0: 55                    push   ebp
   1: 89 e5                 mov    ebp,esp
   3: 83 e4 f0              and    esp,0xfffffff0
   6: e8 fc ff ff ff        call   7 <main+0x7>  ; get address of errno in EAX
   b: c7 00 00 00 00 00     mov    DWORD PTR [eax],0x0  ; store 0 in errno
  11: b8 00 00 00 00        mov    eax,0x0
  16: 89 ec                 mov    esp,ebp
  18: 5d                    pop    ebp
  19: c3                    ret
Bastien Léonard
  • 60,478
  • 20
  • 78
  • 95
14

This is from <sys/errno.h> on my Mac:

#include <sys/cdefs.h>
__BEGIN_DECLS
extern int * __error(void);
#define errno (*__error())
__END_DECLS

So errno is now a function __error(). The function is implemented so as to be thread-safe.

Jonathan Leffler
  • 730,956
  • 141
  • 904
  • 1,278
vy32
  • 28,461
  • 37
  • 122
  • 246
14

We can check by running a simple program on a machine.

#include <stdio.h>                                                                                                                                             
#include <pthread.h>                                                                                                                                           
#include <errno.h>                                                                                                                                             
#define NTHREADS 5                                                                                                                                             
void *thread_function(void *);                                                                                                                                 

int                                                                                                                                                            
main()                                                                                                                                                         
{                                                                                                                                                              
   pthread_t thread_id[NTHREADS];                                                                                                                              
   int i, j;                                                                                                                                                   

   for(i=0; i < NTHREADS; i++)                                                                                                                                 
   {
      pthread_create( &thread_id[i], NULL, thread_function, NULL );                                                                                            
   }                                                                                                                                                           

   for(j=0; j < NTHREADS; j++)                                                                                                                                 
   {                                                                                                                                                           
      pthread_join( thread_id[j], NULL);                                                                                                                       
   }                                                                                                                                                           
   return 0;                                                                                                                                                   
}                                                                                                                                                              

void *thread_function(void *dummyPtr)                                                                                                                          
{                                                                                                                                                              
   printf("Thread number %ld addr(errno):%p\n", pthread_self(), &errno);                                                                                       
}

Running this program and you can see different addresses for errno in each thread. The output of a run on my machine looked like:-

Thread number 140672336922368 addr(errno):0x7ff0d4ac0698                                                                                                       
Thread number 140672345315072 addr(errno):0x7ff0d52c1698                                                                                                       
Thread number 140672328529664 addr(errno):0x7ff0d42bf698                                                                                                       
Thread number 140672320136960 addr(errno):0x7ff0d3abe698                                                                                                       
Thread number 140672311744256 addr(errno):0x7ff0d32bd698 

Notice that address is different for all threads.

Ky -
  • 30,724
  • 51
  • 192
  • 308
Rajat Paliwal
  • 634
  • 7
  • 11
  • 1
    Although looking it up in a man page (or on SO) is faster, I like you've made time to verify it. +1. – Bayou Mar 12 '20 at 17:43
10

On many Unix systems, compiling with -D_REENTRANT ensures that errno is thread-safe.

For example:

#if defined(_REENTRANT) || _POSIX_C_SOURCE - 0 >= 199506L
extern int *___errno();
#define errno (*(___errno()))
#else
extern int errno;
/* ANSI C++ requires that errno be a macro */
#if __cplusplus >= 199711L
#define errno errno
#endif
#endif  /* defined(_REENTRANT) */
Jonathan Leffler
  • 730,956
  • 141
  • 904
  • 1,278
  • 1
    I guess you dont have to compile code explicitly with `-D_REENTRANT`. Please refer to discussion on other answer for same question. – Vinit Dhatrak Nov 07 '09 at 20:12
  • 4
    @Vinit: it depends on your platform - on Linux, you may be correct; on Solaris, you would be correct only if you have set _POSIX_C_SOURCE to 199506 or a later version - probably by using `-D_XOPEN_SOURCE=500` or `-D_XOPEN_SOURCE=600`. Not everyone bothers to ensure that the POSIX environment is specified - and then `-D_REENTRANT` can save your bacon. But you still have to be careful - on each platform - to ensure you get the desired behaviour. – Jonathan Leffler Nov 07 '09 at 20:44
  • Is there a piece of documentation that indicates what standard (ie: C99, ANSI, etc) or at least which compilers (ie: GCC version and onward) that support this feature, and whether or not it is a default? Thank you. – Cloud Aug 25 '14 at 15:41
  • You can look at the C11 standard, or at POSIX 2008 (2013) for [errno](http://pubs.opengroup.org/onlinepubs/9699919799/functions/errno.html). The C11 standard says: _... and `errno` which expands to a modifiable lvalue (201) that has type `int` and thread local storage duration, the value of which is set to a positive error number by several library functions. If a macro definition is suppressed in order to access an actual object, or a program defines an identifier with the name `errno`, the behavior is undefined._ [...continued...] – Jonathan Leffler Aug 25 '14 at 16:26
  • [...continuation...] Footnote 201 says: _The macro `errno` need not be the identifier of an object. It might expand to a modifiable lvalue resulting from a function call (for example, `*errno()`)._ The main text continues: _The value of errno in the initial thread is zero at program startup (the initial value of errno in other threads is an indeterminate value), but is never set to zero by any library function._ POSIX uses the C99 standard which did not recognize threads. [...also continued...] – Jonathan Leffler Aug 25 '14 at 16:30
  • [...and again...] So, the standards say nothing about needing `-D_REENTRANT`, but POSIX 2008 requires `#define _POSIX_C_SOURCE 200809L` before the first system `#include`. If you don't do that, or your system does not support POSIX 2008 but an older version, you have to adjust things accordingly. And, occasionally, you have to resort to per-platform tweaks to get the required result. – Jonathan Leffler Aug 25 '14 at 16:32
7

I think the answer is "it depends". Thread-safe C runtime libraries usually implement errno as a function call (macro expanding to a function) if you're building threaded code with the correct flags.

Timo Geusch
  • 24,095
  • 5
  • 52
  • 70