2013-05-07 12 views
5

Sono nello same situation as this guy, ma non capisco la risposta.Come si "disimpegna" da "accept" su un socket di blocco quando viene segnalato da un altro thread?

Il problema:

  • filettatura 1 chiamate accept su un socket, che blocca.
  • Thread 2 chiama close su questa presa.
  • Il thread 1 continua a bloccare. Voglio che ritorni da accettare.

La soluzione:

cosa si dovrebbe fare è inviare un segnale al thread che è bloccato in accettare. Ciò gli darà EINTR e può essere disinnestato in modo pulito - e quindi chiudere il socket. Non chiuderlo da un filo diverso da quello che lo utilizza.

Non capisco cosa fare qui - quando il segnale viene ricevuto in Thread 1, accept è già il blocco, e continuerà a bloccare dopo che il gestore di segnale è terminato.

  1. Che cosa significa veramente la risposta che dovrei fare?
  2. Se il gestore di segnale Thread 1 può eseguire un'operazione che causerà il ritorno immediato di accept, perché Thread 2 non può fare lo stesso senza i segnali?
  3. C'è un altro modo per farlo senza segnali? Non voglio aumentare le avvertenze sulla libreria.

risposta

2

La chiamata accept() viene restituita con il codice di errore EINTR se viene rilevato un segnale prima che venga accettata una connessione. Quindi controllare il valore restituito e il codice di errore, quindi chiudere il socket di conseguenza.

Se si desidera evitare del tutto il meccanismo del segnale, utilizzare select() per determinare se vi sono connessioni in entrata pronte per essere accettate prima di chiamare accept(). La chiamata select() può essere effettuata con un timeout in modo da poter recuperare e rispondere alle condizioni di interruzione.

Io di solito chiamo select() con un timeout da 1000 a 3000 millisecondi da un ciclo while che controlla una condizione di uscita/interruzione. Se select() restituisce con un descrittore pronto, chiamo accept() altrimenti posso effettuare un loop e bloccare nuovamente su select() o uscire se richiesto.

+0

Quindi se qualche segnale viene inviato al thread, restituisce? C'è un segnale non operativo per questo genere di cose? – spraff

+0

Credo di si. Il tipo di segnale effettivo influisce sull'azione del gestore, ma molte chiamate di sistema di blocco si interromperanno per darti l'opportunità di fare esattamente quello che stai cercando di fare. Imposta il tuo gestore di segnale per catturare un segnale specifico e inviarlo dal thread di controllo. Il tuo gestore non ha bisogno di fare nulla in particolare. SIGINFO potrebbe non essere una cattiva scelta. È abbastanza benigno. –

+0

Sembra contraddire il consiglio [qui] (http://stackoverflow.com/questions/14613409/are-stdsignal-and-stdraise-thread-safe) ... qualche idea? – spraff

5

Invece di bloccare in accept(), blocco select(), poll(), o una delle chiamate simili che permette di attendere per l'attività su più descrittori di file e utilizzare il "self-pipe trick". Tutti i descrittori di file passati a select() dovrebbero essere in modalità non bloccante. Uno dei descrittori di file dovrebbe essere il socket del server che si utilizza con accept(); se quello diventa leggibile allora dovresti andare avanti e chiamare accept() e non bloccherà. Oltre a quello, crea un pipe(), impostalo su non bloccante e verifica che il lato di lettura sia leggibile. Invece di chiamare close() sul socket del server nell'altro thread, inviare un byte di dati al primo thread sul lato di scrittura della pipe. Il valore byte attuale non ha importanza; lo scopo è semplicemente quello di svegliare il primo thread.Quando select() indica che la pipe è leggibile, read() e ignora i dati dalla pipe, close() il socket del server e smetti di attendere nuove connessioni.

+1

+1 ottimo suggerimento per usare un pipe per rilasciare select(). –

+0

Ho scoperto 'shutdown' (vedi la mia risposta). Grazie per questa idea, ma sembra inutilmente complicata e hacky. – spraff

+0

Beh, non penso che lo spegnimento non funzionerà su un socket che non è connesso. – thodg

0

Basta chiudere il socket di ascolto e gestire l'errore o l'eccezione risultante da accept().

+1

'accept()' in C/C++ non genera eccezioni. – mark4o

+0

@marko Giusto, ho generalizzato la risposta. – EJP

+0

@ mark4o Sul mio sistema Linux, 'select()' non ritorna se il socket è chiuso su un altro thread. –

0

Chiamare shutdown() dalla discussione 2. accept restituirà con "argomento non valido".

Questo sembra funzionare ma la documentazione non spiega realmente il suo funzionamento attraverso i thread - sembra funzionare, quindi se qualcuno può chiarirlo, lo accetterò come risposta.

+0

Intendi chiamare 'shutdown()' sulla presa di ascolto? – EJP

+0

POSIX richiede 'shutdown()' su un socket che non è connesso e la documentazione di Linux è d'accordo. Se si sta osservando un comportamento diverso, sarebbe imprudente affidarsi ad esso, poiché è probabile che vari su sistemi e forse versioni del kernel. – mark4o

0

Credo che i segnali possano essere utilizzati senza aumentare "gli avvertimenti sulla libreria". Si consideri il seguente:

#include <pthread.h> 
#include <signal.h> 
#include <stddef.h> 

static pthread_t    thread; 
static volatile sig_atomic_t sigCount; 

/** 
* Executes a concurrent task. Called by `pthread_create()`.. 
*/ 
static void* startTask(void* arg) 
{ 
    for (;;) { 
     // calls to `select()`, `accept()`, `read()`, etc. 
    } 
    return NULL; 
} 

/** 
* Starts concurrent task. Doesn't return until the task completes. 
*/ 
void start() 
{ 
    (void)pthread_create(&thread, NULL, startTask, NULL); 
    (void)pthread_join(thread); 
} 

static void noop(const int sig) 
{ 
    sigCount++; 
} 

/** 
* Stops concurrent task. Causes `start()` to return. 
*/ 
void stop() 
{ 
    struct sigaction oldAction; 
    struct sigaction newAction; 

    (void)sigemptyset(&newAction.sa_mask); 
    newAction.sa_flags = 0; 
    newAction.sa_handler = noop; 
    (void)sigaction(SIGTERM, &newAction, &oldAction); 

    (void)pthread_kill(thread, SIGTERM); // system calls return with EINTR 

    (void)sigaction(SIGTERM, &oldAction, NULL); // restores previous handling 

    if (sigCount > 1) // externally-generated SIGTERM was received 
     oldAction.sa_handler(SIGTERM); // call previous handler 

    sigCount = 0; 
} 

Questo ha i seguenti vantaggi:

  • Non richiede nulla di speciale nel codice compito diverso dalla normale movimentazione EINTR; di conseguenza, rende più semplice ragionare sulla perdita di risorse rispetto all'utilizzo di pthread_cancel(), pthread_cleanup_push(), pthread_cleanup_pop() e pthread_setcancelstate().
  • Non richiede alcuna risorsa aggiuntiva (ad esempio un tubo).
  • Può essere migliorato per supportare più attività simultanee.
  • E 'abbastanza standard.
  • Potrebbe anche essere compilato. :-)
Problemi correlati