27

Ayudo a mantener y construir sobre una GUI Swing bastante grande, con mucha interacción compleja. A menudo me encuentro arreglando errores que son el resultado de que las cosas entren en estados extraños debido a alguna condición de carrera en algún otro lugar del código.Análisis de hilos estáticos: ¿Buena idea?

A medida que la base de código aumenta, he descubierto que ha sido menos consistente al especificar mediante la documentación qué métodos tienen restricciones de enhebrado: lo más común es que se deben ejecutar en Swing EDT. Del mismo modo, sería útil conocer y proporcionar conocimiento estático en el que (de nuestros auditores personalizados) se notifican en el EDT por especificación.

Así que se me ocurrió que esto debería ser algo que podría aplicarse fácilmente mediante el uso de 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 se limita a un hilo específico (más comúnmente el EDT), y marcará los métodos que intentan llamar a ese método sin declarar que están confinados a ese hilo.

Así que en la superficie, esto parece una adición de poca ganancia y gran dolor a la fuente y el ciclo de construcción. Mis preguntas son:

  • ¿Hay alguna historia de éxito para las personas que usan CheckThread o bibliotecas similares para hacer cumplir las restricciones de subprocesos? ¿Alguna historia de fracaso? ¿Por qué tuvo éxito/falló?
  • ¿Esto es bueno en teoría? ¿Hay desventajas teóricas?
  • ¿Esto es bueno en la práctica? ¿Vale la pena? ¿Qué tipo de valor tiene 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 del todo seguro de lo que estoy buscando para encontrar otras herramientas que hagan lo mismo.

Sé que es correcto para nosotros depende de nuestro escenario. Pero nunca he oído hablar de personas que usan algo como esto en la práctica, y para ser honesto, no parece haberse apoderado de una navegación general. Entonces me pregunto por qué.

+0

Por cierto, me encantaría escuchar una discusión relevante, incluso si no pertenece específicamente a Java o al EDT. –

+0

No sé lo suficiente sobre Swing porque paso todo mi tiempo en tierra servlet ... pero la única forma de escribir servlets que son seguros para hilos más que solo suerte es tener mucho cuidado al diseñar cosas que siguen ciertos patrones comunes . Si algo así te anima a limpiar tu código, entonces genial. Pero creo que, en cierto modo, el hilo de seguridad tiene que venir de una codificación cuidadosa. Sin embargo, esto puede ser una buena solución temporal en una base de código existente. – bwawok

+0

No estoy seguro de que me guste la idea de recompensas para wiki de la comunidad, pero esto no está recibiendo mucha atención, así que estoy comenzando uno de todos modos. Realmente no puedo decirle los criterios exactos que usaré para determinar una respuesta correcta en este momento. –

Respuesta

8

Esta respuesta se centra más en el aspecto de la teoría de su pregunta.

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

  • ¿De dónde provienen tales afirmaciones?
  • ¿Pueden los analizadores estáticos verificarlos?
  • ¿De dónde saca un analizador estático?

En su mayoría, tales 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 DBC solo superan el actual estado del programa (C afirman macro) y realmente deberían estar sobre los estados pasado y futuro de los programas ("aserciones temporales" "), e., g.," Esta rutina asignará un bloque de almacenamiento, y eventualmente un trozo de código lo desasignará ". Uno puede construir herramientas que intenten determinar heurísticamente cuáles son las afirmaciones (por ejemplo, el trabajo de inducción de aserción de Engler, mientras que otros han trabajado en esta área). Eso es útil, pero los falsos positivos son un problema. Como cuestión práctica, pedirle a los diseñadores que codifiquen tales afirmaciones no parece particularmente oneroso, y es realmente una buena documentación a largo plazo. Ya sea que se codifican tales afirmaciones con un "Contrato" construcción de un lenguaje específico, o con una declaración si ("si depuración & & No (afirmación) luego no();") u ocultarlos en una anotación es realmente sólo una asunto de conveniencia Es agradable cuando el lenguaje permite codificar dichas afirmaciones directamente.

La comprobación de tales afirmaciones de forma estática es difícil. Si se queda solo con el estado actual, el analizador estático tiene que hacer un análisis de flujo de datos completo de toda su aplicación, porque la información necesaria para satisfacer la afirmación probablemente provenga de datos creados por otra parte de la aplicación. (En su caso, la señal de "EDT interno" tiene que venir de analizar 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 también necesita algún tipo de lógica de verificación de espacio de estado; estas son todavía herramientas de investigación. Incluso con toda esta maquinaria, los analizadores estáticos generalmente tienen que ser "conservadores" en sus anlayses; si no pueden demostrar que algo es falso, tienen que suponer que es cierto, debido al problema de detención.

¿De dónde saca estos analizadores? Dada toda la maquinaria necesaria, son difíciles de construir, por lo que debe esperar que sean raros. Si alguien ha construido uno, genial. Si no ... como regla general, no quiere hacer esto usted mismo desde cero. La mejor esperanza a largo plazo es contar con maquinaria genérica de análisis de programas para construir tales analizadores y amortizar el costo de construir toda la infraestructura. (Construyo bases de herramientas de analizador de programas, vea nuestro DMS Software Reengineering Toolkit).

Una forma de hacer que sea más fácil construir tales analizadores estáticos es restringir los casos que manejan a un alcance reducido, por ejemplo, CheckThread. Esperaría que CheckThread hiciera exactamente lo que hace actualmente, y es poco probable que se vuelva mucho más fuerte.

La razón por la que las macros "assert" y otras comprobaciones dinámicas de "estado actual" son populares es porque realmente se pueden implementar 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 conduce a una condición fallida. Entonces, para el análisis dinámico, la ausencia de falla detectada no es realmente evidencia de corrección. Todavía se siente bien.

Conclusión: los analizadores estáticos y los analizadores dinámicos tienen su propia fuerza.

+0

Esta es una buena explicación sobre cómo implementar una herramienta de análisis estático. Afortunadamente, parece que ya existen. ¿Puedes comentar sobre lo que quieres decir con "restringir los casos que manejan" cuando se habla de CheckThread? –

+0

En cuanto a las herramientas de análisis de tiempo de ejecución que no ejercitan todos los caminos ... ese también fue mi pensamiento. Además, equipar esto para una verificación en tiempo de ejecución es comparativamente difícil a menos que use la inyección de código byte. Si desea asegurarse de que cada método Swing se llame desde el EDT, debe agregar un control de tiempo de ejecución a cada método que ** llame ** a cualquier método Swing. Con el análisis estático (específicamente CheckThread) puede configurar la herramienta para que trabaje backwords automáticamente desde esos métodos. Incluso viene con eso preconfigurado ya que Swing es constante en todos los proyectos. –

+0

@Mark Peters: No conozco específicamente los límites de CheckThread. Mis comentarios sobre restringir lo que hacen las herramientas se basan en ver muchas herramientas creadas a lo largo de los años por otros y por mí mismo; cuando limita su alcance, ¡maximiza sus posibilidades de éxito! Por lo general, cuando tiene que construir la infraestructura desde cero, está * forzado * a limitar su alcance, porque la infraestructura no puede brindarle suficiente soporte. –

3

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 del EDT. Ha encontrado varios lugares en nuestro código que faltaban un SwingUtilities.invokeLater(). Funcionamos con este aspecto habilitado a lo largo de nuestro ciclo de control de calidad, luego lo apagamos poco antes del lanzamiento.

+0

El análisis del tiempo de ejecución parece algo bueno también, si se combina con pruebas exhaustivas. Debo admitir que cuando tenía cosas en desarrollo en el pasado, pirateaba un método 'checkEDT()' que eliminaba las apariciones antes de confirmar. Supongo que la realidad es que las condiciones de carrera no cambian la cuestión de si el código se estaba ejecutando o no en el hilo correcto. Entonces, ¿esto es suficiente y el análisis estático no ofrece resultados mucho mejores? (Excepto por la documentación y el cumplimiento) –

+1

Esa es una gran idea para el control utilizando aspectJ. ¿Es posible compartir el código? – Jayan

+0

@Jayan: No, lo siento, no tengo permitido compartir el código. –

2

Como solicité, esto no pertenece específicamente a Java o al EDT, pero he visto buenos resultados con las comprobaciones de análisis estático de coincidencia de Coverity para C/C++. Tenían una tasa de falsos positivos más alta que las damas menos complicadas, pero los propietarios del código parecían dispuestos a soportar eso, dado cuán difícil es encontrar errores mediante 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 tu código hacen« X »antes de hacer« Y »,; esta instancia no ".

+0

Disfruto leyendo ese artículo, y he echado un vistazo a lo que ofrece Coverity en términos de concurrencia. La investigación de Engler es interesante porque se esfuerza por identificar errores sin conocimiento previo del sistema ("reglas" del sistema, etc.). No sé cómo me siento al respecto, ya que parte del encanto de una solución que utiliza anotaciones (por lo tanto, incorpora las "reglas" en el código) era que obligaría a la documentación y al mismo tiempo evitaría errores. Sin embargo, esta filosofía presenta una forma fácil de encontrar errores sin tener que crear primero esas reglas explícitas. Es barato de implementar. –

Cuestiones relacionadas