You are on page 1of 14

Captulo 6.

Excepciones y manejo de excpeciones


6 Excepciones y manejo de excepciones............................................................................................ 2 6.1 Manejo de excepciones en los lenguajes de tiempo real primitivos _................................... 2 6.1.1 Retorno de un valor inusual......................................................................................... 2 6.1.2 Bifurcacin forzada ..................................................................................................... 3 6.1.3 goto no local ................................................................................................................ 3 6.1.4 Variable de procedimiento........................................................................................... 3 6.2 Manejo de excepciones moderno ........................................................................................ 4 6.2.1 Excepciones y su representacion ................................................................................. 4 6.2.2 El dominio de un manejador de excepciones .............................................................. 5 6.2.3 Propagacin de excepciones........................................................................................ 6 6.2.4 Modelo de reanudacin y modelo ............................................................................... 7 6.3 Bloques de recuperacin y excepciones ............................................................................ 11

Rafael lvarez Garca ltima revisin 03-12-07 rafpalvarez@gmail.com Nota importante:


Este documento no pretende reemplazar al material propuesto por la UNED para la asignatura Sistemas en Tiempo Real. Cualquier sugerencia, comentario o correccin sobre este documento, envelo a rafpalvarez@gmail.com para poder realizar los cambios necesarios.

6 Excepciones y manejo de excepciones


Existen ciertos requisitos generales para cualquier mecanismo de manejo de excepciones: (Rl) Como ocurre con cualquier aspecto de un lenguaje, el mecanismo debe ser fcil de comprender y utilizar. (R2) El cdigo de manejo de excepciones no debiera complicar el aspecto del programa hasta el punto de obscurecer la comprensin de la operacin normal del programa (sin errores). (R3) El mecanismo deber disearse de modo que solo suponga una sobrecarga en la ejecucin cuando se maneja una excepcin. Aunque la mayora de las aplicaciones requieren que las prestaciones de un programa que use excepciones no se vea afectada negativamente bajo condiciones normales, no tiene por que ser siempre as. Bajo ciertas circunstancias, en concreto cuando sea primordial la rapidez en la recuperacin, una aplicacin puede estar preparada para tolerar cierta pequea sobrecarga en una operacin normal libre de errores. (R4) El mecanismo deber permitir un tratamiento uniforme de las excepciones detectadas tanto por el entorno como por el programa. Por ejemplo, una excepcin como el desbordamiento aritmtico, que es detectada por el hardware, debiera ser manejada exactamente de la misma forma que una excepcin generada por el programa como resultado de un fallo en una asercin. (R5) El mecanismo de excepciones deber permitir la programacin de acciones de recuperacin.

6.1 Manejo de excepciones en los lenguajes de tiempo real primitivos


Aunque los trminos excepcin y manejo de excepciones se han puesto de moda solo recientemente, se refieren simplemente a una forma de programar para contener y manejar las situaciones de error. En consecuencia, la mayora de los lenguajes de programacin disponen de medios para manejar al menos algunas excepciones. Esta seccin describe brevemente estos mecanismos en relacin con los requisitos mencionados anteriormente.

6.1.1 Retorno de un valor inusual


Una de las formas ms primitivas de mecanismo de manejo de excepciones es el retorno de un valor inusual o retorno de error desde un procedimiento o una funcin. Su principal ventaja es que es simple, y que para su implementacin no precisa de ningn mecanismo nuevo en el lenguaje. C soporta este modo, y se usa as: Como puede verse, aunque presenta el requisito de simplicidad (Rl) y permite programar acciones para la recuperacin (R5), no satisface (R2), (R3) y (R4). El cdigo se complica, supone una sobrecarga, y no queda claro como manejar los errores detectados desde el entorno. A lo largo de este libro, C se utiliza junto con POSIX. Las condiciones de error en POSIX se indican con valores de retorno distinto de cero (cada uno con un nombre simblico). Por seguridad, al llamar a una funcin del sistema debiera comprobarse el valor devuelto para ver si se ha producido un error inesperado; aunque, como se puede ver, se obscurece la estructura del cdigo. 2

Por esto, y con fines pedaggicos, en este libro se emplea una interfaz de POSIX estilizada. Para cada llamada POSIX al sistema, se supone definida una macro que efectuara la comprobacin del error. Por ejemplo, una llamada al sistema como sys_call que tome un parmetro, tendr definida la siguiente macro,
tdefine SYS_CALL(A) if(syscall(A) != 0} error()

donde error es una funcin que efecta el procesamiento del error. As, en la llamada solo aparecer SYSCALL (param).

6.1.2 Bifurcacin forzada


En los lenguajes de ensamblado, la forma usual de manejar excepciones en subrutinas es sobrepasar el retomo. Es decir, se sobrepasa la instruccin inmediatamente posterior a la llamada a la subrutina para indicar la presencia (o ausencia) de errores. Para ello, la subrutina incrementa su direccin de retorno (contador de programa) en el tamao de una instruccin de salto simple con el fin de indicar un retorno sin (o con) error. En caso de que sea posible ms de un retorno excepcional, la subrutina presupondr que tras la llamada hay varias instrucciones de salto, y manipulara convenientemente el contador de programa. En el caso de un retorno normal, la subrutina incrementara la direccin de retorno en dos instrucciones jmp. Si bien esta practica implica poca sobrecarga (R3) y permite programar acciones de recuperacin (R5), obscurece la estructura del programa, violando as los requisitos (Rl) y (R2). Tampoco puede satisfacerse (R4).

6.1.3 goto no local


Una versin de la bifurcacin forzada sobre un lenguaje de alto nivel requerira pasar como parmetros las diferentes etiquetas, o disponer de variables de etiqueta estndar (una variable de etiqueta es un objeto al que se le puede asignar una direccin del programa y que puede ser usado para transferir el control). RTL/2 es un caso de lenguaje de tiempo real primitivo que proporciona esta posibilidad bajo la forma de un goto no local. RTL/2 emplea bricks (ladrillos) para estructurar sus programas: un brick pueden ser datos (delimitado por data. . . enddata), un procedimiento (de limitado por proc. . . endproc), o una pila (indicada por stack). El sistema define un tipo especial de brick de datos llamado data svc, Uno de estos brick's (rrerr) proporciona un mecanismo estndar de manejo de errores que incluye una variable de etiqueta de error llamada er 1. Vemos que cuando se emplea de esta forma, el goto es mas que un simple salto: implica un retorno anormal de un procedimiento, por lo que habr que desapilar datos de la pila hasta restaurar el entorno del procedimiento que contiene la declaracin de la etiqueta. La penalizacin por el desapilamiento se aplica solo cuando aparece un error, por lo que se satisface el requisito (R3), Aunque el empleo de goto es muy flexible (satisfaciendo R4 y R5), puede desembocar en programas muy obscuros. En consecuencia, no satisface los requisitos (Rl y R2).

6.1.4 Variable de procedimiento


En RTL/2, la etiqueta de error suele emplearse con los errores irrecuperables, recurrindose a una variable de procedimiento de error cuando el control debe retornar al punto donde se origino el error.

De nuevo, el principal reproche que puede hacerse a esta aproximacin es que los programas pueden volverse muy difciles de entender y de mantener.

6.2 Manejo de excepciones moderno


En las aproximaciones tradicionales al manejo de excepciones suele ocurrir que el cdigo de manejo se entremezcla con el flujo normal de ejecucin del programa. Actualmente, se tiende a incluir funcionalidades de manejo de excepciones directamente en el lenguaje, resultando un mecanismo mas estructurado. La naturaleza exacta de estas facilidades vara de un lenguaje a otro, aunque se pueden identificar algunas formas de proceder comunes. En las secciones siguientes se discuten estos mecanismos.

6.2.1 Excepciones y su representacin


Sabemos que hay dos tcnicas principales para detectar errores: deteccin desde el entorno y desde la aplicacin. Tambin, segn el retraso en la deteccin del error, puede ser preciso generar la excepcin sncrona o asncronamente. La excepcin sncrona se genera como respuesta inmediata a una operacin inapropiada de un fragmento de cdigo. La excepcin asncrona se genera cierto tiempo despus de que ocurra la operacin que da lugar a la aparicin del error. Puede generarse desde el proceso que ejecuto la operacin originalmente, o en otro proceso. En consecuencia, existen cuatro clases de excepciones;

Detectada por el entorno y generada sincrnicamente, como por ejemplo una violacin de los lmites de un array o una divisin por cero. Detectada por la aplicacin y generada sncronamente, como el fallo en la comprobacin de un aserto definido en el programa. Detectada por el entorno y generada asincrnicamente, como en las excepciones generadas como resultado de un fallo de alimentacin o en algn mecanismo de monitorizacin vital. Detectada por la aplicacin y generada asincrnicamente, como cuando un proceso verifica una condicin de error que ocasionara que un proceso no cumpla los plazos o no termine correctamente,

Las excepciones asncronas suelen recibir el nombre de notificaciones asncronas o seales, y se suelen emplear en el contexto de la programacin concurrentes Existen diversas formas de declarar las excepciones sncronas. Por ejemplo: Mediante un nombre constante que necesita ser declarado explcitamente. Mediante un objeto de cierto tipo que podr o no ser declarado explcitamente.

Ada precisa que las excepciones se declaren como constantes; por ejemplo, las excepciones que puede generar el entorno de ejecucin de Ada se declaran en el paquete Standard:

package Standard is Constraint_Error : excepcin.;

ProgramJBrror : exception; Stor.ageJError : exception; Tasking.Error : exception; end Standard;

Este paquete es visible para todos los programas Ada. Java y C++ dan una visin de las excepciones mas orientada al objeto. En Java, las excepciones son instancias de una subclase de la clase Throwable, que podrn ser lanzadas (thrown) por el sistema de ejecucin y por la aplicacin. En C++, se pueden lanzar excepciones de cualquier tipo de objetos sin declararlas previamente.

6.2.2 El dominio de un manejador de excepciones


En el programa, puede que haya diversos manejadores para una misma excepcin. Cada manejador lleva asociado un dominio que especifica la regin del computo es la cual se activara dicho manejador si aparece la excepcin. La resolucin con la que pueda especificarse el dominio determinara el grado de precisin de la localizacin del origen de la excepcin. En lenguajes estructurados por bloques, como Ada, el dominio suele ser el bloque. Por ejemplo, considere un sensor de temperatura cuyo valor esta en el rango de 0 C a 100 C. El siguiente bloque Ada establece que la temperatura es un valor entero entre 0 y 100. Si el valor asignado cae fuera del rango, el sistema de apoyo de ejecucin de Ada genera una excepcin Constraint_Error. La invocacin del manejador asociado permite efectuar la necesaria accin correctiva.
declare subtype Temperatura is Integer range 0 .. 100; begin -- lee el sensor de temperatura y calcula su valor exception -- manejador para Constraint_Srror end;

Donde los bloques son la base de otras unidades, como pueden ser procedimientos o funciones, el dominio de un manejador de excepciones suele ser esa unidad. En algunos lenguajes, como Java, C++ o Modula-3, no todos los bloques pueden tener manejadores de excepciones, por lo que habr que declarar explcitamente el dominio de cada manejador de excepciones, a partir de lo cual se considerara que el bloque esta vigilado (guarded); en Java, esto se indica usando un bloque try:
try { .

// sentencias que pueden generar una excepcin catch (ExceptionType e) { // manejador para e

Puesto que el dominio de un manejador de excepciones especifica donde puede haberse generado el error, podra ocurrir que el bloque 5

presentara adecuada.

una

granularidad

El problema para el manejador es decidir que calculo provoco que se generara la excepcin, Aun hay mas problemas si consideramos desbordamientos o subdesbordamientos aritmticos. Una solucin del problema, con dominios de manejo de excepciones basados en bloques, sera disminuir el tamao del bloque y/o anidarlo. Otra alternativa es crear procedimientos con manejadores para cada uno de los bloques anidados. Sin embargo, y de cualquier modo, es una solucin demasiado prolija y tediosa. Otra solucin es permitir el manejo de excepciones a nivel de sentencia. La mejor aproximacin a este problema es permitir el paso de parmetros junto a las excepciones. Java lo hace automticamente, dado que una excepcin es un objeto, y en consecuencia puede contener tanta informacin como desee el programador. Por contra, Ada proporciona un procedimiento predefinido Exception_Informatin que devuelve detalles definidos en la implementacin sobre la aparicin de la excepcin.

6.2.3 Propagacin de excepciones


ntimamente asociado al concepto de dominio de una excepcin, se encuentra la nocin de propagacin de una excepcin. Hasta el momento se daba por sentado que si un bloque o un procedimiento generaban una excepcin, haba un manejador asociado a ese bloque o procedimiento. Sin embargo, no siempre es este el caso. Hay dos formas posibles de proceder cuando no existe un manejador de excepciones inmediato. La primera de ellas es entender la ausencia de manejador como un error del programador, que deber notificarse en tiempo de compilacin. Sin embargo, puede ocurrir que cierta excepcin generada en un procedimiento solo pueda ser manejada apropiadamente en el contexto del procedimiento que lo invoca. En estos casos, no es posible incluir el manejador junto al procedimiento. Por ejemplo, una excepcin generada en un procedimiento al fallar cierta asercin sobre los parmetros, y que solo puede manejarse en el contexto de la llamada. Desgraciadamente, el compilador no siempre puede comprobar si el contexto de llamada incluye los manejadores de excepciones adecuados, a no ser que efecte un complejo anlisis del control del flujo, Esto es especialmente difcil cuando el procedimiento llama a otros procedimientos que tambin pueden generar excepciones. Por esto, los lenguajes que precisan generacin de errores en tiempo de compilacin requieren que los procedimientos especifiquen que excepciones pueden generar (es decir, cuales no manejaran localmente). As, el compilador podr comprobar si el contexto de llamada dispone del manejador apropiado, y generara, si es preciso, el mensaje de error correspondiente. Java y C++ permiten definir que excepciones generara una funcin. Sin embargo, no requieren que haya un manejador disponible en el contexto de llamada. La segunda va, que puede ser seguida cuando no puede localizarse un manejador de excepciones, es buscar manejadores en el pasado de la cadena de invocaciones el tiempo de ejecucin; esto se conoce como propagacin de excepciones. Ada y Java permiten la propagacin de excepciones, as como C++ y Modula-2/3. Cuando el lenguaje precisa declarar el alcance de las excepciones, puede aparecer un problema potencial con la propagacin. Bajo ciertas condiciones puede que una excepcin se propague fuera 6

de su alcance, haciendo imposible su captura por un manejador. Para hacer frente a esta situacin, la mayora de los lenguajes proporcionan un manejador atrapa todo (catch all). Una excepcin sin manejar provoca que un programa secuencial sea abortado. Normalmente, si el programa consta de mas de un proceso y alguno de ellos no maneja cierta excepcin que hubiera generado, se abortara ese proceso. Sin embargo, no queda claro si se debiera propagar la excepcin al proceso padre. Otra cuestin sobre la propagacin de excepciones es si los manejadores se asocian a las excepciones dinmica o estticamente. La asociacin esttica se efecta en la compilacin, por lo que no permite la propagacin, al no conocerse la cadena de procedimientos invocadores. La asociacin dinmica se realiza en ejecucin, y consecuentemente permite la propagacin. Aunque la asociacin dinmica es ms flexible, conlleva mas sobrecarga en la ejecucin donde haya que buscar el manejador correspondiente; en la asociacin esttica es posible generar la direccin del manejador en la compilacin.

6.2.4 Modelo de reanudacin y modelo


Una cuestin crucial en cualquier mecanismo de manejo de excepciones es si el que efecta la llamada a la excepcin debiera continuar su ejecucin tras el manejo de la excepcin. Si el invocador pudiera continuar, y suponiendo que el manejador resolviera el problema que causo la generacin de la excepcin, ser posible que pudiera reanudar su trabajo como si nada hubiera pasado. Esto se conoce como modelo de reanudacin o de notificacin. El modelo en el que no se devuelve el control al invocador se denomina de terminacin o de escape. Obviamente, cabe pensar en un modelo donde el manejador pudiera decidir si reanudar la operacin que provoco la excepcin, o terminar la operacin. Este modelo se denomina hibrido.

6.2.4.1 Modelo de reanulacin


Para ilustrar el modelo de reanulacin, considere tres procedimientos (P, Q y R). El procedimiento P invoca Q, que a su vez invoca R. El procedimiento R genera una excepcin (r) que es manejada por Q, asumiendo que no hay un manejador local en R. El manejador para r es Hr. Mientras maneja r, Hr genera la excepcin q, que es manejada por Hq en el procedimiento P (el invocador de Q). Una vez manejada q, Hr contina su ejecucin, y despus continua R. El diagrama de la Figura 6.1 representa esta secuencia de eventos con los arcos numerados de 1 a 6. El modelo de reanudacin se entiende mucho mejor si contemplamos el manejador como un procedimiento que se invoca implcitamente al generar la excepcin. El problema de esta aproximacin es la dificultad a la hora de reparar errores generados por el entorno de ejecucin. Por ejemplo, un desbordamiento aritmtico en medio de una secuencia compleja de expresiones podra ocasionar que varios de los registros contuvieran evaluaciones parciales. Al llamar al manejador, es probable que se sobrescriban dichos registros. Tanto el lenguaje Pearl como el lenguaje Mesa proporcionan un mecanismo que permite al manejador volver al contexto donde se genero la excepcin. Ambos lenguajes soportan tambin el modelo de terminacin. Puesto que es difcil implementar un modelo de reanudacin estricto, se puede tomar el compromiso de reejecutar el bloque asociado con el manejador de la excepcin. El lenguaje Eiffel (Meyer, 1992) lo permite mediante retry (reintenta), como parte de su modelo de manejo de excepciones. El manejador puede establecer un indicador local que indique que se ha producido un 7

error, y el bloque podr comprobar dicho indicador. Observe que para que funcione este esquema, las variables locales del bloque no deben ser reinicializadas en el reintento. Cuando realmente se nota la ventaja del modelo de reanudacin es cuando la excepcin ha sido generada asincronamente, y por tanto tiene poco que ver con la ejecucin actual del proceso.

Figura 6.1. El modelo de reanudacin.

6.2.4.2 Modelo de terminacin


En el modelo de terminaci6n, tras llamar al manejador para que atienda cierta excepcin, el control no retorna al punto donde apareci la excepcin, sino que se finaliza el bloque o procedimiento que contiene el manejador y se pasa el control al bloque o al procedimiento de llamada. De este modo, un procedimiento invocado puede terminar en varias condiciones de terminacin, siendo una de ellas la condicin normal, y las restantes condiciones de excepcin. Cuando el manejador esta en un bloque, tras el manejo de la excepcin se devuelve el control a la primera sentencia que sigue al bloque. Con los procedimientos, al contrario que con los bloques, el control del flujo puede cambiar drsticamente, tal y como muestra la Figura 6.2. De nuevo, el procedimiento P ha invocado al procedimiento Q, que a su vez ha invocado al procedimiento R. Se ha generado una excepcin en R y se ha manejado en Q.

Ada, Java y C++ tienen todos el modelo de terminacin de manejo de excepciones.

10

6.3 Bloques de recuperacin y excepciones


En esta seccin, se describe la implementacin de los bloques de recuperacin mediante excepciones y manejadores de excepciones. Como recordatorio, se muestra a continuacin la estructura de un bloque de recuperacin: ensures <test de aceptacion> by . <modulo primario> else by <modulo alternativo> else by <modulo alternativo> ... ... else by <modulo alternativo> else error El mecanismo de deteccin de error viene dado por el test de aceptacin. Esta prueba es simplemente la negacin de una prueba que generara una excepcin empleando una recuperacin de error haca delante. El nico problema es la implementacin de la salvaguardia del estado y su restauracin correspondiente. En el ejemplo siguiente, se muestra como un paquete Ada implementa una cache de recuperacin. El procedimiento Guarda almacena el estado de las variables locales y globales del programa en una cach de recuperacin; esto no incluye los valores del contador del programa, el puntero de pila y otros. Una llamada a Restaura asegura restablecer las variables del programa a los estados guardados.
package Ca.che_Recuperacin is

procedure Restaura;

Guarda; procedure

end Cache_Recuperacion;

Obviamente, hay algo de magia en este paquete que requerir la ayuda del sistema de ejecucin y, posiblemente, incluso el apoyo del hardware para la cache de recuperacin. Asimismo, puede que no sea esta la forma ms eficiente de efectuar la restauracin de un estado. Sera preferible proporcionar primitivas mas bsicas, y permitir que el programa emplee su conocimiento de la aplicacin para optimizar la cantidad de informacin salvaguardada. La finalidad del prximo ejemplo es mostrar que es posible usar bloques de recuperacin con las tcnicas dadas de la implementacin de la cache de recuperacin en un entorno de manejo de excepciones. El esquema de bloque de recuperacin puede adems implementarse usando un lenguaje con excepciones, contando adems con un poquito de ayuda por parte del sistema de soporte de eje11

cucin. Por ejemplo, en Ada la estructura para un bloque de recuperacin con triple redundancia podra ser:
procedure Bloque,_Recuperacin is Fallo_Primario, Fallo_Secundario, Fallo_Terciario : exception; Fallo_Bloque_Recuperacin : exception; type Modulo is (Primario, Secundario, Terciario); functin Test_Aceptacin return Boolean is begin --- cdigo del test de aceptacidon end Test_Aceptacion; procedure Primario is begin -- cdigo del algoritmo primario if not Test_Aceptacion then raise Fallo_Primario; end if; exception when Fallo_Primario => -- recuperacin haca delante para devolver -- el entorno al estado requerido raise; when others => -- error inesperado -- recuperacin haca delante para devolver -- el entorno al estado requerido raise Fallo_Primario; end Primario; procedure Secundario is begin -- cdigo del algoritmo secundario if not Test_Aceptacion then raise Fallo_Secundario; end if; exception when Fallo_Secundario => -- recuperacin haca delante para devolver -- el entorno al estado requerido raise; when others =>

12

-- error inesperado -- recuperacin haca delante para devolver -- el entorno al estado requerido raise Fallo_Secundario; end Secundario; procedure Terciario is begin -- cdigo del algoritmo secundario if not Test_Aceptacion then raise Fallo_Terciario; end if; exception when Fallo_Terciario => -- recuperacin haca delante para devolver -- el entorno al estado requerido raise; when others => -- error inesperado -- recuperacin haca delante para devolver -- el entorno al estado requerido raise Fallo_Terciario; end Terciario; begin Cache_Recuperacin.Guarda; for Intento in Modulo loop begin case Intento is when Primario => Primario; exit; when Secundario ~> Secundaria; exit; when Terciario => Terciario; end case; exception when FalloPrimario => Cache_Recuperacin.Restaura; when Fallo_Secun.dario => Cache_Recuperacin. Restaura ; when Fallo_Terciario => Cache_Recuperacin.Restaura; raise Fallo_Bloque_Recuperacion; when others => Cache_Recuperacin.Restaura; raise FalloBloque_Recuperacion;

13

end; end loop; end Bloque_Recuperacion;

14

You might also like