Oui, il est thread-safe. Sous Linux, la variable globale errno est spécifique au thread. POSIX exige que errno soit thread-safe.
Voir http://www.unix.org/whitepapers/reentrant.html
Dans POSIX.1, errno est défini comme une variable globale externe. Mais cette définition est inacceptable dans un environnement multithread, car son utilisation peut entraîner des résultats non déterministes. Le problème est que deux threads ou plus peuvent rencontrer des erreurs, provoquant toutes la définition du même errno. Dans ces circonstances, un thread peut finir par vérifier errno après avoir déjà été mis à jour par un autre thread.
Pour contourner le non-déterminisme qui en résulte, POSIX.1c redéfinit errno en tant que service pouvant accéder au numéro d'erreur par thread comme suit (ISO/IEC 9945:1-1996, §2.4) :
Certaines fonctions peuvent fournir le numéro d'erreur dans une variable accessible via le symbole errno. Le symbole errno est défini en incluant l'en-tête , tel que spécifié par la norme C... Pour chaque thread d'un processus, la valeur de errno ne doit pas être affectée par les appels de fonction ou les affectations à errno par d'autres threads.
Voir aussi http://linux.die.net/man/3/errno
errno est local au thread ; le définir dans un thread n'affecte pas sa valeur dans un autre thread.
Oui
Errno n'est plus une simple variable, c'est quelque chose de complexe dans les coulisses, spécifiquement pour qu'elle soit thread-safe.
Voir $ 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.
Nous pouvons vérifier :
$ cat > test.c
#include <errno.h>
f() { g(errno); }
$ cc -E test.c | grep ^f
f() { g((*__errno_location ())); }
$
Dans errno.h, cette variable est déclarée comme extern in errno ;
Voici ce que dit la norme C :
La macro
errno
n'a pas besoin d'être l'identifiant d'un objet. Il peut s'étendre à une lvalue modifiable résultant d'un appel de fonction (par exemple,*errno()
).
Généralement, errno
est une macro qui appelle une fonction retournant l'adresse du numéro d'erreur du thread en cours, puis le déréférence.
Voici ce que j'ai sous Linux, dans /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
Au final, il génère ce genre de 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