Proper maintenance and care of multi-threading locks
Las siguientes estrategias se utilizan para garantizar que el código esté libre de interbloqueos (generalmente abordando la cuarta condición de Coffman: espera circular).
- estructura el código de tal manera que solo se necesite adquirir un bloqueo a la vez
- siempre adquiera bloqueos compartidos en el mismo orden, según lo indicado en la tabla a continuación
- evitar construcciones que esperen necesitar recursión sin restricciones
Locks
A continuación se presentan todas las cerraduras que existen en el sistema y los mecanismos para utilizarlas que evitan el potencial de interbloqueos (no se permite el algoritmo del avestruz aquí):
Los siguientes son definitivamente bloqueos de hoja (nivel 1) y no deben intentar adquirir ningún otro bloqueo:
punto seguro
Tenga en cuenta que este bloqueo se adquiere implícitamente mediante
JL_LOCK
yJL_UNLOCK
. Use las variantes_NOGC
para evitar eso en bloqueos de nivel 1.Mientras se sostiene este bloqueo, el código no debe realizar ninguna asignación ni alcanzar ningún punto seguro. Tenga en cuenta que hay puntos seguros al realizar asignaciones, habilitar / deshabilitar la recolección de basura, entrar / restaurar marcos de excepciones y tomar / liberar bloqueos.
mapa_compartido
finalizadores
pagealloc
gcpermlock
flisp
jlenstackwalk (Win32)
ResourcePool<?>::mutex
RLST_mutex
llvmimprimiendomutex
jlbloqueadostream::mutex
debuginfo_asyncsafe
inferenciatiempomutex
ExecutionEngine::SessionLock
flisp en sí mismo ya es seguro para hilos, este bloqueo solo protege el grupo
jl_ast_context_list_t
, de igual manera, los mutexes de ResourcePool<?>::solo protegen el grupo de recursos asociado.
El siguiente es un bloqueo de hoja (nivel 2), y solo adquiere bloqueos de nivel 1 (punto seguro) internamente:
- globalrootslock
- Módulo->bloquear
- JLDebuginfoPlugin::PluginMutex
- nuevoinferidomutex
El siguiente es un candado de nivel 3, que solo puede adquirir candados de nivel 1 o nivel 2 internamente:
- Método->bloqueoEscritura
- tipo de caché
El siguiente es un candado de nivel 4, que solo puede recurrir para adquirir candados de nivel 1, 2 o 3:
- MethodTable->bloqueoEscritura
No se puede llamar a ningún código de Julia mientras se sostiene un bloqueo por encima de este punto.
orc::ThreadSafeContext (TSCtx) los bloqueos ocupan un lugar especial en la jerarquía de bloqueos. Se utilizan para proteger el estado global no seguro para hilos de LLVM, pero puede haber un número arbitrario de ellos. Por defecto, todos estos bloqueos pueden ser tratados como bloqueos de nivel 5 a efectos de comparación con el resto de la jerarquía. La adquisición de un TSCtx solo debe hacerse desde el grupo de TSCtx del JIT, y todos los bloqueos en ese TSCtx deben ser liberados antes de devolverlo al grupo. Si se deben adquirir múltiples bloqueos de TSCtx al mismo tiempo (debido a la compilación recursiva), entonces los bloqueos deben adquirirse en el orden en que los TSCtx fueron tomados del grupo.
El siguiente es un candado de nivel 5.
- JuliaOJIT::EmissionMutex
El siguiente es un candado de nivel 6, que solo puede recurrir para adquirir candados en niveles inferiores:
- código generador
- jlmodulesmutex
Lo siguiente es un bloqueo casi raíz (nivel final-1), lo que significa que solo se puede mantener el bloqueo raíz al intentar adquirirlo:
typeinf
este quizás sea uno de los más complicados, ya que la inferencia de tipos puede ser invocada desde muchos puntos
actualmente el bloqueo está fusionado con el bloqueo de codegen, ya que se llaman entre sí de forma recursiva
El siguiente bloqueo sincroniza la operación de E/S. Ten en cuenta que realizar cualquier E/S (por ejemplo, imprimir mensajes de advertencia o información de depuración) mientras se sostiene cualquier otro bloqueo mencionado anteriormente puede resultar en bloqueos perniciosos y difíciles de encontrar. ¡TEN MUCHO CUIDADO!
iolock
Bloqueos de ThreadSynchronizers individuales
esto puede continuar realizándose después de liberar el iolock, o adquirirse sin él, pero ten mucho cuidado de nunca intentar adquirir el iolock mientras lo sostienes
Libdl.LazyLibrary bloqueo
La siguiente es la cerradura raíz, lo que significa que no se debe tener ninguna otra cerradura al intentar adquirirla:
nivel superior
esto debe llevarse a cabo al intentar una acción de alto nivel (como crear un nuevo tipo o definir un nuevo método): intentar obtener este bloqueo dentro de una función en etapa causará una condición de interbloqueo.
además, no está claro si cualquier código puede ejecutarse de manera segura en paralelo con una expresión de nivel superior arbitraria, por lo que puede requerir que todos los hilos lleguen primero a un punto seguro.
Broken Locks
Las siguientes cerraduras están rotas:
nivel superior
no existe en este momento
arreglar: créalo
Módulo->bloquear
Esto es vulnerable a bloqueos ya que no puede estar seguro de que se adquiera en secuencia. Algunas operaciones (como
import_module
) carecen de un bloqueo.arreglar: reemplazar con
jl_modules_mutex
?loading.jl:
require
yregister_root_module
Este archivo potencialmente tiene numerosos problemas.
arreglar: necesita cerraduras
Shared Global Data Structures
Estas estructuras de datos necesitan bloqueos debido a que son un estado global mutable compartido. Es la lista inversa de la lista de prioridad de bloqueos anterior. Esta lista no incluye recursos hoja de nivel 1 debido a su simplicidad.
Modificaciones de MethodTable (def, cache) : MethodTable->bloqueo de escritura
Declaraciones de tipo: bloqueo de nivel superior
Tipo de aplicación: bloqueo de caché de tipo
Tablas de variables globales: Módulo->bloquear
Módulo serializador: bloqueo de nivel superior
JIT y inferencia de tipos: bloqueo de generación de código
Actualizaciones de MethodInstance/CodeInstance: Method->writelock, bloqueo de codegen
Estos se establecen en la construcción e son inmutables:
- tiposDeEspecificación
- sparam_vals
- def
- propietario
Estos son establecidos por
jl_type_infer
(mientras se mantiene el bloqueo de generación de código):
- caché
- rettype
- inferido
* valid ages
inInference
bandera:
- optimización para evitar rápidamente volver a
jl_type_infer
mientras ya se está ejecutando- el estado actual (de establecer
inferred
, luegofptr
) está protegido por el bloqueo de generación de código
Punteros a funciones:
- estas transiciones ocurren una vez, de
NULL
a un valor, mientras se mantiene el bloqueo de generación de códigoCaché del generador de código (el contenido de
functionObjectsDecls
):
- estos pueden transitar múltiples veces, pero solo mientras se mantenga el bloqueo de generación de código
- es válido usar una versión antigua de esto, o bloquear para nuevas versiones de esto, por lo que las carreras son benignas, siempre que el código tenga cuidado de no hacer referencia a otros datos en la instancia del método (como
rettype
) y asuma que está coordinado, a menos que también se mantenga el bloqueo de generación de código.
LLVMContext : bloqueo de generación de código
Método : Método->bloqueoEscritura
- raíces matriz (serializador y generación de código)
- invocar / especializaciones / modificaciones de tfunc