- Se plantea la crítica de que el libro de texto The Art of Multiprocessor Programming no aborda el concepto de futex, algo lamentable.
- Futex es un componente clave de la sincronización eficiente en la programación paralela moderna y ofrece un rendimiento superior al de los locks tradicionales basados en System V.
- Futex tiene una estructura que separa la adquisición del lock de las funciones de espera/despertar, reduciendo llamadas al sistema y sobrecarga innecesarias.
- Se incluyen ejemplos y técnicas para implementar directamente varios primitivos de concurrencia basados en futex, como spinlocks, mutexes y locks recursivos.
- El autor señala la brecha entre la academia y la práctica profesional, ya que el libro no cubre metodologías modernas de sincronización esenciales en la ingeniería real.
Introducción
- Phil Eaton inició un club de lectura de The Art of Multiprocessor Programming, 2nd Edition.
- Aunque este libro es considerado un texto de referencia en programación paralela, el autor critica la falta de utilidad práctica de su contenido.
- En particular, cuestiona que, pese a estar dirigido a estudiantes avanzados de licenciatura y posgrado, no trate futex, una técnica central de sincronización.
Qué es futex y por qué importa
- Futex significa “fast user space mutex”, pero en realidad, más que un mutex, es un bloque primitivo de sincronización con soporte del sistema operativo para implementar locks modernos.
- En el pasado, la mayoría de los locks se implementaban con semáforos de System V IPC, lo que tenía límites de eficiencia y escalabilidad.
- Cuando Linux introdujo futex en 2002, mostró un rendimiento 20 a 120 veces más rápido que los locks de System V en entornos con 1000 tareas concurrentes.
- Otros sistemas operativos como Windows (2012) y macOS (2016) también adoptaron mecanismos similares.
- Hoy en día, los locks de bibliotecas del sistema ampliamente usadas, como pthreads, utilizan futex.
Cómo funciona futex y en qué se diferencia
- Los semáforos tradicionales combinaban lock y espera, pero futex separa la adquisición del lock de la espera/despertar.
- Gracias a esto, se pueden reducir demoras y llamadas al sistema innecesarias, y si está claro que no hay hilos esperando al liberar el lock, ni siquiera hace falta entrar al kernel.
- La llamada de espera de futex (
wait) hace que solo se espere cuando el valor de una dirección de memoria específica está en el estado deseado, y también soporta timeout.
- La llamada de despertar de futex (
wake) despierta la cantidad deseada de hilos desde una lista interna de espera asociada con una dirección de memoria específica.
- Como exige validar el valor real de la dirección de memoria, evita esperas innecesarias cuando el estado ya cambió.
Uso práctico de futex: implementación directa
- Como futex es una primitiva de bajo nivel, se usan tipos de datos
atomic teniendo en cuenta los problemas de orden de operaciones de memoria del compilador y del hardware.
- En Linux, hay que invocar directamente la syscall de futex con
syscall; en macOS, se usa la interfaz __ulock (recientemente se agregó una API más simple).
- Básicamente, la espera de futex devuelve 0 en caso de éxito y un código de error en caso de falla (por ejemplo, timeout).
- Operaciones clave basadas en futex:
h4x0r_futex_wait_timespec() : espera si el valor esperado coincide; puede aplicar timeout
h4x0r_futex_wake() : despierta a 1 o a todos los hilos en espera
Ejemplos prácticos de implementación de mutex/spinlock/lock recursivo
Spinlock
- La forma más simple de lock, funciona solo con un único bit (
atomic_fetch_or).
- Hace un bucle infinito (“spin”) hasta obtener el lock, pero en situaciones de alta contención desperdicia CPU y tiene problemas estructurales, como desbloqueos incorrectos y riesgo de deadlock en llamadas recursivas.
Mutex híbrido (“unsafe” mutex)
- Normalmente intenta primero con un spinlock y, tras cierto número de fallos, cambia a futex para bloquear de forma eficiente.
- Si no hay hilos esperando, se pueden evitar llamadas al sistema innecesarias; y para quienes esperan, se pueden minimizar las syscalls de despertar.
- Como no valida estrictamente la propiedad ni maneja bien la recursión, se usa el nombre “unsafe”.
Mutex con contador de hilos en espera
- Un bit representa el estado del lock y el resto se usa para contar la cantidad de hilos en espera, con el objetivo de reducir syscalls de despertar innecesarias.
- Aun así, sigue sin manejar propiedad ni recursión.
Mutex con gestión de propiedad
- Mediante el valor
pthread_t, rastrea claramente al propietario del lock y su estado, detectando problemas como unlock incorrectos o uso recursivo indebido.
- La adquisición del lock, su liberación y la gestión de hilos en espera se controlan todas con operaciones atómicas estrictas.
Lock recursivo
- Agrega un contador de profundidad por hilo, permitiendo que el mismo hilo adquiera el lock varias veces de forma anidada.
- Al hacer
unlock, la profundidad disminuye, y cuando llega a 0 se realiza el desbloqueo real y el despertar correspondiente.
- Cada operación se implementa con operaciones atómicas y validación estricta de propiedad.
Tareas pendientes y realidad de la ingeniería
- Si el hilo dueño del lock termina de forma anormal o muere, hace falta gestión adicional, como listas de control separadas o callbacks de finalización, para administrar el lock.
- También se requieren consideraciones adicionales para gestionar cambios de estado cuando se usan mutexes compartidos entre procesos.
- En los locks RW de POSIX, el comportamiento de anidación recursiva no está definido y varía según la implementación, por lo que en la práctica es difícil garantizar la seguridad.
- El autor critica que el libro no incluya en el plan de estudios problemas de concurrencia realmente importantes en la práctica (futex, locks recursivos, runtimes asíncronos, etc.).
Conclusión
- The Art of Multiprocessor Programming está demasiado sesgado hacia la historia o la teoría y no incorpora adecuadamente conocimientos prácticos modernos de programación paralela.
- Si no se tratan bien componentes clave de sincronización como futex, que son los que realmente funcionan en los sistemas, eso podría causar un perjuicio real a quienes se están formando.
- El autor enfatiza la necesidad de reflejar conceptos actuales y complementar el contenido con aspectos prácticos.
Material de referencia
- El código de ejemplo completo puede consultarse en codeberg
Aún no hay comentarios.