Lecture 5
Advanced Exception Handling
uC++ EHM
- exceptions must be generated from a specific kind of type.
- this is unlike other languages where anything can be thrown
- supports two kinds of raising: throw and resume
- supports two kinds of handlers: termination and resumption
- 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
- 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.
Author And Source
이 문제에 관하여(Lecture 5), 우리는 이곳에서 더 많은 자료를 발견하고 링크를 클릭하여 보았다 https://velog.io/@wjddlstjd396/Lecture-5저자 귀속: 원작자 정보가 원작자 URL에 포함되어 있으며 저작권은 원작자 소유입니다.
우수한 개발자 콘텐츠 발견에 전념 (Collection and Share based on the CC Protocol.)