Anotar comportamiento de bloqueo
Para evitar errores de simultaneidad en el programa multiproceso, siga siempre una materia de bloqueo adecuada y use anotaciones SAL.
Los errores de simultaneidad son muy difíciles de reproducir, diagnosticar y depurar porque no son deterministas. El razonamiento sobre la intercalación de subprocesos es difícil en el mejor de los casos y resulta poco práctico cuando se diseña un cuerpo de código que tiene más de unos pocos subprocesos. Por lo tanto, es una buena práctica seguir una materia de bloqueo en los programas multiproceso. Por ejemplo, seguir un orden de bloqueo al adquirir varios bloqueos ayuda a evitar interbloqueos y adquirir el bloqueo de protección adecuado antes de acceder a un recurso compartido ayuda a evitar condiciones de carrera.
Desafortunadamente, las reglas de bloqueo aparentemente simples pueden ser sorprendentemente difíciles de seguir en la práctica. Una limitación fundamental en los compiladores y lenguajes de programación actuales es que no admiten directamente la especificación y el análisis de los requisitos de simultaneidad. Los programadores tienen que basarse en comentarios de código informales para expresar sus intenciones sobre cómo usan los bloqueos.
Las anotaciones SAL de simultaneidad están diseñadas para ayudarle a especificar efectos secundarios de bloqueo, responsabilidad de bloqueo, protección de datos, jerarquía de orden de bloqueo y otro comportamiento de bloqueo esperado. Al hacer explícitas las reglas implícitas, las anotaciones de simultaneidad sal proporcionan una manera coherente de documentar cómo usa el código las reglas de bloqueo. Las anotaciones de simultaneidad también mejoran la capacidad de las herramientas de análisis de código para buscar condiciones de carrera, interbloqueos, operaciones de sincronización no coincidentes y otros errores sutiles de simultaneidad.
Instrucciones generales
Mediante el uso de anotaciones, puede establecer los contratos implícitos en las definiciones de función entre implementaciones (destinatarios) y clientes (llamadores), y invariables express y otras propiedades del programa que puedan mejorar aún más el análisis.
SAL admite muchos tipos diferentes de primitivas de bloqueo, por ejemplo, secciones críticas, exclusiones mutuas, bloqueos de número y otros objetos de recursos. Muchas anotaciones de simultaneidad toman una expresión de bloqueo como parámetro. Por convención, la expresión de ruta de acceso del objeto de bloqueo subyacente indica un bloqueo.
Algunas reglas de propiedad de subprocesos que hay que tener en cuenta:
Los bloqueos de giro son bloqueos sin contar que tienen clara la propiedad del subproceso.
Las exclusiones mutuas y las secciones críticas son bloqueos con recuento que tienen una propiedad de subproceso clara.
Los semáforos y los eventos son bloqueos de recuento que no tienen propiedad de subproceso clara.
Bloqueo de anotaciones
En la tabla siguiente se enumeran las anotaciones de bloqueo.
| Anotación | Descripción |
|---|---|
_Acquires_exclusive_lock_(expr) |
Anota una función e indica que, en el estado posterior, la función incrementa en uno el recuento exclusivo de bloqueos del objeto de bloqueo denominado por expr . |
_Acquires_lock_(expr) |
Anota una función e indica que, en el estado posterior, la función incrementa en uno el número de bloqueos del objeto de bloqueo denominado por expr . |
_Acquires_nonreentrant_lock_(expr) |
Se adquiere el bloqueo denominado expr por . Si el bloqueo ya se mantiene, se notifica un error. |
_Acquires_shared_lock_(expr) |
Anota una función e indica que, en el estado posterior, la función incrementa en uno el número de bloqueos compartidos del objeto de bloqueo denominado por expr . |
_Create_lock_level_(name) |
Instrucción que declara que el símbolo es un nivel de bloqueo para que name se pueda usar en las anotaciones y _Has_Lock_level__Lock_level_order_ . |
_Has_lock_kind_(kind) |
Anota cualquier objeto para refinar la información de tipo de un objeto de recurso. A veces se usa un tipo común para diferentes tipos de recursos y el tipo sobrecargado no es suficiente para distinguir los requisitos semánticos entre varios recursos. Esta es una lista de parámetros kind predefinidos:_Lock_kind_mutex_Identificador de tipo de bloqueo para exclusiones mutuas. _Lock_kind_event_Identificador de tipo de bloqueo para eventos. _Lock_kind_semaphore_Identificador de tipo de bloqueo para semáforos. _Lock_kind_spin_lock_Identificador de tipo de bloqueo para bloqueos de número. _Lock_kind_critical_section_Identificador de tipo de bloqueo para las secciones críticas. |
_Has_lock_level_(name) |
Anota un objeto de bloqueo y le proporciona el nivel de bloqueo de name . |
_Lock_level_order_(name1, name2) |
Instrucción que proporciona el orden de bloqueo entre name1 y name2 . Los bloqueos que tienen name1 nivel deben adquirirse antes de los bloqueos que tienen el nivel name2 . |
_Post_same_lock_(expr1, expr2) |
Anota una función e indica que, en el estado posterior, los dos bloqueos, y , se tratan como si fuera expr1 el mismo objeto de expr2 bloqueo. |
_Releases_exclusive_lock_(expr) |
Anota una función e indica que, en el estado posterior, la función disminuye en uno el recuento exclusivo de bloqueos del objeto de bloqueo denominado por expr . |
_Releases_lock_(expr) |
Anota una función e indica que, en el estado posterior, la función disminuye en uno el número de bloqueos del objeto de bloqueo denominado por expr . |
_Releases_nonreentrant_lock_(expr) |
Se libera el bloqueo denominado expr por . Se notifica un error si el bloqueo no se mantiene actualmente. |
_Releases_shared_lock_(expr) |
Anota una función e indica que, en el estado posterior, la función disminuye en uno el número de bloqueos compartidos del objeto de bloqueo denominado por expr . |
_Requires_lock_held_(expr) |
Anota una función e indica que, en estado previo, el recuento de bloqueos del objeto denominado por expr es al menos uno. |
_Requires_lock_not_held_(expr) |
Anota una función e indica que, en estado previo, el recuento de bloqueos del objeto denominado expr por es cero. |
_Requires_no_locks_held_ |
Anota una función e indica que los recuentos de bloqueos de todos los bloqueos que conoce el checker son cero. |
_Requires_shared_lock_held_(expr) |
Anota una función e indica que, en estado previo, el recuento de bloqueos compartidos del objeto denominado por expr es al menos uno. |
_Requires_exclusive_lock_held_(expr) |
Anota una función e indica que, en estado previo, el recuento de bloqueos exclusivos del objeto denominado por expr es al menos uno. |
Intrínsecos de SAL para objetos de bloqueo no expuestos
La implementación de las funciones de bloqueo asociadas no expone determinados objetos de bloqueo. En la tabla siguiente se enumeran las variables intrínsecas de SAL que permiten anotaciones en funciones que operan en esos objetos de bloqueo no explorados.
| Anotación | Descripción |
|---|---|
_Global_cancel_spin_lock_ |
Describe el bloqueo de número de cancelación. |
_Global_critical_region_ |
Describe la región crítica. |
_Global_interlock_ |
Describe las operaciones entrelazados. |
_Global_priority_region_ |
Describe la región de prioridad. |
Anotaciones de acceso a datos compartidos
En la tabla siguiente se enumeran las anotaciones para el acceso a datos compartidos.
| Anotación | Descripción |
|---|---|
_Guarded_by_(expr) |
Anota una variable e indica que cada vez que se tiene acceso a la variable, el número de bloqueos del objeto de bloqueo al que llama expr es al menos uno. |
_Interlocked_ |
Anota una variable y es equivalente a _Guarded_by_(_Global_interlock_) . |
_Interlocked_operand_ |
El parámetro de función anotada es el operando de destino de una de las distintas funciones interbloqueadas. Esos operandos deben tener propiedades adicionales específicas. |
_Write_guarded_by_(expr) |
Anota una variable e indica que cada vez que se modifica la variable, el número de bloqueos del objeto de bloqueo al que se llama expr es al menos uno. |
Anotaciones de bloqueo inteligente y RAII
Los bloqueos inteligentes normalmente encapsulan bloqueos nativos y administran su duración. En la tabla siguiente se enumeran las anotaciones que se pueden usar con bloqueos inteligentes y patrones de codificación RAII con compatibilidad con move semántica.
| Anotación | Descripción |
|---|---|
_Analysis_assume_smart_lock_acquired_ |
Indica al analizador que suponga que se ha adquirido un bloqueo inteligente. Esta anotación espera un tipo de bloqueo de referencia como parámetro. |
_Analysis_assume_smart_lock_released_ |
Indica al analizador que suponga que se ha liberado un bloqueo inteligente. Esta anotación espera un tipo de bloqueo de referencia como parámetro. |
_Moves_lock_(target, source) |
Describe la move constructor operación que transfiere el estado de bloqueo del objeto a sourcetarget . se considera un objeto recién construido, por lo que cualquier estado que tenía target antes se pierde y se reemplaza por el estado source . también se restablece a un estado limpio sin recuentos de bloqueos ni destino de alias, pero los alias que apuntan a él source permanecen sin cambios. |
_Replaces_lock_(target, source) |
Describe la move assignment operator semántica en la que se libera el bloqueo de destino antes de transferir el estado desde el origen. Esto se puede considerar como una combinación de _Moves_lock_(target, source) precedida de _Releases_lock_(target) un . |
_Swaps_locks_(left, right) |
Describe el comportamiento swap estándar que supone que los objetos y leftright intercambian su estado. El estado intercambiado incluye el recuento de bloqueos y el destino de alias, si está presente. Los alias que apuntan a left los objetos right y permanecen sin cambios. |
_Detaches_lock_(detached, lock) |
Describe un escenario en el que un tipo de contenedor de bloqueo permite la desasociación con su recurso contenido. Esto es similar a cómo funciona con su puntero interno: permite a los programadores extraer el puntero y dejar su contenedor de puntero inteligente std::unique_ptr en un estado limpio. Una lógica similar es compatible con std::unique_lock y se puede implementar en contenedores de bloqueo personalizados. El bloqueo desasociado conserva su estado (recuento de bloqueos y destino de alias, si existe), mientras que el contenedor se restablece para que contenga un número de bloqueos cero y ningún destino de alias, al tiempo que conserva sus propios alias. No hay ninguna operación en los recuentos de bloqueos (liberación y adquisición). Esta anotación se comporta exactamente _Moves_lock_ como, salvo que el argumento desasociado debe ser return en lugar de this . |
Vea también
- Utilizar anotaciones SAL para reducir defectos de código de C/C++
- Introducción a SAL
- Anotar parámetros de función y valores devueltos
- Anotar el comportamiento de funciones
- Anotar structs y clases
- Especificar cuándo y dónde se aplica una anotación
- Funciones intrínsecas
- Procedimientos recomendados y ejemplos