Handling Exceptions In C & C++ [Part B] Ver 2

Preview:

DESCRIPTION

Second part of my series on Exception Handling. Talks mostly of the stuff in C++. Prepared in 2007

Citation preview

Oct 11, 2007

Handling Exceptions in C++

Dr. Partha Pratim DasInterra Systems (India) Pvt. Ltd.

PART BPART B

04/12/23 22

Agenda

• PART A– Exception Fundamentals

– Exceptions in C• C Language Features

• C Standard Library Support

– SEH in Microsoft C

– Exceptions in C++• C++ Language Features

– try–catch–throw

– Exception Specifications

• C++ Standard Library Support

04/12/23 33

Agenda

• PART B– Exception Instrumentations in C++

• How Compilers Manage Exceptional Flow?

– Designing with Exceptions in C++• Goals

• Scope

• Anatomy of a Function

• Meyers Guidelines

04/12/23 44

Agenda

• PART C– Designing with Exceptions in C++

• Analysis & Design of an Exception-safe stack

• Exception behavior of Standard Library

• Handling Exceptions in Multithreaded Environment

• TR1 Proposal

04/12/23 55

PART BPART B

04/12/23 66

Exceptions Instrumentations in C++

How compilers manage How compilers manage Exceptional FlowExceptional Flow

04/12/23 77

Exception Handling: Issues• Code Isolation

– Separate Exceptions Flow from Normal Flow

– Separate Error Reporting from Error Handling

• Efficiency– Minimal Time Overhead for Normal Flow

– Minimal Space Overhead for Normal Flow

• Optimization– Minimize Loss of code optimizations under Exceptions

• Safety– Contain the vulnerability of the Program

04/12/23 88

Function Call: Instrumentations

• Normal Flow– return

• Exceptional Flow with Stack Cutting– setjmp / longjmp

• Exceptional Flow with Stack Unwinding– try-catch-throw

04/12/23 99

Function Call: Items• Normal Call

– Stack Frame

– Context

– Finalization

• Stack Cutting– Enhanced Stack Frame

– Exception Handler Frame

• Stack Unwinding– Destructor Thunk

– EH Handler

04/12/23 1010

Function Call Items: Stack Frame

• Function parameters

• Function return address

• Frame pointer

• Local Objects

• Callee save registers

04/12/23 1111

Function Call Items: Context

• Register PC / Return Address – (eip on x86)

• Register SP / Stack Pointer – (esp on x86)

• Register FP / Frame Pointer or Base Pointer – (ebp on x86)

04/12/23 1212

Function Call Items: Finalization

• How are the right destructors called in the right order? – On Normal Exit– On Exceptional Exit

• NOTE:– This is tricky once the function has a multiple

return statements before / after a number of local object constructions

04/12/23 1313

Function Call: Normal Flow

• Caller prepares the Parameters• Caller calls the Callee• Callee saves the Context (Function Prologue)• Callee does the job• Callee restores the Context (Function Epilogue)• Callee returns• Caller cleans up the Parameters• Caller uses the return value

04/12/23 1616

Function Call: Stack Cutting

• setjmp sets the jmp_buf buffer.#define _JBLEN  16#define _JBTYPE int// Define jump buffer layout for x86 setjmp/longjmp.typedef struct __JUMP_BUFFER {    unsigned long Ebp;    unsigned long Ebx;    unsigned long Edi;    unsigned long Esi;    unsigned long Esp;    unsigned long Eip;    unsigned long Registration;    unsigned long TryLevel;    unsigned long Cookie;    unsigned long UnwindFunc;    unsigned long UnwindData[6];} _JUMP_BUFFER;typedef _JBTYPE jmp_buf[_JBLEN];

04/12/23 1717

Function Call: Stack Cutting

• longjmp forces the context (FP, SP and PC) to the jmp_buf buffer stored at setjmp point.

• Effect is – control resurfaces in setjmp and longjmp never returns.

• Stack is ‘CUT’:– Local objects are not finalized– All intervening frames are trashed

04/12/23 http://en.wikipedia.org/wiki/Thunk 1919

Function Call Items: Thunk

• A delayed computation

• Runtime registers a destructor thunk for the exception object.

• Catch handler calls the thunk at end.

04/12/23 2121

Function Call: Stack Unwinding

• Flow:– Creation of Exception object– Placement of destructor thunk for Exception object– Wrapping up of the stack frame.– Calling of Finalizers – ‘UNWIND’– Matching for Handler

• Catch handlers are statically overloaded but dynamically dispatched.

• Explain why this will need RTTI.

04/12/23 2222

Function Call: Stack Unwinding

• Flow:– Invocation of the right handler.– Exit from the handler – Invocation of the thunk if no rethrow has been

done.

04/12/23 2323

Function Call: Stack Unwinding

• Data Structures:– Stack Frame– RUNTIME_FUNCTION

– UNWIND_INFO– TRY_REGION_TABLE– CLEANUP_TABLE

04/12/23 2525

Designing with Exceptions in C++

Glimpses of Design IssuesGlimpses of Design Issues

04/12/23 2626

Designing with Exceptions: Goals

• “With Exceptions” !!!– Designing in spite of Exceptions?

– Designing with the help of Exceptions?

– Both.

04/12/23 2727

Designing with Exceptions: Goals

• Graded Goals– Do Nothing

– No Crash

– No Resource Leak

– Valid System State

– Unchanged System State

– Works ALWAYS

No Safety

Minimal Safety

Basic Safety

Strong Safety

No-Throw Safety

04/12/23 http://en.wikipedia.org/wiki/Exception_handling 2828

Exception Safety: Levels• No Exception Safety

– No guarantees are made.

– This is never acceptable in a production environment.

• Minimal Exception Safety– Partial execution of failed operations may store invalid data but

will not cause a crash.

– This may be acceptable for a graceful exit.

• Basic Exception Guarantee– Partial execution of failed operations can cause side effects

– Invariants on the state are preserved

– Any stored data will contain valid values.

04/12/23 http://en.wikipedia.org/wiki/Exception_handling 2929

Exception Safety: Levels• Strong Exception Guarantee (Commit or Rollback)

– Failed operations are guaranteed to have no side effects.

– Operations either succeed or have no effect at all.

• No-Throw Guarantee (Failure Transparency)– Operations are guaranteed to succeed and satisfy all requirements

even in presence of exceptional situations.

– Ideal; but may be unrealistic.

– Usually not possible in libraries where complete knowledge of the application is not available.

04/12/23 3030

Designing with Exceptions: Scope

• We Consider:– Function Calls

• Global Functions

• Static Methods

• Non-Static Methods

• Virtual Functions

• Operators (Overloaded)

– Objects• Automatic

• Dynamic

04/12/23 3131

Designing with Exceptions: Scope

• We do not consider:– Static Objects

– Asynchronous Exceptions

– Standard Library Objects

– STL Containers

– …

04/12/23 3232

C++ Ground Rules: Lifetime

• When does an object's lifetime begin?– When its constructor completes successfully

and returns normally.

• When does an object's lifetime end?– When its destructor begins.

• What is the state of the object after its lifetime has ended?– There is no object.

04/12/23 3333

C++ Ground Rules: Lifetime• Construction:

– An object is considered fully constructed when the control goes out of constructor.

• Destruction:– C++ refuses to call destructors for objects that haven't

been fully constructed – When an object is destructed, all fully constructed sub-

objects are destructed.

• Finalization: – Destructors for all local objects are called on exit

(except for abort(), exit() and longjmp()).

04/12/23 3434

Anatomy of a Function

• Safe Operations– Operations with built-in

types– Compiler Mechanisms

• Call, Return, Try, Throw, Catch

– Safe Functions• Unsafe Operations

– Functions– Construction– Copy Construction– Copy Assignment– Destruction– operator new / delete– …

class A { };A Viscera(

A x, // Argument CopyA& rx, A* px)

{ // Local objectsA a;A& ra = *px;A* pa = new A(rx);

try { // ...// Parameter CopyA a = // Return Value CopyViscera(a, *pa, &ra);// ...

} catch (A& e) {// Handler

} // Exception Destructor Thunk

// Exception Object Copythrow x; // Exception Exit

// Temporary Object Copyreturn a; // Normal Exit

} // Local Object Cleanup

04/12/23 3535

Exceptional Design Rules

Meyers’ Recommendations Meyers’ Recommendations on Basic Exception Safetyon Basic Exception Safety

04/12/23 More Effective C++ 3636

Exception Safety: Meyers Guidelines

• Item 9:  Use destructors to prevent resource leaks  

• Item 10:  Prevent resource leaks in constructors  

• Item 11:  Prevent exceptions from leaving destructors  

• Item 12:  Understand how throwing an exception differs from passing a parameter or calling a virtual function  

• Item 13:  Catch exceptions by reference  

• Item 14:  Use exception specifications judiciously  

• Item 15:  Understand the costs of exception handling  

04/12/23 More Effective C++, Example: PPD 3737

Meyers [9]: Use destructors to prevent resource leaks

• Situation

– A hierarchy of Polygonal objects

– Two methods:•Poly* readPoly(istream&)

– Read from input stream, and

– Create (through factory).•virtual void plotPoly()

– Object drawn on the plotter device

PolyPoly

QuadQuad TriTri

04/12/23 More Effective C++, Example: PPD 3838

Meyers [9]: Use destructors to prevent resource leaks

• Classesclass Poly { public:

virtual void plotPoly() = 0; ... };

class Quad: public Poly { public: virtual void plotPoly();

... };

class Tri: public Poly { public: virtual void plotPoly();

... };

04/12/23 More Effective C++, Example: PPD 3939

Meyers [9]: Use destructors to prevent resource leaks

• plot() for the Graphic Device (unsafe)void plot(istream& myStream) {

// while there's data while (myStream) {

// get next poly Poly *pPoly = readPoly(myStream);

// Plot the polygonpPoly->plotPoly();

// delete object that // readPoly returned delete pPoly;

} }

plotPoly() throws *pPoly leaks

04/12/23 More Effective C++, Example: PPD 4040

Meyers [9]: Use destructors to prevent resource leaks

• plot() for the Graphic Device (safe)void plot(istream& myStream) {

// while there's data while (myStream) {

// get next poly Poly *pPoly = readPoly(myStream);

try {// Plot the polygonpPoly->plotPoly();

} catch (...){

// delete object - exceptiondelete pPoly;throw; // passes on exception

}// delete object – no exceptiondelete pPoly;

} }

Code Duplication

Code Duplication

04/12/23 More Effective C++, Example: PPD 4141

Meyers [9]: Use destructors to prevent resource leaks

• Litter code with try and catch blocks. • Duplicate cleanup code

– Normal paths and – Exceptional paths. – Executes anyway!

• Move the cleanup code that must always be executed into the destructor for an object local to plot(). – Local objects are always destroyed when leaving a function,

regardless of how that function is exited.

• The solution is to replace the pointer with an object that acts like a pointer– aka, Smart Pointer

04/12/23 4242

Meyers [9]: Use destructors to prevent resource leaks

• Smart pointer – Is a C++ object

– Stores pointers to dynamically allocated (heap / free store) objects

– Improves raw pointers by implementing • Construction & Destruction

• Copying & Assignment

• Dereferencing:– operator–> – operator*

– Grossly mimics raw pointer syntax & semantics

04/12/23 More Effective C++, Example: PPD 4343

Meyers [9]: Use destructors to prevent resource leaks

• auto_ptrtemplate<class T> class auto_ptr { public:

// save ptr to object auto_ptr(T *p = 0): ptr_(p) {}

// delete ptr to object ~auto_ptr() { delete ptr_; }

// Indirection T* operator->() const { return ptr_; } ...

private: // raw ptr to object T *ptr_;

};

04/12/23 More Effective C++, Example: PPD 4444

Meyers [9]: Use destructors to prevent resource leaks

• plot() for the Graphic Device (safe)void plot(istream& myStream) {

// while there's data while (myStream) {

// get next poly auto_ptr<Poly> pPoly(readPoly(myStream));

// Plot the polygonpPoly->plotPoly();

} }

pPoly->plotPoly();

means

(pPoly.operator->())->plotPoly();

04/12/23 4545

Meyers [9]: Use destructors to prevent resource leaks

• Smart Pointers work as Holders of Resources

– RAII – Resource Acquisition is Initialization Idiom

– Scoped Acquisition – Release Paradigm• Acquires / Locks in Constructor

• Releases / Unlocks in Destructor

• Ensures safety on face of exceptions

04/12/23 More Effective C++, Example: PPD 4646

Meyers [9]: Use destructors to prevent resource leaks

• Window Handling in Windows OS (unsafe)

// This function leaks resources // if an exception is thrown void Display(const Information& info) {

HANDLE w = CreateWindow(/* Creation Parameters */);

/* Data preparations */RenderWindow(w, info, /* Display Parameters */); /* Data Cleanup */

DestroyWindow(w); }

04/12/23 More Effective C++, Example: PPD 4747

Meyers [9]: Use destructors to prevent resource leaks

• Window Holder// class for Holding (acquiring and // releasing) a window handle class WindowHolder { public:

WindowHolder(HANDLE h): w_(h) {} ~WindowHolder() { DestroyWindow(w_); }

operator HANDLE() { return w_; }private:

HANDLE w_;

// Stop free functions being available WindowHolder(const WindowHolder&); WindowHolder& operator=(const WindowHolder&);

};

04/12/23 More Effective C++, Example: PPD 4848

Meyers [9]: Use destructors to prevent resource leaks

• Window Handling in Windows OS (safe)

// This function cleans up resources - alwaysvoid Display(const Information& info) {

WindowHolder w(CreateWindow(/* Creation Parameters */));

/* Data preparations */// WindowHandle is implicitly converted to HANDLERenderWindow(w, info, /* Display Parameters */); /* Data Cleanup */

}

04/12/23 More Effective C++, Example: PPD 4949

Meyers [9]: Use destructors to prevent resource leaks

• Morale– Resources should be encapsulated inside

objects. – Usually avoids resource leaks in the face of

exceptions.

04/12/23 More Effective C++, Example: PPD 5050

More Questions

• What happens if an exception is thrown in the process of acquiring a resource, that is, in the constructor of a resource-acquiring class?

• What happens if an exception is thrown during the automatic destruction of such resources?

04/12/23 More Effective C++ 5151

Meyers [10]: Prevent resource leaks in constructors

• Consider: 

class T { ... }; class T1 { public: T1(const T&); ... }; class T2 { public: T2(const T&); ... }; class A { public:

A(const T&, const T& = (T)0, const T& = (T)0); ~A(); void f(const T&); ...

private: T m_; T1 *p1_; T2 *p2_;

};

04/12/23 More Effective C++ 5252

Meyers [10]: Prevent resource leaks in constructors

• Constructor (unsafe) / Destructor: 

A::A(const T& d, const T& s1, const T& s2):m_(d), p1_(0), p2_(0)

{if (s1 != (T)0)

p1_ = new T1(s1); // 1if (s2 != (T)0)

p2_ = new T2(s2); // 2}

A::~A(){

delete p1_;delete p2_’

}

04/12/23 More Effective C++ 5353

Meyers [10]: Prevent resource leaks in constructors

• Exception in body:– operator (T) may throw.– T::operator != may throw– T::operator new may throw bad_alloc

– Constructor for T1 or T2 may throw

• Exception at Line 1 is safe. – m_ gets destructed.

• Exception at Line 2 leaks p1_. – A::~A() does not get called as the object is not there.

A::A(const T& d, const T& s1, const T& s2):m_(d), p1_(0), p2_(0) {if (s1 != (T)0) p1_ = new T1(s1); // Line 1if (s2 != (T)0) p2_ = new T2(s2); // Line 2 }

04/12/23 More Effective C++ 5454

Meyers [10]: Prevent resource leaks in constructors

• Try Exception Fix by Dynamic Allocation– Doesn’t work as pA is never assigned if the following throws

• T::operator new

• Constructor for A

{ A *pA = 0; try {

pA = new A(d, s1, s2); ...

} catch (...) { // catch all exceptionsdelete pA; // delete pA on an exceptionthrow; // Rethrow exception

}delete pA; // delete pA normally

}

04/12/23 More Effective C++ 5555

Meyers [10]: Prevent resource leaks in constructors

• Constructor (safe) cleans up itselfA::A(const T& d, const T& s1, const T& s2):

m_(d), p1_(0), p2_(0){

try {if (s1 != (T)0)

p1_ = new T1(s1); // 1if (s2 != (T)0)

p2_ = new T2(s2); // 2} catch (...) {

delete p1_;delete p2_;throw;

}}

A::~A(){

delete p1_;delete p2_’

}

04/12/23 More Effective C++ 5656

Meyers [10]: Prevent resource leaks in constructors

• Constructor (safe): w/o code duplicationA::A(const T& d, const T& s1, const T& s2):

m_(d), p1_(0), p2_(0){

try {if (s1 != (T)0)

p1_ = new T1(s1); // 1if (s2 != (T)0)

p2_ = new T2(s2); // 2} catch (...) {

CleanUp();throw;

}}

A::~A(){

CleanUp();}

A::CleanUp(){

delete p1_;delete p2_;

}

04/12/23 More Effective C++ 5757

Meyers [10]: Prevent resource leaks in constructors

• Reconsider: 

class T { ... }; class T1 { public: T1(const T&); ... }; class T2 { public: T2(const T&); ... }; class A { public:

A(const T&, const T& = (T)0, const T& = (T)0); ~A(); void f(const T&); ...

private: T m_; T1 * const p1_; T2 * const p2_;

};

04/12/23 More Effective C++ 5858

Meyers [10]: Prevent resource leaks in constructors

• Constructor (unsafe): 

• Exception at Line 1 is safe. – m_ gets destructed.

• Exception at Line 2 leaks p1_. – A::~A() does not get called.

• No try-catch on Initializer list – only expressions.

A::A(const T& d, const T& s1, const T& s2):m_(d), p1_((s1 != (T)0)? new T1(s1): 0), // Line 1p2_((s2 != (T)0)? new T2(s2): 0) // Line 2

{ }

04/12/23 More Effective C++ 5959

Meyers [10]: Prevent resource leaks in constructors

• Constructor (safe): 

T1* A::InitT1(const T&s) {if (s != (T)0) return new T1(s);else return (T1*)0; }

T2* A::InitT2(const T&s) {try {

if (s != (T)0) return new T2(s);else return (T2*)0;

} catch (...) {delete p1_;throw; }

}A::A(const T& d, const T& s1, const T& s2):

m_(d), p1_(InitT1(s1)), p2_(InitT2(s2)) { }

04/12/23 More Effective C++ 6060

Meyers [10]: Prevent resource leaks in constructors

• A better design: 

class T { ... }; class T1 { public: T1(const T&); ... }; class T2 { public: T2(const T&); ... }; class A { public:

A(const T&, const T& = (T)0, const T& = (T)0); ~A(); void f(const T&); ...

private: T m_; const auto_ptr<T1> p1_; const auto_ptr<T2> p2_;

};

04/12/23 More Effective C++ 6161

Meyers [10]: Prevent resource leaks in constructors

• Constructor (safe by design): 

• Exception at Line 1 is safe. – m_ gets destructed.

• Exception at Line 2 is safe. – m_ & p1_ get destructed.

// ConstructorA::A(const T& d, const T& s1, const T& s2):

m_(d), p1_((s1 != (T)0)? new T1(s1): 0), // Line 1p2_((s2 != (T)0)? new T2(s2): 0) // Line 2

{ }

// DestructorA::~A(){ }

04/12/23 More Effective C++, Example: PPD 6262

Meyers [10]: Prevent resource leaks in constructors

• Moral– Replace pointer class members with their

corresponding auto_ptr objects• Fortifies constructors against resource leaks in the

presence of exceptions,

• Eliminates the need to manually deallocate resources in destructors, and

• Allows const member pointers to be handled in the same graceful fashion as non-const pointers.

04/12/23 More Effective C++ 6363

Meyers [11]: Prevent exceptions from leaving destructors

• A destructor is called in two situations – When an object is destroyed under “normal”

conditions• When it goes out of scope or

• Is explicitly deleted.

– When an object is destroyed by the exception-handling mechanism during the stack-unwinding part of “exception propagation”.

04/12/23 More Effective C++ 6464

Meyers [11]: Prevent exceptions from leaving destructors

• Recap – If an exception is thrown when another

exception is active, terminate() is called and the program immediately terminates.

– From within a destructor, there is no robust way to determine if an exception is active.

04/12/23 More Effective C++ 6565

Meyers [11]: Prevent exceptions from leaving destructors

• Consider

class Session { public:

Session(); ~Session(); ...

private: static void logCreation(Session *); static void logDestruction(Session *);

};

Session::~Session() {// Fatal to throw herelogDestruction(this);

};

04/12/23 More Effective C++ 6666

Meyers [11]: Prevent exceptions from leaving destructors

• Manage the exceptions Session::~Session() {

try {logDestruction(this);

}catch (...) {

// Fatal again if operator<<() throwscerr << "Unable to log destruction of Session object"

<< "at address " << this << ".\n";

} };

04/12/23 More Effective C++ 6767

Meyers [11]: Prevent exceptions from leaving destructors

• Bite the dust – swallow the exceptions

Session::~Session() {try {

logDestruction(this);}catch (...) { }

};

04/12/23 More Effective C++ 6868

Meyers [11]: Prevent exceptions from leaving destructors

• Moral – Keep exceptions from propagating out of

destructors. • Prevents terminate from being called during the

stack-unwinding part of exception propagation.

• Helps ensure that destructors always accomplish everything they are supposed to accomplish.

04/12/23 More Effective C++ 6969

Meyers [12]: Throwing an exception differs from passing a parameter or calling a virtual function

• Control does not return to the throw site.

• Throw always copies the object.

• Catch needs to clean-up the thrown object.

• Parameter Matching is exact for Catch and done with the static type

• Overloaded Catch clauses are tried in lexical order.

04/12/23 More Effective C++ 7070

Meyers [13]: Catch exceptions by reference

04/12/23 More Effective C++ 7171

Meyers [14]: Use exception specifications judiciously

04/12/23 More Effective C++ 7272

Meyers [15]: Understand the costs of exception handling

04/12/23 7373

Handling Exceptions in C & C++

References & CreditsReferences & Credits

13-May-05 7474

References• Handling Exceptions: Part 1 – 4

– Robert Schmidt• Modern C++ Design: Generic Programming & Design Pattern Applied

– Andrei Alexandrescu• Exceptional C++ & More Exceptional C++

– Herb Sutter • Effective C++ & More Effective C++

– Scott Meyers• Standard Features Missing From VC++ 7.1. Part I: Exception

Specifications – Nemanja Trifunovic

http://www.codeproject.com/cpp/stdexceptionspec.asp • A Pragmatic Look at Exception Specifications

– http://www.gotw.ca/publications/mill22.htm

04/12/23 7575

Credits / Acknowledgements

04/12/23 7676

Thank You

Recommended