Esta será una respuesta más conceptual a por qué estas advertencias pueden existir incluso con enfoques de prueba con recursos. Desafortunadamente, tampoco es el tipo de solución fácil que esperas lograr.
La recuperación de errores no puede fallar
finally
modela un flujo de control posterior a la transacción que se ejecuta independientemente de si una transacción tiene éxito o falla.
En el caso de falla, finally
captura la lógica que se ejecuta en medio de la recuperación de un error, antes de que se haya recuperado completamente (antes de llegar a nuestro catch
destino).
Imagine el problema conceptual que presenta al encontrarse con un error en medio de la recuperación de un error.
Imagine un servidor de base de datos donde estamos tratando de confirmar una transacción, y falla a la mitad (digamos que el servidor se quedó sin memoria en el medio). Ahora el servidor quiere revertir la transacción a un punto como si nada hubiera pasado. Sin embargo, imagine que encuentra otro error en el proceso de retroceso. Ahora terminamos teniendo una transacción medio comprometida con la base de datos: la atomicidad y la naturaleza indivisible de la transacción ahora se rompe, y la integridad de la base de datos ahora se verá comprometida.
Este problema conceptual existe en cualquier lenguaje que trate con errores, ya sea C con propagación de código de error manual, o C ++ con excepciones y destructores, o Java con excepciones y finally
.
finally
no puede fallar en lenguajes que lo proporcionan de la misma manera que los destructores no pueden fallar en C ++ en el proceso de encontrar excepciones.
La única forma de evitar este problema conceptual y difícil es asegurarse de que el proceso de deshacer las transacciones y liberar recursos en el medio no pueda encontrar una excepción / error recursivo.
Entonces, el único diseño seguro aquí es un diseño donde writer.close()
posiblemente no puede fallar. Por lo general, hay formas en el diseño para evitar escenarios en los que tales cosas pueden fallar en medio de la recuperación, lo que lo hace imposible.
Desafortunadamente, es la única forma: la recuperación de errores no puede fallar. La forma más fácil de garantizar esto es hacer que esos tipos de funciones de "liberación de recursos" y "efectos secundarios inversos" no puedan fallar. No es fácil: la recuperación adecuada de errores es difícil y desafortunadamente difícil de probar. Pero la forma de lograrlo es asegurarse de que las funciones que "destruyen", "cierren", "apaguen", "retrocedan", etc., no pueden encontrar un error externo en el proceso, ya que tales funciones a menudo necesitarán ser llamado en medio de la recuperación de un error existente.
Ejemplo: registro
Digamos que quieres registrar cosas dentro de un finally
bloque. Esto a menudo será un gran problema a menos que el registro no pueda fallar . El registro casi seguro puede fallar, ya que es posible que desee agregar más datos al archivo, y eso puede encontrar fácilmente muchas razones para fallar.
Entonces, la solución aquí es hacerlo para que cualquier función de registro utilizada en los finally
bloques no pueda arrojarse a la persona que llama (podría fallar, pero no arrojará). ¿Cómo podríamos hacer eso? Si su idioma permite el lanzamiento dentro del contexto de que finalmente haya un bloque anidado de prueba / captura, esa sería una forma de evitar lanzar al interlocutor tragándose excepciones y convirtiéndolas en códigos de error, por ejemplo, tal vez el registro se puede hacer en un sitio separado proceso o subproceso que puede fallar por separado y fuera de una pila de recuperación de errores existente desenrollar Siempre que pueda comunicarse con ese proceso sin la posibilidad de encontrarse con un error, eso también sería seguro para las excepciones, ya que el problema de seguridad solo existe en este escenario si estamos lanzando recursivamente desde el mismo hilo.
En este caso, podemos salir con la falla de registro siempre y cuando no se lance, ya que no iniciar sesión y no hacer nada no es el fin del mundo (no está filtrando ningún recurso o no revierte los efectos secundarios, por ejemplo).
De todos modos, estoy seguro de que ya puedes comenzar a imaginar lo increíblemente difícil que es realmente hacer que un software sea seguro para las excepciones. Es posible que no sea necesario buscar esto en el mayor grado posible, excepto en el software más crítico. Pero vale la pena tomar nota de cómo lograr realmente la seguridad de las excepciones, ya que incluso los autores de bibliotecas de propósito muy general a menudo pierden el control y destruyen toda la seguridad de las excepciones de su aplicación usando la biblioteca.
SomeFileWriter
Si SomeFileWriter
puede arrojar dentro close
, entonces diría que generalmente es incompatible con el manejo de excepciones, a menos que nunca intente cerrarlo en un contexto que implique recuperarse de una excepción existente. Si el código está fuera de su control, podríamos ser SOL, pero valdría la pena notificar a los autores de este flagrante problema de seguridad de excepción. Si está bajo su control, mi recomendación principal es asegurarse de que cerrarlo no pueda fallar por los medios necesarios.
Imagínese si un sistema operativo realmente no puede cerrar un archivo. Ahora, cualquier programa que intente cerrar un archivo al apagarse no podrá cerrarse . ¿Qué se supone que debemos hacer ahora, simplemente mantener la aplicación abierta y en el limbo (probablemente no), simplemente filtrar el recurso del archivo e ignorar el problema (tal vez está bien si no es tan crítico)? El diseño más seguro: haga que sea imposible no cerrar un archivo.