Rocco Errno e i suoi fratelli |
extern int errno;e si riferiva a una semplice variabile globale della libc, per l'appunto un int chiamato errno. Poi sono arrivati i thread, con lo standard POSIX 1003.1c (detto anche POSIX.1c, ma fa lo stesso), e con lui è arrivata anche la strerror_r() e, come no, anche errno ha avuto un fratello thread-safe. Come si può ben leggere nel manuale di errno (dopo POSIX.1c) ora errno è:
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.Quindi la nuova definizione di errno è ora in bits/errno.h (che viene incluso dal errno.h classico). Semplificando un po' (ho omesso alcuni dettagli per facilitare la lettura) il nuovo giro del fumo è:
nel header-file errno.h #include <bits/errno.h> /* 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. */ #ifndef errno extern int errno; #endif nel header-file bits/errno.h /* Function to get address of global `errno' variable. */ extern int *__errno_location (void); /* When using threads, errno is a per-thread value. */ #define errno (*__errno_location ())Quindi, in parole povere, ora errno non è più un int globale ma è "il contenuto di un indirizzo ritornato da una funzione globale". Ovviamente la variabile int a cui punta la funzione in oggetto è il nuovo errno locale di un thread (ossia: ogni thread ha il suo errno). Un esempio (mooolto semplificato) di come si potrebbe implementare la __errno_location() è il seguente:
// errno locale di un thread: è una variabile di tipo Thread-local storage (TLS) __thread int th_errno; int *__errno_location(void) { // ritorna l'indirizzo della variabile th_errno return &th_errno; }E, alla fine della fiera, nonostante i cambi descritti, sarà ancora possibile fare operazioni di questo tipo:
int my_errno = errno; // Ok, equivale a: int my_errno = (* __errno_location()); errno = EADDRINUSE; // Ok, equivale a: (* __errno_location()) = EADDRINUSE;perché, ovviamente, tutto è stato pensato per essere retro-compatibile, e quindi errno, nonostante ora sia una macro, deve ancora comportarsi come quando era un semplice int.
E, per finire in bellezza, non facciamoci mancare un piccolo estratto dello standard POSIX.1c, che puntualizza tutto quello detto fin'ora:
Redefinition of errno 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, n2.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 <errno.h>, 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. In addition, all POSIX.1c functions avoid using errno and, instead, return the error number directly as the function return value, with a return value of zero indicating that no error was detected. This strategy is, in fact, being followed on a POSIX-wide basis for all new functions.Notare che l'ultima frase (da In addition... in poi) ci spiega il perchè dell'esistenza della strerror_r() XSI-compliant descritta nel post precedente: visto? tutti i nodi vengono al pettine... E con questo possiamo considerare risolto anche il mistero dell'errno thread-safe. Missione compiuta!
Ciao, e al prossimo post!
Nessun commento:
Posta un commento