Non sono a conoscenza del fatto che POSIX abbia persino osato parlare di questo argomento, ma non ho fatto una ricerca esauriente.
Alcune brevi sperimentazioni con un sistema gcc/nptl rivelano che, come sospettavo e penso che lo faceste anche voi, non esiste una tale protezione in NPTL - i gestori di cancellazione vengono effettivamente chiamati, dal contesto del gestore di segnali.
Il seguente programma (scuse per la hackiness etc) visualizza il seguente output:
Signal handler called
Sent cancellation
Cleanup called
In sighandler
... indicano che:
- il gestore di segnale stato chiamato
- l'altro filo poi chiamato
pthread_cancel()
- il gestore di annullamento è stato chiamato, senza il gestore del segnale che completa
Ecco il programma:
#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 di qualsiasi risposta migliore in arrivo presto, io probabilmente accettare la vostra. Per quanto ne so, questo significa semplicemente che non puoi usare la cancellazione in un programma a meno che i tuoi gestori di segnale disabilitino la cancellazione o evitino di chiamare qualsiasi funzione che potrebbe essere un punto di cancellazione. Questo a sua volta, sfortunatamente, significa che il codice della libreria che utilizza i thread senza la consapevolezza/cooperazione del programma chiamante non può usare affatto la cancellazione (perché il programma chiamante potrebbe avere gestori di segnale di installazione); qualsiasi utilizzo potrebbe comportare condizioni di gara in cui il gestore del segnale viene cancellato. –
Sì, è vero, anche se in senso stretto il gestore del segnale non può disabilitare la cancellazione (le funzioni pthread non sono sicure per il segnale asincrono). La libreria * potrebbe * bloccare * tutti * i segnali in qualsiasi thread che crea, ma non è l'ideale (specialmente dal momento che bloccare SIGRTMIN su Linux disabilita la cancellazione ...). Ho sempre pensato che la cancellazione fosse pericolosa - non è possibile chiamare alcuna funzione di libreria da una discussione cancellabile a meno che non si sia certi che la libreria è stata progettata pensando alla cancellazione (altrimenti potrebbe allocare risorse, chiamare una funzione di cancellazione, quindi mai liberare quelle risorse ...) – psmears
Per quello che vale ho appena provato lo stesso programma su una macchina con Solaris 10, con gli stessi risultati ... Suppongo che questo significhi che, anche se ci sarà qualcosa in lo standard che rende questo sicuro, è inutile dal momento che le implementazioni più comuni non lo supportano: -/ – psmears