Exception handling

From Wikipedia, the free encyclopedia

Exception handling is a programming language construct or computer hardware mechanism designed to handle the occurrence of some condition that changes the normal flow of execution. The condition is called an exception. Exceptions are used only for signaling error (exceptional) conditions. For signaling conditions that are part of the normal flow of execution see the concepts of signal and event handler.

In general, current state will be saved in a predefined location and execution will switch to a predefined handler. Depending on the situation, the handler may later resume the execution at the original location, using the saved information to restore the original state. For example, an exception which will usually be resumed is a page fault, while a division by zero usually cannot be resolved transparently.

From the processing point of view, hardware interrupts are similar to resumable exceptions, although they are usually not related to the current program flow.

From the point of view of the author of a routine, raising an exception is a useful way to signal that the routine could not execute normally, for example when its input arguments are invalid (a zero denominator in division) or when a resource it relies on is unavailable (a missing file, or a disk error). In systems without exceptions, the routine would need to return some special error code. However, this sometimes is complicated by the semipredicate problem, in which users of the routine need to write extra code to distinguish normal return values from erroneous ones.

Contents

[edit] Exception safety

A piece of code is said to be exception-safe if run-time failures within the code will not produce ill-effects, such as memory leaks, garbled data or invalid output. Exception-safe code must satisfy invariants placed on the code even if exceptions occur. There are several levels of exception safety:

  1. failure transparency, operations are guaranteed to succeed and satisfy all requirements even in presence of exceptional situations. (best)
  2. commit or rollback semantics, operations can fail, but failed operations are guaranteed to have no side effects.[1]
  3. basic exception safety, partial execution of failed operations can cause side effects, but invariants on the state are preserved (that is, any stored data will contain valid values).
  4. minimal exception safety, partial execution of failed operations may store invalid data but will not cause a crash.
  5. no exception safety, no guarantees are made. (worst)

Usually at least basic exception safety is required. Failure transparency is difficult to implement, and is usually not possible in libraries where complete knowledge of the application is not available.

[edit] Exception support in programming languages

See also: Exception handling syntax

Many computer languages, such as Ada, C++, Common Lisp, D, Delphi, Eiffel, Java, Objective-C, Ocaml, PHP (as of version 5), Python, REALbasic, ML, Ruby, and all .NET languages have built-in support for exceptions and exception handling. In those languages, the advent of an exception (more precisely, an exception handled by the language) unwinds the stack of function calls until an exception handler is found. That is, if function f contains a handler H for exception E, calls function g, which in turn calls function h, and an exception E occurs in h, then functions h and g will be terminated and H in f will handle E.

Excluding minor syntactic differences, there are only a couple of exception handling styles in use. In the most popular style, an exception is initiated by a special statement (throw, or raise) with an exception object. The scope for exception handlers starts with a marker clause (try, or the language's block starter such as begin) and ends in the start of the first handler clause (catch, except, rescue). Several handler clauses can follow, and each can specify which exception types it handles and what name it uses for the exception object.

A few languages also permit a clause (else) that is used in case no exception occurred before the end of the handler's scope was reached. More common is a related clause (finally, or ensure) that is executed whether an exception occurred or not, typically to release resources acquired within the body of the exception-handling block. Notably, C++ lacks this clause, and the Resource Acquisition Is Initialization technique is used to free such resources instead.

In its whole, exception handling code might look like this (in pseudocode):

try
{
 line = console.readLine();
 if (line.length() == 0)
 {
  throw new EmptyLineException("The line read from console was empty!");
 }
 console.printLine("Hello %s!" % line);
}
catch (EmptyLineException e)
{
 console.printLine("Hello!");
}
catch (Exception e)
{
 console.printLine("Error: " + e.message());
}
else
{
 console.printLine("The program ran successfully");
}
finally
{
 console.printLine("The program terminates now");
}

As a minor variation, some languages use a single handler clause, which deals with the class of the exception internally.

Languages such as Perl and C don't use the term exception handling, but include facilities that allow implementing similar functionality.

[edit] Checked exceptions

The designers of Java devised[2][3] checked exceptions[4] which are a special set of exceptions. The checked exceptions that a method may raise are part of the method's signature. For instance, if a method might throw an IOException, it must declare this fact in its method signature. Failure to do so raises a compile-time error.

This is related to exception checkers that exist at least for OCaml. The external tool for OCaml is both transparent (i.e. it does not require any syntactic annotations) and facultative (i.e. it is possible to compile and run a program without having checked the exceptions, although this is not suggested for production code).

The CLU programming language had a feature with the interface closer to what Java has introduced later. A function could raise only exceptions listed in its type, but any leaking exceptions from called functions would automatically be turned into the sole runtime exception, failure, instead of resulting in compile-time error. Later, Modula-3 had a similar feature.[5] These features don't include the compile time checking which is central in the concept of checked exceptions and hasn't as of 2006 been incorporated into other major programming languages than Java.[6]

[edit] Pros and cons

Checked exceptions can, at compile time, greatly reduce (but not entirely eliminate) the incidence of unhandled exceptions surfacing at runtime in a given application; the unchecked exceptions (RuntimeExceptions and Errors) can still go unhandled.

However, some see checked exceptions as a nuisance, syntactic salt that either requires large throws declarations, often revealing implementation details and reducing encapsulation, or encourages the (ab)use of poorly-considered try/catch blocks that can potentially hide legitimate exceptions from their appropriate handlers.

Others do not consider this a nuisance as you can reduce the number of declared exceptions by either declaring a superclass of all potentially thrown exceptions or by defining and declaring exception types that are suitable for the level of abstraction of the called method,[7] and mapping lower level exceptions to these types, preferably wrapped using the exception chaining in order to preserve the root cause.

A simple throws Exception declaration or catch (Exception e) is always sufficient to satisfy the checking. While this technique is sometimes useful, it effectively circumvents the checked exception mechanism, so it should only be used after careful consideration.

One prevalent view is that unchecked exception types should not be handled, except maybe at the outermost levels of scope, as they often represent scenarios that do not allow for recovery: RuntimeExceptions frequently reflect programming defects,[8] and Errors generally represent unrecovereable JVM failures. The view is that, even in a language that supports checked exceptions, there are cases where the use of checked exceptions is not appropriate.

[edit] Exception synchronity

Somewhat related with the concept of checked exceptions is exception synchronity. Synchronous exceptions happen at a specific program statement whereas asynchronous exceptions can raise practically anywhere.[9][10] It follows that asynchronous exception handling can't be required by the compiler. They are also difficult to program with. Examples of naturally asynchronous events include pressing Ctrl-C to interrupt a program, and receiving a signal such as "stop" or "suspend" from another thread of execution.

Programming languages typically deal with this by limiting asynchronity, for example Java has lost thread stopping and resuming.[11] Instead, there can be semi-asynchronous exceptions that only raise in suitable locations of the program or synchronously

[edit] Condition systems

Common Lisp, Dylan and Smalltalk have a Condition system which encompasses the aforementioned exception handling systems. In those languages or environments the advent of a condition (a "generalisation of an error" according to Kent Pitman) implies a function call, and only late in the exception handler the decision to unwind the stack may be taken.

Conditions are a generalization of exceptions. When a condition arises, an appropriate condition handler is searched for and selected, in stack order, to handle the condition. Conditions which do not represent errors may safely go unhandled entirely; their only purpose may be to propagate hints or warnings toward the user.[12]

[edit] Continuable exceptions

This is related to the so-called resumption model of exception handling, in which some exceptions are said to be continuable: it is permitted to return to the expression that signaled an exception, after having taken corrective action in the handler. The condition system is generalized thus: within the handler of a non-serious condition (a.k.a. continuable exception), it is possible to jump to predefined restart points (a.k.a. restarts) that lie between the signaling expression and the condition handler. Restarts are functions closed over some lexical environment, allowing the programmer to repair this environment before exiting the condition handler completely or unwinding the stack even partially.

[edit] Restarts separate mechanism from policy

Condition handling moreover provides a separation of mechanism from policy. Restarts provide various possible mechanisms for recovering from error, but do not select which mechanism is appropriate in a given situation. That is the province of the condition handler, which (since it is located in higher-level code) has access to a broader view.

An example: Suppose there is a library function whose purpose is to parse a single syslog file entry. What should this function do if the entry is malformed? There is no one right answer, because the same library could be deployed in programs for many different purposes. In an interactive log-file browser, the right thing to do might be to return the entry unparsed, so the user can see it -- but in an automated log-summarizing program, the right thing to do might be to supply null values for the unreadable fields, but abort with an error if too many entries have been malformed.

That is to say, the question can only be answered in terms of the broader goals of the program, which are not known to the general-purpose library function. Nonetheless, exiting with an error message is only rarely the right answer. So instead of simply exiting with an error, the function may establish restarts offering various ways to continue -- for instance, to skip the log entry, to supply default or null values for the unreadable fields, to ask the user for the missing values, or to unwind the stack and abort processing with an error message. The restarts offered constitute the mechanisms available for recovering from error; the selection of restart by the condition handler supplies the policy.

[edit] References

  1. ^ http://www.open-std.org/jtc1/sc22/wg21/docs/papers/1997/N1077.asc
  2. ^ http://archives.java.sun.com/cgi-bin/wa?A2=ind9901&L=rmi-users&F=P&P=36083
  3. ^ http://answers.google.com/answers/threadview?id=26101
  4. ^ Java Language Specification, chapter 11.2. http://java.sun.com/docs/books/jls/third_edition/html/exceptions.html#11.2
  5. ^ http://www1.cs.columbia.edu/graphics/modula3/tutorial/www/m3_23.html#SEC23
  6. ^ http://www.mindview.net/Etc/Discussions/CheckedExceptions
  7. ^ Bloch 2001:178 Bloch, Joshua (2001). Effective Java Programming Language Guide. Addison-Wesley Professional. ISBN 0-201-31005-8. 
  8. ^ Bloch 2001:172
  9. ^ http://citeseer.ist.psu.edu/415348.html
  10. ^ Safe asynchronous exceptions for Python. http://www.cs.williams.edu/~freund/papers/02-lwl2.ps
  11. ^ http://java.sun.com/j2se/1.5.0/docs/guide/misc/threadPrimitiveDeprecation.html
  12. ^ http://www.franz.com/support/documentation/6.2/ansicl/section/conditio.htm

[edit] See also

[edit] External links