Wake up thread bloqueado en accept() llamada
sockets pthreads (4)
Cierre el socket con la llamada shutdown()
. Esto activará los hilos bloqueados en él, manteniendo el descriptor de archivo válido.
close()
en un descriptor que otro hilo B está utilizando es intrínsecamente peligroso: otro hilo C puede abrir un nuevo descriptor de archivo que el hilo B utilizará en lugar del cerrado. dup2()
a /dev/null
en él evita ese problema, pero no activa los hilos bloqueados de manera confiable.
Tenga en cuenta que shutdown()
solo funciona en sockets; para otros tipos de descriptores es probable que necesite los enfoques select + pipe-to-self o de cancelación.
Sockets en Linux question
Tengo un hilo de trabajo que está bloqueado en una llamada accept (). Simplemente espera una conexión de red entrante, la maneja y luego vuelve a escuchar la siguiente conexión.
Cuando es hora de que el programa salga, ¿cómo señalo este hilo de trabajo de red (desde el hilo principal) para que regrese desde la llamada a accept () mientras aún puedo salir graciosamente de su ciclo y manejar su código de limpieza?
Algunas cosas que intenté:
pthread_kill para enviar una señal. Se siente demasiado optimista para hacer esto, además de que no permite de manera confiable que el hilo haga su lógica de apagado. También hace que el programa finalice también. Me gustaría evitar las señales si es posible.
pthread_cancel. Lo mismo que arriba. Es una muerte dura en el hilo. Eso, y el hilo puede estar haciendo otra cosa.
Cerrar el conector de escucha del hilo principal para hacer que accept () aborte. Esto no funciona de manera confiable.
Algunas restricciones:
Si la solución implica hacer que el socket de escucha no sea bloqueante, está bien. Pero no quiero aceptar una solución que implique el inicio de subprocesos a través de una llamada de selección cada pocos segundos para verificar la condición de salida.
La condición del hilo para salir puede no estar vinculada al proceso de salida.
Esencialmente, la lógica que estoy buscando se ve así.
void* WorkerThread(void* args)
{
DoSomeImportantInitialization(); // initialize listen socket and some thread specific stuff
while (HasExitConditionBeenSet()==false)
{
listensize = sizeof(listenaddr);
int sock = accept(listensocket, &listenaddr, &listensize);
// check if exit condition has been set using thread safe semantics
if (HasExitConditionBeenSet())
{
break;
}
if (sock < 0)
{
printf("accept returned %d (errno==%d)/n", sock, errno);
}
else
{
HandleNewNetworkCondition(sock, &listenaddr);
}
}
DoSomeImportantCleanup(); // close listen socket, close connections, cleanup etc..
return NULL;
}
void SignalHandler(int sig)
{
printf("Caught CTRL-C/n");
}
void NotifyWorkerThreadToExit(pthread_t thread_handle)
{
// signal thread to exit
}
int main()
{
void* ptr_ret= NULL;
pthread_t workerthread_handle = 0;
pthread_create(&workerthread, NULL, WorkerThread, NULL);
signal(SIGINT, SignalHandler);
sleep((unsigned int)-1); // sleep until the user hits ctrl-c
printf("Returned from sleep call.../n");
SetThreadExitCondition(); // sets global variable with barrier that worker thread checks on
// this is the function I''m stalled on writing
NotifyWorkerThreadToExit(workerthread_handle);
// wait for thread to exit cleanly
pthread_join(workerthread_handle, &ptr_ret);
DoProcessCleanupStuff();
}
Cierre la toma de audición y aceptar devolverá un error.
¿Qué no funciona de manera confiable con esto? Describe los problemas que estás enfrentando.
Puede usar un conducto para notificar al hilo que desea que salga. Luego puede tener una llamada de select()
que selecciona tanto en el conducto como en el puerto de escucha.
Por ejemplo (compila pero no ha sido completamente probado):
// NotifyPipe.h
#ifndef NOTIFYPIPE_H_INCLUDED
#define NOTIFYPIPE_H_INCLUDED
class NotifyPipe
{
int m_receiveFd;
int m_sendFd;
public:
NotifyPipe();
virtual ~NotifyPipe();
int receiverFd();
void notify();
};
#endif // NOTIFYPIPE_H_INCLUDED
// NotifyPipe.cpp
#include "NotifyPipe.h"
#include <unistd.h>
#include <assert.h>
#include <fcntl.h>
NotifyPipe::NotifyPipe()
{
int pipefd[2];
int ret = pipe(pipefd);
assert(ret == 0); // For real usage put proper check here
m_receiveFd = pipefd[0];
m_sendFd = pipefd[1];
fcntl(m_sendFd,F_SETFL,O_NONBLOCK);
}
NotifyPipe::~NotifyPipe()
{
close(m_sendFd);
close(m_receiveFd);
}
int NotifyPipe::receiverFd()
{
return m_receiveFd;
}
void NotifyPipe::notify()
{
write(m_sendFd,"1",1);
}
A continuación, select
con receiverFd()
y notifique la finalización mediante notify()
.
pthread_cancel para cancelar un hilo bloqueado en accept () es arriesgado si la implementación pthread no implementa la cancelación correctamente, es decir, si el hilo creó un socket, justo antes de volver a su código, se le solicita un pthread_cancel (), el hilo es cancelado, y el socket recién creado se filtró. Aunque FreeBSD 9.0 y posterior no tiene ese problema de condición de carrera, primero debe verificar su sistema operativo.