É errno thread-safe?

Em errno.h , esta variável é declarada como extern int errno; então minha pergunta é, é seguro verificar o valor de errno após algumas chamadas ou usar perror () no código multi-threaded. Esta é uma variável segura de thread? Se não, então qual é a alternativa?

Eu estou usando o linux com gcc na arquitetura x86.

Sim, é thread-safe. No Linux, a variável global errno é específica do segmento. POSIX requer que errno seja thread-safe.

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

No POSIX.1, errno é definido como uma variável global externa. Mas essa definição é inaceitável em um ambiente multithread, porque seu uso pode resultar em resultados não determinísticos. O problema é que dois ou mais encadeamentos podem encontrar erros, todos fazendo com que o mesmo errno seja configurado. Sob essas circunstâncias, um thread pode acabar verificando errno depois que ele já foi atualizado por outro thread.

Para contornar o não-determinismo resultante, POSIX.1c redefine errno como um serviço que pode acessar o número de erro por thread como segue (ISO / IEC 9945: 1-1996, §2.4):

Algumas funções podem fornecer o número do erro em uma variável acessada através do símbolo errno. O símbolo errno é definido incluindo o header, conforme especificado pelo Padrão C … Para cada encadeamento de um processo, o valor de errno não deve ser afetado pelas chamadas de function ou atribuições a errar por outros encadeamentos.

Veja também http://linux.die.net/man/3/errno

errno é thread-local; defini-lo em um segmento não afeta seu valor em qualquer outro segmento.

sim


Errno não é mais uma variável simples, é algo complexo nos bastidores, especificamente para ser seguro para threads.

Veja $ man 3 errno :

 ERRNO(3) Linux Programmer's Manual ERRNO(3) NAME errno - number of last error SYNOPSIS #include  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. 

Podemos verificar novamente:

 $ cat > test.c #include  f() { g(errno); } $ cc -E test.c | grep ^f f() { g((*__errno_location ())); } $ 

Em errno.h, esta variável é declarada como extern int errno;

Aqui está o que o padrão C diz:

A macro errno não precisa ser o identificador de um object. Ele pode se expandir para um lvalue modificável resultante de uma chamada de function (por exemplo, *errno() ).

Geralmente, errno é uma macro que chama uma function retornando o endereço do número do erro para o segmento atual e, em seguida, cancela a referência.

Aqui está o que eu tenho no Linux, em /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 

No final, gera esse tipo de código:

 > cat essai.c #include  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 
: 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
; 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

Em muitos sistemas Unix, compilar com -D_REENTRANT garante que errno seja thread-safe.

Por exemplo:

 #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) */ 

Isso é de no meu Mac:

 #include  __BEGIN_DECLS extern int * __error(void); #define errno (*__error()) __END_DECLS 

Então, errno é agora uma function __error() . A function é implementada para ser thread-safe.

Eu acho que a resposta é “depende”. As bibliotecas de tempo de execução C corretas geralmente implementam errno como uma chamada de function (macro expandindo para uma function) se você estiver construindo um código encadeado com os sinalizadores corretos.

sim , como é explicado na man page do errno e as outras respostas, errno é uma variável local do thread.

No entanto , há um detalhe tolo que pode ser facilmente esquecido. Os programas devem salvar e restaurar o erro em qualquer manipulador de sinal que esteja executando uma chamada do sistema. Isso ocorre porque o sinal será tratado por um dos encadeamentos do processo que pode replace seu valor.

Portanto, os manipuladores de sinal devem salvar e restaurar o errno. Algo como:

 void sig_alarm(int signo) { int errno_save; errno_save = errno; //whatever with a system call errno = errno_save; }