sisoputnfrba / foro

Foro de consultas para el trabajo práctico
148 stars 7 forks source link

El recv con la interfaz puede tener una condicion de carrera? #3557

Closed diegohcanetti closed 5 months ago

diegohcanetti commented 5 months ago

Para el manejo de Interfaces hicimos que cada vez que una termina su operación haga un send y del lado del Kernel hay un recv que desencola al proceso de bloqueados de esa proceso y lo manda a Ready. Sin embargo con las pruebas que estuvimos haciendo las mayorías de las veces rompe al segundo send y recv, otras pasa la operación y en otras rompe en el primer IO_GEN_SLEEP. Haciendo pruebas, debuggeando e intentando diferentes soluciones no encontramos el problema exacto.

📝 Código relevante

Del lado del Kernel:

void crear_hilo_io(t_pcb* proceso, t_interfaz* interfaz){

    ingresar_a_BLOCKED_IO(interfaz->cola_bloqueados ,proceso, motivo, interfaz->cola_bloqueado_mutex);
    logear_cola_io_bloqueados(interfaz);

    pthread_t hilo_manejo_io;
    pthread_create(&hilo_manejo_io, NULL, (void* ) esperar_io, interfaz);
    pthread_detach(hilo_manejo_io);

}

void esperar_io(t_interfaz* interfaz)
{
    int termine_io = 0;

    pthread_mutex_lock(&interfaz->comunicacion_interfaz_mutex);
    recv(interfaz->socket_conectado, &termine_io, sizeof(int), 0); 
    pthread_mutex_unlock(&interfaz->comunicacion_interfaz_mutex);

    ingresar_de_BLOCKED_a_READY_IO(interfaz->cola_bloqueados, interfaz->cola_bloqueado_mutex);
}

Del lado del IO:

while(1)
    {
        t_paquete* paquete = recibir_paquete(socket_kernel);
        void* stream = paquete->buffer->stream;

        if(paquete->codigo_operacion == GENERICA_IO_SLEEP) {

        proceso_conectado = sacar_entero_de_paquete(&stream);
        cantidad_tiempo = sacar_entero_de_paquete(&stream);

        log_info(generica_logger, "PID: %d - Operacion: IO_GEN_SLEEP\n", proceso_conectado);

        usleep(tiempo_unidad_de_trabajo * cantidad_tiempo * 1000);

        send(socket_kernel, &proceso_conectado, sizeof(int), 0);

        printf("El proceso: %d finalizo IO\n", proceso_conectado);
        }
     }

🐛 Cómo reproducir el error

Estamos realizando la prueba de DEADLOCK del cuatrimestre pasado, pero descubrimos que podemos reproducir el error con un solo proceso con estas instrucciones: SET AX 1 WAIT REC1 IO_GEN_SLEEP impresora1 10 WAIT REC2 SET BX 1 SUM AX BX IO_GEN_SLEEP impresora1 1 SIGNAL REC2 SIGNAL REC1 EXIT

💻 Logs

Lo corrí con Helgrind y logré observar esto en el Kernel cuando corte la conexion:

==8984== Thread #8: Exiting thread still holds 1 lock
==8984==    at 0x49F786E: __libc_recv (recv.c:28)
==8984==    by 0x49F786E: recv (recv.c:23)
==8984==    by 0x10BFC6: esperar_io (manejo_io.c:214)
==8984==    by 0x485396A: ??? (in /usr/libexec/valgrind/vgpreload_helgrind-amd64-linux.so)
==8984==    by 0x4964B42: start_thread (pthread_create.c:442)
==8984==    by 0x49F5BB3: clone (clone.S:100)
iago64 commented 5 months ago

Buenas! Cómo va?

Cuando decis rompe el send() y el recv(), puntualmente que error les esta tirando? Helgrind los va a ayudar solamente a ver que onda con los semáforos, pero si el problema es de memoria, la herramienta es Valgrind.

Saludos.-

diegohcanetti commented 5 months ago

Helgrind no nos está mostrando ninguna posible condición de carrera, no tenemos ni memory leaks ni segmention fault (sin embargo chequeamos y no nada definetly lost).

Al mencionar rompe, me refiero que nosotros lo que estamos haciendo como mostré antes es un send y recv entre el Kernel y la IO generica para que el proceso de Kernel sepa cuando termino el IO. Sin embargo, hay veces que funciona y hay veces que no, lo que dice helgrind es que el Kernel sigue esperando el mensaje de IO de que ya termino, pero la interfaz ya le hizo el send. Entonces no entendemos pq el Kernel no está recibiendo este "ping".

iago64 commented 5 months ago

Buenas!

Vamos por partes, en primer lugar Helgrind no te va a mostrar absolutamente NADA porque estas en 2 procesos distintos, la herramienta es potente, pero no puede saber que esta pasando en otros procesos.

En segundo lugar, lo que tenes que buscar aca es ver como estas estableciendo la comunicación, y empezar a ver un poco el mapa completo del problema. Para que entiendas lo que te estoy diciendo, lo que yo veo es lo siguiente:

Por un lado lo tenemos al Kernel que en algún lugar del código que no esta subido en este issue le manda un paquete a la interfaz de IO con un Código de Operacion, el PID y el tiempo a esperar y asumo que el código de operación uds tienen un ENUM donde existe GENERICA_IO_SLEEP y con esto podemos saber que lo que llego es la operación más simple de IO que es la de esperar X tiempo. Veo que del paquete sacas de forma extraña 2 enteros, y cuando me refiero a que lo sacas de forma extraña es porque veo que tenes las siguientes líneas:

proceso_conectado = sacar_entero_de_paquete(&stream);
cantidad_tiempo = sacar_entero_de_paquete(&stream);

Y veo que le pasas la dirección del puntero stream en lugar de quizas pasarle una copia o pasarle el mismo puntero. Una vez terminado este tiempo, tenemos un envío del PID del proceso desde la interfaz al Kernel, aca del lado kernel deberíamos salir del recv() y justo depsues de eso tenemos en la interfaz el mensaje: "El proceso: %d finalizo IO\n", que no entiendo porque lo tenes en un printf y no en un log_info o log_trace. Ahora del lado kernel tenes el recv() esperando que le llegue un mensaje y que no tiene un flag que solemos recomendarles que lo tengan en todos los llamados a recv() como es el flag MSG_WAITALL y aca veo que lo dejaste con 0 que no es el valor de MSG_WAITALL.

Escrito todo lo que estoy pensando, lo que no me termina de cerrar es, si te estas tomando el tiempo de debuggear las cosas para ver que datos estas mandando / recibiendo de ambos lados.

Saludos.-

diegohcanetti commented 5 months ago

Voy respondiendo las sugerencias/preguntas de a una:

  1. Helgrind solamente lo estaba usando para revisar si había errores antes del hilo y dio la casualidad que vi que el problema era que el Kernel se quedaba "pegada" en el recv, esperando a que le hagan un send.
  2. Asumiste correctamente como mandamos el paquete, y debuggeando podemos ver que se envía y recibe correctamente todo lo que necesitamos, en ese aspecto no hay ningún problema. Siempre hemos mandado los paquetes con la & del stream y nunca nos ha ocasionado problemas.
  3. Lo habíamos puesto en un printf pq no es un log_obligatorio, pero lo empezamos a testear con log_info para revisar los tiempos.
  4. Lo del recv le cambiamos la flag a MSG_WAITALL, se nos había pasado eso. Muchas gracias.

Sin embargo nos sigue provocando errores el send y recv, cuál es el error: Nosotros estamos haciendo dos IO_GEN_SLEEP, uno de 10 y otro de 1, en el config de ejemplo el tiempo de unidad de trabajo es de 250 por lo tanto el primer usleep es de (1000*10*250) y el segundo es de (1000*1*250), al ser tan rápido el sleep de esta segundo instrucción ocurre primero el send y después y el Kernel llega al recv, por lo tanto casi nunca "recibe nada". Intentamos probando diferentes flags y estrategias para manejar estos casos, pero no logramos corregir este problema.

iago64 commented 5 months ago

Buenas! Cómo va?

A ver si entiendo un poco la situación porque no se si es que me perdí en algo o que pasó. Del lado Kernel uds deberían:

Del lado de la Interfaz de IO uds deberían:

En el caso del código que esta de la interfaz, asumo que la función: recibir_paquete() es la que tiene el recv(). En toda la secuencia que estoy mencionando, ambos recv() deberían bloquear al proceso, por lo que, no te importa si llegaste antes al recv() que al send() del otro lado, vos deberías bloquearte esperando esa info. Este tipo de problemáticas es uno de los motivos principales por el que nos ponemos molestos con el tema de los protocolos, me parece que algo esta fallando en la definición.

Saludos.-

diegohcanetti commented 5 months ago

Hola,

Gracias por tu explicación detallada. Sin embargo, quería aclarar que ya habíamos implementado las funciones de send() y recv() de acuerdo con la lógica que mencionaste desde hace dos semanas. Parece que hubo una confusión en la comunicación anterior, pero quería asegurarte de que esos aspectos estaban cubiertos en nuestra implementación. Vamos a seguir con otros aspectos del TP.

Saludos.-