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
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();
}
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 :)
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:
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.
_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
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
_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().
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.