# Destructor is run
testcpp11unwind:::call_A()
#> Error: oh no!
#> hello from the destructor!
# Destructor is NOT run
testcpp11unwind:::call_B()
#> Error: oh no!
Note that cpp_A()
and cpp_B()
on their own are seemingly innocuous
code that a developer is likely to write without thinking twice about
it.
The destructor of a
doesn’t run in the cpp_B()
case because:
call_B()
sets up a try/catch through the entry macrosfn()
callsunwind_protect()
and setsshould_unwind_protect = FALSE
R_UnwindProtect()
is used to callcall_A()
, and asetjmp()
is used to be able to recover fromlongjmp()
scall_A()
sets up a try/catch through the entry macroscpp_A()
is calleda
is createdstop()
callsunwind_protect()
, butshould_unwind_protect = FALSE
so it does NOT useR_UnwindProtect()
orsetjmp()
stop()
ends up callingRf_error()
, causing alongjmp()
- Because
should_unwind_protect = FALSE
, thelongjmp()
is not caught instop()
. Instead we jump alllll the way back to wherefn()
calledunwind_protect()
andR_UnwindProtect()
. This skips over thecpp_A()
frame wherea
should have been destructed on the way out. VERY BAD!