Sin mantener una lista de hilos actuales, intento ver que se entregue una señal en tiempo real a todos los hilos de mi proceso. Mi idea es hacerlo así:Señalización de todos los hilos en un proceso
- Inicialmente, el controlador de señal está instalado y la señal está desbloqueada en todos los hilos.
- Cuando un hilo desea enviar la señal de "emisión", adquiere un mutex y establece un indicador global de que la transmisión se está llevando a cabo.
- El emisor bloquea la señal (usando
pthread_sigmask
) por sí mismo, e ingresa repetidamente en un bucle llamando araise(sig)
hasta quesigpending
indica que la señal está pendiente (no había hilos restantes con la señal bloqueada). - Cuando los hilos reciben la señal, actúan sobre ella pero esperan en el manejador de señal para que se borre la señal de transmisión, de modo que la señal permanecerá enmascarada.
- El emisor termina el ciclo desbloqueando la señal (para obtener su propia entrega).
- Cuando el remitente maneja su propia señal, borra la bandera global para que todos los demás hilos puedan continuar con su actividad.
El problema que me estoy encontrando es que pthread_sigmask
no se está respetando. Todo funciona bien si ejecuto el programa de prueba bajo strace
(presumiblemente debido a diferentes tiempos de programación), pero tan pronto como lo ejecuto solo, el emisor recibe su propia señal (a pesar de haberlo bloqueado ...) y ninguno de los otros hilos alguna vez programado.
¿Alguna idea de lo que podría estar mal? He intentado usar sigqueue
en lugar de raise
, probando la máscara de señal, agregando sleep
por todos lados para asegurarme de que los hilos esperan pacientemente sus señales, etc. y ahora estoy perdido.
Edit: Gracias a la respuesta de psmears, creo que entiendo el problema. Aquí hay una solución potencial. La retroalimentación sería genial:
- En cualquier momento, puedo saber el número de subprocesos en ejecución, y puedo evitar toda creación de subprocesos y salir durante la señal de transmisión si es necesario.
- El hilo que desea hacer la señal de emisión adquiere un bloqueo (para que ningún otro hilo pueda hacerlo al mismo tiempo), luego bloquea la señal por sí mismo, y envía señales
num_threads
al proceso, luego desbloquea la señal por sí mismo . - El manejador de señal incrementa atómicamente un contador, y cada instancia del manejador de señal espera hasta que ese contador sea igual a
num_threads
para regresar. - El hilo que hizo la transmisión también espera que el contador llegue al
num_threads
, luego libera el bloqueo.
Una posible preocupación es que las señales no se pondrán en cola si el kernel está sin memoria (Linux parece tener ese problema). ¿Sabe si sigqueue
informa confiablemente a la persona que llama cuando no puede poner la señal en cola (en cuyo caso yo haría un bucle hasta que tenga éxito), o las señales podrían perderse silenciosamente?
Editar 2: Parece que está trabajando ahora. De acuerdo con la documentación para sigqueue
, devuelve EAGAIN
si no pone en cola la señal.Pero para mayor robustez, decidí seguir llamando al sigqueue
hasta que se ejecuten los manejadores de señal num_threads-1
, intercalando llamadas a sched_yield
después de que he enviado las señales num_threads-1
.
Hubo una condición de carrera en el tiempo de creación de subprocesos, contando nuevos hilos, pero lo resolví con un extraño (ab) uso de bloqueos de lectura-escritura. La creación de subprocesos es "lectura" y la señal de difusión es "escritura", por lo tanto, a menos que haya un hilo tratando de transmitir, no crea ninguna contención en la creación de subprocesos.
OK, si * todas * las actualizaciones de los datos tocados por el manejador de señal son atómicas, entonces esto se está acercando al funcionamiento :) Creo que todavía hay una condición de carrera si todos los subprocesos esperan que el contador sea 'num_threads' : si ocurre otro evento de transmisión * inmediatamente * después del primero, entonces es posible que el contador regrese a 0 antes de que todos los hilos hayan notado que es igual a 'núm_hilos'. Creo que esto se puede resolver con una variable de condición. También desaconsejaría la espera de ocupado (incluso con 'sched_yield'), esto puede provocar una gran carga de la CPU (¡y una duración de la batería baja si es relevante!) – psmears
Otro posible problema que puede probar es que es posible una señal para ser entregada a un hilo después de haber sido creada en el kernel, pero antes de que sus datos locales del hilo estén completamente configurados (es decir, en el tiempo entre el inicio del hilo y cuando llama a la función pasada a pthread_create))) ... – psmears
Todas las actualizaciones son atómicas porque todas ocurren en el kernel-space a través de syscalls que afectan erróneamente solo el hilo que llama y no el proceso; este código es una solución. :-) Solo puede ocurrir un evento de transmisión a la vez; toda la función está protegida por un mutex global. En cuanto a la espera ocupada, tal vez funcione una espera temporizada de una variable condicional con un tiempo de espera muy corto, pero desde entonces me he encontrado teniendo problemas con la entrega de muy pocas señales a menos que siga llamando a 'sigqueue' hasta que funcione. –