POSIX thread

In questo codice, il processo  dopo aver generato 3 threads, si sospende in un ciclo infinito in attesa del segnale SIGUSR1 al quale è agganciata func_ handler (). Tale funzione invia il segnale SIGUSR2 ai threads precedentemente generati. I  threads  agganciano   func_ handler _th() al segnale SIGUSR2 nella quale visualizzano la ricezione del segnale e ritornano in un ciclo infinito di attesa di un evento.


/*
   prova di creazione thread 
                             */
#include <pthread.h>
#include <signal.h>
#include <unistd.h>
#include <stdio.h>
void* func_thread();
void func_handler();
void func_handler_th();
int pidt[3];
main()
{
int i;
pthread_t tid;
sigset(SIGUSR1,func_handler);
for (i=0;i<3;i++)
  {
  pthread_create(&tid,NULL,func_thread,NULL);
  printf("processo: %d, generato thread: %d\n",getpid(),tid);
  pidt[i]=tid;
  sleep(2);
  }
printf("processo: %d sospeso\n",getpid());
for(;;);
}
void* func_thread()
{
sigset(SIGUSR2,func_handler_th);
fprintf(stdout,"thread: %d generato\n",pthread_self());
for (;;){fprintf(stdout,"thread: %d alive\n",pthread_self());sleep(10);}
}
void func_handler(sig)
int sig;
{
int i;
printf("thread: %d, ricevuto segnale: %d\n",pthread_self(),sig);
for (i=0;i<3;i++) 
  {printf("invio il segnale 17 al th %d\n",pidt[i]);
   pthread_kill(pidt[i],SIGUSR2);}
}
void func_handler_th(sig)
int sig;
{
int i;
printf("thread: %d, ricevuto segnale: %d\n",pthread_self(),sig);
}
 

Si noti nel main la sleep(2) all' interno del ciclo di generazione dei threads. Concettualmente non è necessaria ma nella pratica se la si omette il codice non funziona correttamente o forse è meglio dire che è il kernel a non comportarsi correttamente in quanto senza la sleep() la pthread_create() va a buon fine ma i threads non hanno modo di entrare in esecuzione : l' impressione è che lo scheduler si dimentichi di loro.

Altrettanto interessante è analizzare il comportamento dei threads nei confronti dei segnali. Questo esempio dimostra come l' uso dei threads combinato con quello dei segnali richieda particolare attenzione, se non addirittura sia il caso di evitarlo. 

In UNIX la gestione dei segnali è sempre riferita al contesto globale del processo nel quale vengono generati i threads, con la complicazione del fatto che lo stesso processo pesante è considerato a sua volta un thread: il thread 1.

Ciò impedisce la gestione di un segnale entro la località di un thread nel senso che non è possibile aggangiare un segnale per un thread specifico in quanto tale operazione fa riferimento al contesto globale del threads dunque il processo pesante nel quale i threads vengono generati,  conseguentemente una signal() riguarda tutti i threads generati. Va detto comunque che è possibile ignorare un segnale all' interno di una località. 

Vediamo comunque di valutare il comportamento dei threads nei confronti dei segnali analizzando il codice dell' esempio. La sigset(SIGUSR1,funct_handler) è eseguita all' interno del main dunque riguarda il processo, però questo non significa che tale handler vanga agganciato da tutti i threads generati dal processo. Il segnale SIGUSR1 non ha alcun side effect sui threads generati, per cui non c' è bisogno di mascherarlo entro la località del thread (probabilmente perchè generati dopo la sigset()). In base a questo comportamento si potrebbe pensare di utilizzare lo stesso segnale SIGUSR1 all' interno della località del thread,  facendo in modo che il processo, ricevuto il segnale SIGUSR1, a sua volta lo invii ai threads, ma la primitiva sigset(SIGUSR1,funct_handler_th) eseguita nella località del thread entra in conflitto con la precedente primitiva eseguita nel contesto del processo.

Attenzione anche al tipo  di primitiva utilizzata per la gestione dei segnali: considerando che tutti i threads eseguono la func_thread() si potrebbe pensare che l' uso della signal, piuttosto che la sigset, consenta a tutti i threads di eseguire la funct_handler_th() almeno una volta, per poi assumere il comportamento a default nei confronti di SIGUSR2 (azione abortiva). In realtà quello che succede è che già a partire dal secondo thread la gestione del segnale è a default.

Un' altra considerazione interessante può essere fatta in merito al fatto che  UNIX di SOLARIS considera il processo che genera i threads come il thread1, per cui in termini di programmazione nulla vieta di considerarlo alla stregua di un thread effettivo e ad esempio eliminarlo con la pthread_exit(). A tal proposito se si sostituisce il ciclo infinito compiuto dal processo all' interno del main col seguente codice:

                 sleep(20);
                 printf (" processo PESANTE: ora termino.\n");
                 pthread_exit(NULL);
 

finchè il processo non termina intercetta il SIGUSR1, ma scaduti i 20 secondi il processo termina ma non i threads da lui generati ed è il primo thread generato che si occupa della gestione del  SIGUSR1.