java multithreading static-analysis event-dispatch-thread

java - Análisis de hilo estático: ¿Buena idea?



javax swing swingutilities invokelater (3)

Esta respuesta está más enfocada en el aspecto teórico de tu pregunta.

Fundamentalmente, está haciendo una afirmación: "Este método se ejecuta solo en ciertos subprocesos". Esta afirmación no es realmente diferente de cualquier otra afirmación que pueda hacer ("El método acepta solo enteros menores de 17 para el parámetro X"). Los problemas son

  • ¿De dónde vienen esas afirmaciones?
  • ¿Pueden los analizadores estáticos verificarlos?
  • ¿De dónde sacas tal analizador estático?

La mayoría de estas afirmaciones tienen que venir de los diseñadores de software, ya que son las únicas personas que conocen las intenciones. El término tradicional para esto es "Diseño por Contrato", aunque la mayoría de los esquemas de DBC solo están sobre el estado actual del programa (macro de afirmación de C) y realmente deberían estar sobre los estados pasados ​​y futuros del programa ("aserciones temporales"), e. , g., "Esta rutina asignará un bloque de almacenamiento y, finalmente, algún fragmento de código lo desasignará". Uno puede construir herramientas que intenten determinar de forma realista cuáles son las afirmaciones (por ejemplo, el trabajo de inducción de afirmaciones de Engler; otros han trabajado en esta área). Eso es útil, pero los falsos positivos son un problema. Como cuestión práctica, pedir a los diseñadores que codifiquen tales afirmaciones no parece ser particularmente oneroso, y es realmente una buena documentación a largo plazo. Si codifica tales aserciones con una construcción de lenguaje "Contrato" específica, o con una declaración if ("si Debug && Not ( aserción ) Then Fail ();") u ocultalas en una anotación, en realidad es solo una cuestión de conveniencia. Es agradable cuando el lenguaje permite codificar tales aserciones directamente.

La verificación de tales aseveraciones estáticamente es difícil. Si solo utiliza el estado actual, el analizador estático tiene que hacer un análisis completo del flujo de datos de toda su aplicación, ya que la información necesaria para satisfacer la afirmación probablemente proviene de datos creados por otra parte de la aplicación. (En su caso, la señal "dentro de la EDT" debe provenir del análisis de todo el gráfico de llamadas de la aplicación para ver si hay alguna ruta de llamada que conduzca al método desde un hilo que NO sea el hilo EDT). Si usa propiedades temporales, la verificación estática necesita, además, algún tipo de lógica de verificación de espacio de estado; Estos son actualmente herramientas de investigación bastante importantes. Incluso con toda esta maquinaria, los analizadores estáticos generalmente tienen que ser "conservadores" en sus análisis; Si no pueden demostrar que algo es falso, tienen que asumir que es verdad, debido al problema de la detención.

¿De dónde sacas esos analizadores? Dada toda la maquinaria necesaria, son difíciles de construir y, por lo tanto, debe esperar que sean raras. Si alguien ha construido uno, genial. Si no ... como regla general, no querrá hacerlo usted mismo desde cero. La mejor esperanza a largo plazo es contar con una maquinaria genérica de análisis de programas sobre la cual construir dichos analizadores, para amortizar el costo de la construcción de toda la infraestructura. (Construyo los fundamentos de las herramientas de análisis de programas; consulte nuestro kit de herramientas de reingeniería de software DMS ).

Una forma de hacer que sea "más fácil" construir tales analizadores estáticos es restringir los casos que manejan para reducir el alcance, por ejemplo, CheckThread. Espero que CheckThread haga exactamente lo que hace actualmente, y es poco probable que se haga mucho más fuerte.

La razón por la que las "macros" de afirmación y otras comprobaciones dinámicas de "estado actual" son populares es que realmente pueden implementarse mediante una simple prueba de tiempo de ejecución. Eso es bastante práctico. El problema aquí es que nunca puede ejercer un camino que conduzca a condiciones fallidas. Por lo tanto, para el análisis dinámico, la ausencia de falla detectada no es realmente evidencia de corrección. Todavía se siente bien.

En pocas palabras: los analizadores estáticos y los analizadores dinámicos tienen su fuerza.

Ayudo a mantener y construir sobre una interfaz gráfica de usuario de Swing bastante grande, con mucha interacción compleja. A menudo me encuentro corrigiendo errores que son el resultado de que las cosas se meten en estados extraños debido a alguna condición de carrera en otra parte del código.

A medida que el código base crece, me he dado cuenta de que se ha vuelto menos consistente al especificar a través de la documentación qué métodos tienen restricciones de subprocesamiento: más comúnmente, los métodos que deben ejecutarse en Swing EDT. De manera similar, sería útil conocer y proporcionar una conciencia estática en la cual (de nuestros clientes personalizados) se notifique en la EDT por especificación.

Entonces, me di cuenta de que esto debería ser algo que podría hacerse cumplir fácilmente utilizando anotaciones. Y he aquí, existe al menos una herramienta de análisis estático, CheckThread , que utiliza anotaciones para lograr esto. Parece que le permite declarar que un método está limitado a un subproceso específico (más comúnmente el EDT), y marcará los métodos que intentan llamar a ese método sin declararse como confinados a ese hilo.

Entonces, en la superficie, esto parece ser una adición de poca ganancia y gran ganancia al ciclo de generación y origen. Mis preguntas son:

  • ¿Existen historias de éxito para las personas que usan CheckThread o bibliotecas similares para imponer restricciones de subprocesos? ¿Alguna historia de fracaso? ¿Por qué tuvo éxito / fracasó?
  • ¿Esto es bueno en teoría? ¿Hay desventajas teóricas?
  • ¿Es esto bueno en la práctica? ¿Vale la pena? ¿Qué tipo de valor ha entregado?
  • Si funciona en la práctica, ¿cuáles son las buenas herramientas para apoyar esto? Acabo de encontrar CheckThread pero admito que no estoy completamente seguro de lo que estoy buscando para encontrar otras herramientas que hagan lo mismo.

Sé que lo correcto para nosotros depende de nuestro escenario. Pero nunca he oído hablar de personas que usen algo como esto en la práctica, y para ser sincero, no parece haberse hecho mucho con una navegación general. Así que me pregunto por qué.


Según lo solicitado, esto no se refiere específicamente a Java o EDT, pero he visto buenos resultados con los verificadores de análisis estático de concurrencia de Coverity para C / C ++. Tenían una tasa de falsos positivos más alta que las verificaciones menos complicadas, pero los propietarios del código parecían estar dispuestos a tolerar eso, dado lo difícil que pueden ser los errores de subprocesamiento para encontrar a través de las pruebas. Los detalles son confidenciales, me temo, pero los documentos públicos de Dawson Engler (por ejemplo, "Bugs as Deviant Behavior") son muy buenos en el enfoque general de "Las siguientes" N "instancias de su código hacen" X "antes de hacer" Y »,; esta instancia no lo hace ".


No hemos probado ninguna herramienta de análisis estático, pero hemos utilizado AspectJ para escribir un aspecto simple que detecta en tiempo de ejecución cuando se invoca cualquier código en java.awt o javax.swing fuera de la EDT. Se han encontrado varios lugares en nuestro código que faltaban un SwingUtilities.invokeLater() . Ejecutamos con este aspecto habilitado a lo largo de nuestro ciclo de control de calidad, luego lo apagamos poco antes del lanzamiento.