Exception Handling
Lecture Notes for CS 190
Spring 2016
John Ousterhout
- Exceptions come from many sources:
- From above:
- Bad input from user or client
- Misconfiguration and operator errors
- From below: underlying system facilities don't work as desired:
- Disk I/O error
- Can't open file (wrong permissions, missing directory, etc.)
- Out of memory
- Network socket already in use
- From peers in a distributed system:
- Server crashes
- Slow communication
- Lost network packets
- From ourselves: internal bugs
- Errors and exceptions are a major source of complexity and bugs
- They account for a lot of code in large systems
- They disrupt normal code flow:
- They happen in the middle of other activities
- Something didn't work like you expected
- Hard to figure out how to handle them
- May not be able to complete work in progress
- Cleanup/recovery may be tricky
- Language support is clunky
- Verbose
- Makes code hard to read
- Hard to test
- Don't occur very often in running applications
- Programmers often make the exception problem worse:
- Defensive programming: throw exceptions for anything that
looks a tiny bit suspicious. More exceptions are better?
- Expediency: rather than figure out how to solve a problem,
just throw an exception, punt it to the next level
- Result: even more exceptions, many of which no-one really knows
how to handle.
- Key idea: reduce the number of places where exceptions must be
handled. Specific techniques:
- Whenever possible, define errors out of existence:
- Deleting variables in Tcl
- File deletion in Windows
- Bounds checks in Java substring method
- Mask exceptions (recover automatically so the exception doesn't
have to be reported)
- E.g., if a server crashes, automatically fetch data from
a backup server
- Or, if a server crashes, wait until it restarts
- Collapse exceptions (handle several different cases with the
same code)
- Promote one exception to another (in RAMCloud, many exceptions
get promoted to "server crash").
- Reuse existing handler
- May not work for exceptions that happen frequently
- Defer reporting to a place where other exceptions could
already happen.
- Example: in RAMCloud, report RPC exceptions only on wait,
not send.
- Or, handle exception once in a higher-level method,
rather than multiple times and lower-level methods.
- Group exceptions into categories according to how they
will be handled.
- Find a code structure were each category is only handled
in one place.
- Advantages of collapsing:
- Simplifies code (fewer cases).
- Remaining handlers get invoked more often (will get
debugged).
- Just panic (crash app)?
- If there's not a viable way to handle it
- Example: running out of memory in malloc
- Or, throw an error, which isn't handled except at the
very top level.
- Before throwing an exception, think about how the caller will
handle it.
- If you can't visualize how the caller will handle it, rethink
the exception
- Choose between throwing an exception and returning a value.
- If the caller will almost always care about the exception,
might as well report it with a return value.
- Make lots of specific information available after exceptions:
- Include in exception message?
- Additional fields of exception?
- Or, output to system log
- Assertions versus exceptions:
- Assertions are for things that should absolutely never happen in
production
- An assertion failure means there is a bug in the code
- Assertion checking can be disabled during production for highest
performance
- How much internal error checking should an application do?
- One philosophy: check everything (tons of assertions)
- I tend to use assertions fairly lightly (assume that unit tests
will flush out most bugs)
- Must always check "less trusted" input