BookinglyTech News
Software

Los turnstiles de Solaris, el mecanismo que sigue vivo en los kernels actuales

Solaris está muerto, pero su forma de gestionar la inversión de prioridad en mutex bloqueantes se copió a Illumos, al XNU de Apple, a FreeBSD y a NetBSD.

3 min de lecturaLobsters0 vistas

Solaris fue durante años la referencia en soporte de multiproceso simétrico, y buena parte de esa ventaja salía de innovaciones nacidas dentro del proyecto. Una de las menos comentadas son los turnstiles, la pieza con la que el sistema gestionaba los hilos que esperan un lock bloqueante y propagaba prioridades por la cadena de propietarios. Solaris está muerto, pero la idea sobrevive: se copió a Illumos, al XNU de Apple, a FreeBSD y a NetBSD, y hay variantes del mismo concepto en navegadores y runtimes de lenguajes.

El problema que resolvían

La apuesta de Sun por los mutex bloqueantes tenía sentido: dan mejor latencia para tareas de alta prioridad, algo necesario si quieres acercarte a un comportamiento de tiempo casi real. Pero traen dos inconvenientes.

El primero es el espacio. Un mutex bloqueante no es un bit que dice si está libre. El kernel necesita saber quién lo tiene, qué hilos esperan y con qué estado coordinarlos. Todo eso se puede meter en cada mutex, pero en software con locking fino —muchos locks protegiendo porciones pequeñas de estado— la mayoría está sin contención y ese libro contable es espacio desperdiciado. Mantener los mutex pequeños abarata el locking fino y permite usar más locks en lugar de agrupar estado bajo unos pocos gruesos.

El segundo es la inversión de prioridad. Un spinlock desactiva la preempción, mientras que un mutex bloqueante no. Sin ese truco, una tarea de baja prioridad B que tiene el lock que necesita la de alta prioridad A puede ser preemptada una y otra vez por cualquier tarea de prioridad media que se vuelva ejecutable. A acaba esperando detrás de trabajo que nunca debería haberla retrasado, y con un retraso que no tiene tope.

La herencia de prioridad arregla eso: B hereda temporalmente la prioridad de A hasta terminar su sección crítica y así corre por delante de las tareas intermedias. Cuando los locks dependen de otros locks, la prioridad tiene que propagarse por la cadena —A espera a B, B espera a C— hasta llegar al propietario que de verdad hace falta. Eso es la herencia multihop, y llevarla en el kernel sin cargarse el rendimiento es la parte difícil.

Un turnstile por hilo

El turnstile es la estructura que un sistema derivado de UNIX asocia a un lock contenido. Guarda qué hilos esperan y lo necesario para propagar la prioridad por la cadena de propietarios. El detalle astuto: nada de eso vive dentro del lock. Cada hilo recibe su propio turnstile al crearse, por si algún día compite por un mutex bloqueante. Así el lock se queda diminuto y la contabilidad pesada solo existe donde hay contención real.

No es el único camino. Windows tiene AutoBoost y Linux resuelve lo mismo con su maquinaria de rt-mutex. Para quien quiera leer código, la implementación de FreeBSD es de las más directas.

Lo relevante aquí no es la nostalgia por Solaris, sino que un mecanismo de sincronización que casi nadie nombra está en el kernel que ejecuta tus contenedores y en el que hay debajo de tu portátil. Si tocas scheduling, locking o latencia en cualquiera de esos sistemas, la cadena de propietarios que hay detrás tiene este origen.