Rocco Errno y sus hermanos |
extern int errno;y hacia referencia a una simple variable global de la libc, exactamente un int llamado errno. Luego vinieron los thread, con el estándar POSIX 1003.1c (también conocido como POSIX.1c, pero hace lo mismo), y con él vino también la strerror_r() y, como no, también errno ha conseguido un hermano thread-safe. Como bien se puede leer en el manual de errno (después de POSIX.1c) ahora errno es:
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.Entonces la nueva definición de errno ahora está en bits/errno.h (que se incluye desde el clásico errno.h). Simplificando un poco (he omitido algunos detalles para que sea más fácil de leer) la nueva impostacion es:
en el 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 en el 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 ())Entonces, en pocas palabras, ahora errno ya no es un int global, sino que es "el contenido de una dirección devuelta por una función global". Obviamente, la variable int a la que apunta este objeto es el nuevo errno local de un thread (es decir, cada thread tiene su propio errno). Un ejemplo (muuuy simplificado) de cómo se podría implementar la __errno_location() es el siguiente:
// errno local de un thread: es una variable de tipo Thread-local storage (TLS) __thread int th_errno; int *__errno_location(void) { // retorna la dirección de la variable th_errno return &th_errno; }Y, al final de todo, a pesar de los cambios descritos, aún será posible hacer operaciones como estas:
int my_errno = errno; // Ok, equivale a: int my_errno = (* __errno_location()); errno = EADDRINUSE; // Ok, equivale a: (* __errno_location()) = EADDRINUSE;porque, por supuesto, todo ha sido diseñado para ser retro-compatible, y por lo tanto errno, aunque ahora es una macro, todavía tiene que comportarse como si fuera un simple int.
Y, para terminar a lo grande, no podemos renunciar a un pequeño extracto del estándar POSIX.1c, que señala todo lo que se ha dicho hasta ahora:
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.Notar que la ultima parte del extracto (de In addition... en adelante) explica el porqué existe la strerror_r() XSI-compliant descrita en el post anterior: ¿Habéis visto? todo se aclara al final... Y con esto también podemos considerar resuelto el misterio del errno thread-safe. ¡Misión cumplida!
¡Hasta el próximo post!