No soy consciente de que POSIX incluso se atreve a mencionar este tema, pero no he hecho una búsqueda exhaustiva.
Algunos experimentos breves con un sistema gcc/nptl revelan que, como sospechaba y creo que tú también, no existe tal protección en NPTL: los controladores de cancelación son llamados desde el contexto del controlador de señal.
El programa siguiente (disculpas por la hackiness etc) muestra el resultado siguiente:
Signal handler called
Sent cancellation
Cleanup called
In sighandler
... que indican que:
- el manejador de la señal fue llamado
- el otro hilo a continuación, llamado
pthread_cancel()
- se llamó al controlador de cancelación, sin que el manejador de señal haya completado
Aquí está el programa:
#include <stdio.h>
#include <pthread.h>
#include <signal.h>
#include <string.h>
#include <unistd.h>
#include <assert.h>
pthread_t mainthread;
int in_sighandler = 0;
void
cleanup (void *arg)
{
write(1, "Cleanup called\n", strlen("Cleanup called\n"));
if (in_sighandler) {
write(1, "In sighandler\n", strlen("In sighandler\n"));
} else {
write(1, "Not in sighandler\n", strlen("In sighandler\n"));
}
}
void
sighandler (int sig, siginfo_t *siginfo, void *arg)
{
in_sighandler = 1;
write(1,"Signal handler called\n", strlen("Signal handler called\n")); // write() is a CP
usleep(3000000); // usleep() is a CP; not strictly async-signal-safe but happens to be so in Linux
write(1, "Signal handler exit\n", strlen("Signal handler exit\n"));
in_sighandler = 0;
}
void *
thread (void *arg)
{
sleep(1);
pthread_kill(mainthread, SIGUSR1);
usleep(500000);
pthread_cancel(mainthread);
printf("Sent cancellation\n");
return (NULL);
}
int
main (int argc, char **argv)
{
int rc;
struct sigaction sa;
pthread_t threadid;
mainthread = pthread_self();
// Set up a signal handler to test its cancellation properties
sa.sa_sigaction = &sighandler;
sigemptyset(&sa.sa_mask);
sa.sa_flags = SA_SIGINFO;
rc = sigaction(SIGUSR1, &sa, NULL);
assert(rc == 0);
// Set up a thread to send us signals and cancel us
rc = pthread_create(&threadid, NULL, &thread, NULL);
assert(rc == 0);
// Set up cleanup handlers and loop forever
pthread_cleanup_push(&cleanup, NULL);
while (1) {
sleep(60);
}
pthread_cleanup_pop(0);
return (0);
}
corto de cualquier respuesta mejor venir en breve, probablemente va a aceptar la suya. Por lo que puedo decir, esto básicamente significa que no puede usar la cancelación en un programa a menos que sus controladores de señal deshabiliten la cancelación ellos mismos o eviten llamar a cualquier función que pueda ser un punto de cancelación. Esto, a su vez, desafortunadamente significa que el código de la biblioteca que utiliza hilos sin la concientización/cooperación del programa llamante no puede usar la cancelación en absoluto (porque el programa llamante podría tener manejadores de señal de configuración); cualquier uso puede dar como resultado condiciones de carrera en las que el manejador de señales se cancela. –
Sí, eso es correcto, aunque estrictamente hablando, el manejador de señal no puede deshabilitar la cancelación (las funciones pthread no son seguras para señales asíncronas). La biblioteca * podría * bloquear * todas las señales * en cualquier conversación que cree, pero eso no es lo ideal (especialmente porque el bloqueo de SIGRTMIN en Linux desactiva la cancelación ...). Siempre pensé que la cancelación era peligrosa: no se puede llamar a ninguna función de la biblioteca desde un hilo cancelable a menos que esté seguro de que la biblioteca se diseñó teniendo en cuenta la cancelación (o podría asignar recursos, llamar a una función de cancelación de puntos, nunca liberar esos recursos ...) – psmears
Por lo que vale, acabo de probar el mismo programa en una máquina que ejecuta Solaris 10, con los mismos resultados ... Supongo que eso significa que, incluso si llega a haber algo en el estándar lo hace seguro, es inútil ya que las implementaciones más comunes no lo admiten: -/ – psmears