Newsgroup: it.comp.os.linux.development
Titolo: cancellazione di thread

ciao a tutti,
ho scritto un programmino semplice semplice (che poi vi metto) che
fa le seguenti cose:
1- crea un thread e aspetta che termini (questo thread esegue la
funzione threadControllo)
2-il thread, a sua volta, ne crea un altro e subito dopo ne chiede la
cancellazione. Il secondo thread (che esegue la funzione compito)
stampa a video 15 frasi, controllando ogni 5 stampe se c'è una
richiesta di cancellazione.
Quello che succede è la seguente cosa:
se dichiaro il secondo thread "sincronicamente
cancellabile" (impostazione di default) viene eseguita una stampa e
poi il thread viene cancellato.
se dichiaro il thread PTHREAD_CANCELASYNCHRONOUS o
PTHREAD_CANCEL_DISABLE, esso va dritto come un fuso ignorando la
richiesta di cancellazione.
Io, da quello che ho capito da testo (Advanced Linux Programming),
invece mi sarei aspettato che:
1- di default il thread avrebbe dovuto stampare 5 frasi e poi
terminare
2- impostando il thread come PTHREAD_CANCEL_ASYNCHRONOUS avrebbe
dovuto terminare al volo
3-impostando il thread come PTHREAD_CANCEL_DISABLE avrebbe dovuto
completare la serie di stampe
Sapreste dirmi dove sbaglio?
Spero di non essere stato troppo noioso...
Grazie in anticipo a tutti!
Ecco il codice:
#include <stdio.h>
#include <errno.h>
#include <pthread.h>
#include <string.h>
void* compito (void* param){
    int i = 1;
    //variabili locali
    for (; i < 16; i++){
        //pthread_setcancelstate(PTHREAD_CANCEL_ASYNCHRONOUS, NULL);
        printf("Thread scrive per la %d volta\n", i);
        sleep(1);
        if (i % 5 == 0){
            printf("Controllo richiesta terminazione\n");
            sleep(1);
            pthread_testcancel();
        }//end if*/
    }//end for
}//end compito
void* threadControllo (void* parametri){
    pthread_t T = 0;
    //variabili locali
    pthread_create(&T, NULL, &compito, NULL);
    printf("Thread di controllo lancia thread compito\n");
    sleep(1);
    pthread_cancel(T);
    printf("Richiesta di terminazione di T\n");
    pthread_join(T, NULL);
    printf("Thread di controllo ha recuperato thread compito\n");
}//end threadControllo
int main(int agrc, char* argv[]){
    pthread_t thread = 0;
    //variabili locali
    pthread_create(&thread, NULL, &threadControllo, NULL);
    sleep(1);
    pthread_join(thread, NULL);
    printf("Programma principale termina\n");
    return 0;
}//end main

 richiesta di cancellazione.
questa chiamata e' sbagliata:
pthread_setcancelstate(PTHREAD_CANCEL_ASYNCHRONOUS, NULL);
deve essere:
pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS, NULL);
dando un'occhiata a pthread.h sembra che PTHREAD_CANCEL_ASYNCHRONOUS e
PTHREAD_CANCEL_DISABLE assumano incidentalmente lo stesso valore.
Quindi tu in realta' stavi chiamando:
pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, NULL);
il che spiega perche' il thread non si arrestava. Comunque la
cancellazione asincrona va evitata come la peste.
Saluti,
The Librarian


Scusa, ma ancora non ho avuto tempo di provare la modifica che mi hai
consigliato.
Ho letto la documentazione di pthread_setcancelstate e
pthread_setcanceltype e non ho ben capito quale sia la differenza.
Cosa cambia tra stato e tipo di un thread?

 Cosa cambia tra stato e tipo di un thread?
In questo caso i termini "stato" e "tipo" si riferiscono alla
cancellazione del thread, non al thread stesso. Lo "stato" indica se
la cancellazione e' abilitata o meno. Il "tipo" indica se la
cancellazione e' asincrona o differita.
Saluti,
The Librarian

Ciao a tutti, ho un problema simile a quello qui illustrato, e così
leggendo questo post ho copiato il codice sopra, modificandolo come è
stato consigliato.
Purtroppo, complando il codice sia usando la flag
PTHREAD_CANCEL_ASYNCHRONOUS che la flag PTHREAD_CANCEL_DEFERRED con la
funzione pthread_setcanceltype() ottengo lo stesso identico risultato,
ovvero che il thread che esegue "compito" viene terminato
immediatamente anche quando dovrebbe essere interrotto in modo
sincrono.
Anche non utilizzando la pthread_setcanceltype() ottengo lo stesso
risultato (di default il thread viene creato sincrono)...e questo è
proprio lo stesso problema che ho sul mio codice!
Se può aiutare, utilizzo g++ su debian Etch.
Grazie a tutti in anticipo!
ciao Mauro

 proprio lo stesso problema che ho sul mio codice!
Mi dispiace di non averlo fatto notare gia' in risposta al primo post,
ma confesso di averlo letto molto di fretta.
Quando si cancella un thread in modo differito, questo continua a
girare fino a che non incontra un "cancellation point".
La funzione pthread_testcancel() serve per creare un cancellation
point, ma non bisogna dimenticare che anche le funzioni seguenti sono
considerate cancellation point (prese dall guida
http://www.mkssoftware.com/docs/man3/pthread_cancel.3.asp#Cancellation_Points
):
- pthread_cond_wait(), pthread_cond_timedwait(), pthread_join(), and
sigwait()
- close(), creat(), fcntl(), fsync(), lockf(), nanosleep(), open(),
pause(), pthread_cond_timedwait(), pthread_cond_wait(),
pthread_join(), pthread_testcancel(), read(), readv(), select(),
sigpause(), sigsuspend(), sigwait(), sleep(), system(), tcdrain(),
usleep(), wait(), waitpid(), write(), writev()
Inoltre ci sono altre funzioni che possono essere potenzialmente dei
cancellation point (non le cito qui per brevita').
Comunque come vedi sleep e' un cancellation point ed e' quindi lecito
che il thread venga cancellato in quella chiamata e non
successivamente quando viene chiamata pthread_testcancel().
Saluti,
The Librarian

 proprio lo stesso problema che ho sul mio codice!
Mi dispiace di non averlo fatto notare gia' in risposta al primo post,


Perfetto, grazie mille!
ciao Mauro

Già che ci sono, avrei un'altra domanda: nel mio codice, il programma
(in caso di cancellazione differita) si blocca sulla join tra il
thread principale e quello creato.
In pratica, il valore ritornato della pthread_cancel chiamata dal main
è 0 (quindi la segnalazione è avvenuta correttamente) ma da lì in poi
il thread main rimane bloccato.
Il thread creato dal main invece credo esegua la cancellazione al
primo cancellation point (dall'analisi delle funzioni che lo sono, ce
ne sono molti).
Il processo in sè viene visualizzato dal ps come sleep, quindi rimane
"bloccato" proprio sulla join (o sul comando immediatamente precedete,
una printf).
E' possibile che ciò accada perché il thread creato dal main, al
momento della cancellazione, sta utilizzando delle risorse condivise
con il thread principale e attende che tali risorse vengano liberate,
mentre quello main attende sulla pthread_join?è una spiegazione che mi
sono dato, ma potrebbe essere anche una boiata pazzeesca! :o)
grazie per la tua disponibilità!
ciao mauro

 The Librarian
Grazie milee sei stato chiarissimo

 mentre quello main attende sulla pthread_join?
Non ho ben capito se hai usato esattamente il codice riportato dall'OP
nel primo post o se hai apportato delle modifiche, quindi non ti so
dare un parere in merito. Non credo che il codice originale si possa
bloccare per un cancel del thread "compito" (anche se potrei
sbagliarmi :P).
Comunque il mio consiglio spassionato e' di lasciare perdere la
cancellazione dei thread perche' significa solo problemi. Gia' un
programma multi-thread e' altamente non-deterministico, inoltre
cancellare un thread senza sapere esattamente dove si e' fermato vuol
dire andare a cercare guai. Probabilmente una persona molto esperta in
alcuni casi (e.g. background threads) puo' usare la cancellazione
(sempre differita) con disinvoltura, ma comunque si puo' sempre
risolvere il problema in modo piu' sicuro. Basta mantenere una
variabile booleana che viene letta dal thread in dei punti noti (molto
meglio se in un solo punto) e se la variabile e' true allora il thread
chiama exit. Il thread principale setta questa variabile a true quando
vuole canellare il thread.
Saluti,
The Librarian

 dare un parere in merito.
Scusa, mi sono spiegato male :) :non sto usando quel codice! :) mi
riferivo al mio codice, un programma che funziona alla stessa maniera
del codice riportato da Garet che mi da problemi di deadlock sulla
join! Nel mio caso però il thread "compito" è molto complesso
(utilizza classi che fanno molto uso di files e librerie (gsl
soprattutto)), e per questo pensavo (e chiedevo ad un esperto come te)
che il deadlock potesse essere dato dall'uso condiviso di alcune di
queste risorse.
Il problema l'ho comunque risolto "proteggendo" da cancellazioni la
parte di codice che esegue il lavoro (dove ho problemi di possibili
cancellation point), impostando il thread come non cancellabile (anche
se magari ciò non è proprio "pulito", concordo con te sul fatto che
l'uso di una variabile booleana protetta da mutex sarebbe meglio, ma
credo che così ottenga il medesimo effetto).
Infatti il comportamento desiderato è che la cancellazione sia
effettuata alla fine di ogni "ciclo" di lavoro, prorpio là dove chiamo
la testcancel.
Pensi che la spiegazione che mi sono dato e la soluzione trovata
possano essere corrette?
grazie ancora, ciao ciao!
mauro