Las funciones reentrantes no dependen de las variables globales que están expuestas en los encabezados de la biblioteca de C ... tome strtok () vs strtok_r () por ejemplo en C.
Algunas funciones necesitan un lugar para almacenar un 'trabajo en progreso', las funciones reentrantes le permiten especificar este puntero dentro del propio almacenamiento del hilo, no en un global. Dado que este almacenamiento es exclusivo de la función de llamada, se puede interrumpir y volver a ingresar (reentrante) y dado que en la mayoría de los casos no se requiere la exclusión mutua más allá de lo que implementa la función para que esto funcione, a menudo se considera que son hilo seguro . Sin embargo, esto no está garantizado por definición.
errno, sin embargo, es un caso ligeramente diferente en los sistemas POSIX (y tiende a ser el bicho raro en cualquier explicación de cómo funciona todo esto) :)
En resumen, reentrante a menudo significa seguro para subprocesos (como en "use la versión reentrante de esa función si está usando subprocesos"), pero seguro para subprocesos no siempre significa reentrante (o al revés). Cuando busca seguridad en subprocesos, la concurrencia es lo que debe pensar. Si tiene que proporcionar un medio de bloqueo y exclusión mutua para usar una función, entonces la función no es inherentemente segura para subprocesos.
Pero no es necesario examinar todas las funciones. malloc()
no tiene necesidad de ser reentrante, no depende de nada fuera del alcance del punto de entrada para un hilo dado (y es en sí mismo seguro para hilos).
Las funciones que devuelven valores asignados estáticamente no son seguras para subprocesos sin el uso de un mutex, futex u otro mecanismo de bloqueo atómico. Sin embargo, no necesitan volver a entrar si no van a ser interrumpidos.
es decir:
static char *foo(unsigned int flags)
{
static char ret[2] = { 0 };
if (flags & FOO_BAR)
ret[0] = 'c';
else if (flags & BAR_FOO)
ret[0] = 'd';
else
ret[0] = 'e';
ret[1] = 'A';
return ret;
}
Entonces, como puede ver, tener múltiples subprocesos usando eso sin algún tipo de bloqueo sería un desastre ... pero no tiene ningún propósito volver a entrar. Te encontrarás con eso cuando la memoria asignada dinámicamente sea tabú en alguna plataforma integrada.
En la programación puramente funcional, reentrante a menudo no implica que sea seguro para subprocesos, dependería del comportamiento de funciones definidas o anónimas pasadas al punto de entrada de la función, recursividad, etc.
Una mejor manera de poner 'seguro para subprocesos' es segura para el acceso concurrente , lo que ilustra mejor la necesidad.
TL; DR: una función puede ser reentrante, segura para subprocesos, ambas o ninguna.
Vale la pena leer los artículos de Wikipedia sobre seguridad de subprocesos y reentrada . Aquí hay algunas citas:
Una función es segura para subprocesos si:
Una función es reentrante si:
Como ejemplos de posible reentrada, Wikipedia da el ejemplo de una función diseñada para ser llamada por interrupciones del sistema: supongamos que ya se está ejecutando cuando ocurre otra interrupción. Pero no crea que está seguro solo porque no codifica con interrupciones del sistema: puede tener problemas de reentrada en un programa de un solo subproceso si usa devoluciones de llamada o funciones recursivas.
Ejemplos
(Ligeramente modificado de los artículos de Wikipedia)
Ejemplo 1: no seguro para subprocesos, no reentrante
/* As this function uses a non-const global variable without any precaution, it is neither reentrant nor thread-safe. */ int t; void swap(int *x, int *y) { t = *x; *x = *y; *y = t; }
Ejemplo 2: seguro para subprocesos, no reentrante
/* We use a thread local variable: the function is now thread-safe but still not reentrant (within the same thread). */ __thread int t; void swap(int *x, int *y) { t = *x; *x = *y; *y = t; }
Example 3: not thread-safe, reentrant
/* We save the global state in a local variable and we restore it at the end of the function. The function is now reentrant but it is not thread safe. */ int t; void swap(int *x, int *y) { int s; s = t; t = *x; *x = *y; *y = t; t = s; }
Example 4: thread-safe, reentrant
/* We use a local variable: the function is now thread-safe and reentrant, we have ascended to higher plane of existence. */ void swap(int *x, int *y) { int t; t = *x; *x = *y; *y = t; }
fuente
t = *x
, callsswap()
, thent
will be overridden, leading to unexpected results.swap(5, 6)
being interrupted by aswap(1, 2)
. Aftert=*x
,s=t_original
andt=5
. Now, after the interruption,s=5
andt=1
. However, before the secondswap
returns it will restore context, makingt=s=5
. Now, we go back to the firstswap
witht=5 and s=t_original
and continue aftert=*x
. So, the function does appear to be re-entrant. Remember that every call gets its own copy ofs
allocated on stack.It depends on the definition. For example Qt uses the following:
but they also caution:
fuente