#include <csetjmp>
template <typename Callable>
void create_checkpoint(std::jmp_buf buf, Callable&& callable)
{
if (setjmp(buf) != 0)
{
callable();
}
}
#include <iostream>
struct announcer {
int id;
announcer(int id):
id{id}
{
std::cout << "created announcer with id " << id << '\n';
}
~announcer() {
std::cout << "destructing announcer with id " << id << '\n';
}
};
void oopsie(std::jmp_buf buf, bool shouldJump)
{
if (shouldJump)
{
// std::cout << "performing jump...\n";
std::longjmp(buf, 1);
}
}
void test1()
{
std::jmp_buf buf;
announcer a1{1};
create_checkpoint(buf, []() {throw std::exception();});
oopsie(buf, true);
}
void test2()
{
std::jmp_buf buf;
announcer a1{1};
create_checkpoint(buf, []() {throw std::exception();});
oopsie(buf, false);
announcer a2{2};
create_checkpoint(buf, []() {throw std::exception();});
oopsie(buf, true);
}
int main()
{
try
{
test1();
}
catch (...)
{}
try
{
test2();
}
catch (...)
{}
}
I have to call some C library that reports errors via longjmp
. To provide strong exception guarantee, I want to create a function that is very similar to std::lock_guard
, e.g. I just write create_checkpoint(buf, handler)
and keep calling the C library functions until I allocate more resources (destructors for objects created below setjmp
line are not called, if I understand correctly).
Why undefined behavior is invoked in this case and how can I fix it?
Printing message to std::cout
before std::longjmp
vs not printing produces very different results even though that line has little to do with control flow.
I understand that std::longjmp
essentially restores the registers and jumps at the instruction pointer saved by setjmp
macro. Also, functions are not optimized away, and at least during compilation, there is an instruction to call longjmp
.
Converting the create_checkpoint
to macro seems to solve the issue. Yet I wonder is there a better way to do this?
From https://en.cppreference.com/w/cpp/utility/program/longjmp
If the function that called setjmp has exited, the behavior is undefined (in other words, only long jumps up the call stack are allowed)
As you aren't following this rule your program has undefined behaviour