Lecture 5

Advanced Exception Handling

uC++ EHM

  1. exceptions must be generated from a specific kind of type.
    • this is unlike other languages where anything can be thrown
  2. supports two kinds of raising: throw and resume
  3. supports two kinds of handlers: termination and resumption
  4. supports propagation of nonlocal and concurrent exceptions
    • so in c++ source and faulting execution is always the same
    • but in u++ source and faulting can be different
      i.e. you can throw from one coroutine to another
  5. all exception types (user, runtime, I/O) are grouped into a hierarchy

Exception Type

C++ allows anything to be an exception type
uC++ restricts exceptions to those types defined by _Event

_Event D{...};
D d;            // local creation
_Resume d;      // resumption     
D *dp = new D;  // dynamic creation
_Resume *dp
delete dp;
_Throw D();     // temporary local creation

Inherited Members

it automatically inherits from uBaseEvent:

class uBaseEvent{ // like std::exception
  // if exception was not caught, it gets printed!
  uBaseEvent(const char *const msg = ""); 
  const char *const message() const;
  
  // who raised exception at you?
  const uBaseCoroutine &source() const;   // source object
  const char *sourceName() const;         // source name
  
  // what happens if its not caught
  // by default, it throws the exception again
  virtual void defaultTerminate();
  virtual void defaultResume();
}

Raising

you can throw and resume like c++ but uBaseEvent feature is lost in that case :/

use:
_Throw [exception-type];
_Resume [exception-type][_At uBaseCoroutine-id];

the _Throw cannot throw at perticular coroutine because it would unwind stacks of targetted coroutine, and it is very invasive.
_Resume on the other hand only looks for available catch block, then returns back to its original position in the stack.

Exception in uC++ are propagated differently from C++

C++ does truncation on throws!
meaning that if we were to catch f(m) with D:

try{f(m);}
catch(D){...}

This will not work in C because a D which is passed to function f (which is passed as B) is truncated to become B upon throw.

but _Throw of uC++ does not do such thing and will catch D with no issue :)


Handler

two types of handlers
1. termination
2. resumption

Termination works just like C++ using try/catch block

Resumption is different!
there is _CatchResume clause which can only catch resumption exception

typedef int Foo;
Foo i;
try{
  f(...);                //f is recursive and raises Foo
}_CatchResume(Foo & e){  // handler H
  Foo fix = i;           // use type and variable in local scope
  ... e = fix ...        // change _Resume block
}

when f raises resumption, the i that is refered in H is the same i in the upper scope. but notice that infact, its is way far down the stack! the compiler makes a lexical link that allows reference to the scope that called H.

Resumption cannot perform:

  • break
  • continue
  • goto
  • return
B: try{
  f(...);                //f is recursive and raises Foo
}_CatchResume(E e){      // handler H
  break B;               // force static return (disallowed)
  _Throw e;              // force recovery (allowed)
}

break in the above example is not allowed because considering the picture above, to break B is to unwind stack. meaning that it functions just like throw. So you need to throw instead of tricking the compiler to do what the throw is supposed to do.

action of defaultResume

_Event R{};
void rtn(){
  try{
    _Resume R();
  }catch (R & ){...}
}

Although there is no _CatchResume under the try, the program will not terminate for missing R().
it will actually be caught by catch (R &) clause!
its not that _Resume matches catch clause, but once _Resume is not get caught by any handler, it calls defaultResume of uBaseEvent! which throws R rather than resume


Nonlocal Exceptions

raising exception from one coroutine to another

_Event E{};
_Coroutine C{
  void main(){
    try{                  // setup handlers
      _Enable{            // [3] allow nonlocal exception
        ...suspend();...  // [4]/[9] inside suspend is _Resume E();
      }                   // disable all nonlocal exceptions
    }_CatchResume(E){     // [10] catches resume
      // handle nonlocal exception
    }
    //finalization, no nonlocal delivery
  }
 public:
  C() {resume();}         // [2]/[5]
  void mem(){resume();}   // [8]
};
int main(){
  C c;                    // [1] create C
  _Resume E() _At c;      // [6] exception pending
  c.mem();                // [7] trigger exception
}

source is not allowed to propagate in the faulting
For nonlocal resume, _Resume is a proxy for actual raise in the faulting coroutine
i.e. nonlocal resumption becomes local resumption

coroutine can always find resumer

  • _Resume E() _at resumer()

_Enable <T><T><T>{}
_Disable <T><T><T>{}

the default implementation of defaultTerminate is
_Resume UnhaldledException() _At resumer()

it resumes UnhandledException(), a more general exception, then whatever actual unhandled Exception was
The actual exception is kept inside UnhandledException().


Memory Management

coroutine stack is normally located in the heap

By default uC++ coroutine stack size is 256K and it does not grow

_Couroutine C{
 public:
  C() : uBaseCoroutine(8192){};          // default 8K stack
  C(int size) : uBaseCoroutine(size){};  // user specified stack size
}

careful for allocating big array in coroutine because the stack size is not that big
verify() function is to check if coroutine stack is running out of space.

좋은 웹페이지 즐겨찾기