Skip to content

gabrieldiem/threads-posix-poc-with-cpp

Repository files navigation

TP 2: Threads - Taller de Programación 1 Veiga

Este repositorio contiene la solución propuesta para la PoC detallada en la consigna.

Table of Contents

  1. Diagramas de hilos y queues
    1. Manejo de mensajes con los clientes
    2. Servidor
  2. Queues
  3. Tick system
  4. Setup
  5. Compilación
  6. Running multi client
  7. Comparación outputs
  8. Supresión de flags de cppcheck
  9. Refactor para la re-entrega
  10. Licencia
  11. Código fuente utilizado

Diagramas de hilos y queues

Manejo de mensajes con los clientes

Este diagrama refleja cómo se manejan los threads relacionados al envío y recepción de mensajes con un cliente, desde el thread del gameloop del server (apodado thread Engine).

Se ilustra un sólo cliente, nombrándolo con el sufijo "1", las interacciones serían las mismas con más clientes en sus respectivos threads. El cliente es single-threaded.

Vale aclarar que si bien el objeto ClientManager está instanciado en el thread Main, el thread Engine, por ser un thread, tiene acceso al stack, en particular al code segment donde las instrucciones de ClientManager residen, por lo que no representa un bloqueo del thread Engine el hecho de que el thread Main esté bloqueado en leer input del usuario

Servidor

El resto de la arquitectura / intercomunicación de threads con queues se representa en este diagrama:

Queues

Todas las Queues utilizadas fueron del tipo unbounded, entendiendo que el sistema podrá tomar una cantidad virtualmente infinita de clientes, aunque en la práctica la cantidad máxima estaría delimitada por la memoria y otros recursos del sistema que hostee el servidor.

Las queues de las ClientConnections son bloqueantes porque me interesa que esos threads no hagan busy wait.

Los pop de las queues con los que GameServerEngine debe interactuar son no bloqueantes ya que no se quiere bloquear el gameloop. Sin embargo, los push de las mismas son bloqueantes.

Tick system

Para el motor del servidor se utiliza un "sistema de ticks", inspirado en el funcionamiento del engine Source by Valve.

Así como un frame es una actualización de la imagen gráfica que renderiza un motor gráfico. Un tick o "gameframe" es una actualización de la lógica/estado del juego.

Setup

Instalar pre-commit hooks:

pre-commit install

Para crear un commit sin correr los hooks se debe ejecutar:

git commit --no-verify

Para correr cppcheck sin necesidad de un commit se debe ejecutar:

pre-commit run --all-files

Compilación

Compilación con código as-is:

make

Compilación con código con wrap randomizado de sockets:

make wrapsocks=1

Running multi client

Correr tests sin valgrind:

./run_tests.sh ./ casos/ multi-client no-valgrind 60 10

Correr tests con valgrind:

./run_tests.sh ./ casos/ multi-client valgrind 60 10

Comparación outputs

Comparar outputs:

./compare_outputs.sh casos/ salidas/

Supresión de flags de cppcheck

En algunos headers (.h) puntuales se suprimió el warning unusedStructMember de cppcheck. Esta decisión fue tomada porque es un falso positivo, en todos los casos que sucede, es sólo en headers de clases sobre variables privadas que sí se usan dentro del código (en el .cpp).

Refactor para la re-entrega

Se colocó lógica de sanitización de user input en las funciones main tanto del server como del cliente.

Donde se consideró necesario se utilizaron constantes de tipo static en lugar de member variables de una clase, para reducir la complejidad del constructor.

Se eliminó el thread de UserInput para ahora ejecutar esa lógica en el thread main del server directamente. Dejando la responsabilidad de correr el gameloop principal en un thread aparte a la nueva clase GameServerEngine. Ahora el manejo de threads de más alto nivel es responsabilidad del main thread del server, para tener una arquitectura más top-down.

Se refactorizó la lógica de prints y manejo de notificaciones a los clientes para que clases que no deban conocer de esa lógica, no la tengan. De esta manera, los roles de GameServerProtocol, GameServerEngine, Army y el game server mismo son más claros.

En diversas partes de la solución se reemplazó código custom por algoritmos de la STL para que el código sea más expresivo y estándar.

La función reaper ahora se ejecuta inmediatamente antes de la conexión de un nuevo cliente, y no en cada tick como era antes.

Se aplicó una mejora/simplificación del código de la clase Socket para el envío y recepción de halfwords.

Licencia

GPL v2

Código fuente utilizado

Se agradece el código proveído por la cátedra para los sockets, threads y queue. Se trabajó sobre ese código y se lo modificó. Código fuente usado bajo licencia GPL v2: https://github.com/eldipa/sockets-en-cpp y https://github.com/eldipa/hands-on-threads agradecimiento a @eldipa.