Add FiberStack

This commit is contained in:
Nikolai Kochetov 2020-12-02 14:18:46 +03:00
parent 319d36a3b7
commit 0fae325d76
2 changed files with 113 additions and 4 deletions

45
src/Common/FiberStack.h Normal file
View File

@ -0,0 +1,45 @@
#include <boost/context/stack_context.hpp>
#include <Common/Allocator.h>
#if defined(BOOST_USE_VALGRIND)
#include <valgrind/valgrind.h>
#endif
/// This is an implementation of allocator for fiber stack.
/// It uses internal allocator, so we track memory usage. It is the main reason why this class is needed.
/// The reference implementations are pooled_fixedsize_stack and protected_fixedsize_stack from boost::context.
template <typename TAllocator = Allocator<false, false>>
class FiberStack
{
private:
size_t stack_size;
public:
/// 8MB of memory per fiber stack may seem too expensive. It is indeed.
/// The reason is that current (patched) libunwind needs > 4MB of stack memory to unwind stack.
/// If we allocate less memory, any thrown exception inside fiber will cause segfault.
static constexpr size_t default_stack_size = 8 * 1024 * 1024;
explicit FiberStack(size_t stack_size_ = default_stack_size) : stack_size(stack_size_) {}
boost::context::stack_context allocate()
{
void * vp = TAllocator().alloc(stack_size);
boost::context::stack_context sctx;
sctx.size = stack_size;
sctx.sp = static_cast< char * >(vp) + sctx.size;
#if defined(BOOST_USE_VALGRIND)
sctx.valgrind_stack_id = VALGRIND_STACK_REGISTER(sctx.sp, vp);
#endif
return sctx;
}
void deallocate(boost::context::stack_context & sctx)
{
#if defined(BOOST_USE_VALGRIND)
VALGRIND_STACK_DEREGISTER( sctx.valgrind_stack_id);
#endif
void * vp = static_cast< char * >(sctx.sp) - sctx.size;
TAllocator().free(vp, stack_size);
}
};

View File

@ -1,26 +1,90 @@
#include <iostream> #include <iostream>
/// #define BOOST_USE_UCONTEXT
#include <boost/context/fiber.hpp> #include <boost/context/fiber.hpp>
#include <boost/context/pooled_fixedsize_stack.hpp>
#include <boost/context/segmented_stack.hpp>
#include <Common/Exception.h>
#include <Common/FiberStack.h>
void __attribute__((__noinline__)) foo(std::exception_ptr exception)
{
if (exception)
std::rethrow_exception(exception);
}
void __attribute__((__noinline__)) bar(int a)
{
std::cout << StackTrace().toString() << std::endl;
if (a > 0)
throw DB::Exception(0, "hello");
}
void __attribute__((__noinline__)) gar(int a)
{
char buf[1024];
buf[1023] = a & 255;
if (a > 2)
return gar(a - 1);
else
bar(a);
}
int main(int, char **) int main(int, char **)
{ try {
namespace ctx=boost::context; namespace ctx=boost::context;
int a; int a;
ctx::fiber source{[&a](ctx::fiber&& sink) std::exception_ptr exception;
// ctx::protected_fixedsize allocator
// ctx::pooled_fixedsize_stack(1024 * 64 + 2 * 2 * 1024 * 1024 * 16, 1)
ctx::fiber source{std::allocator_arg_t(), FiberStack(), [&](ctx::fiber&& sink)
{ {
a=0; a=0;
int b=1; int b=1;
while (true) for (size_t i = 0; i < 9; ++i)
{ {
sink=std::move(sink).resume(); sink=std::move(sink).resume();
int next=a+b; int next=a+b;
a=b; a=b;
b=next; b=next;
} }
try
{
gar(1024);
}
catch (...)
{
std::cout << "Saving exception\n";
exception = std::current_exception();
}
return std::move(sink); return std::move(sink);
}}; }};
for (int j=0;j<10;++j) for (int j=0;j<10;++j)
{ {
source=std::move(source).resume(); try
{
source=std::move(source).resume();
}
catch (DB::Exception & e)
{
std::cout << "Caught exception in resume " << e.getStackTraceString() << std::endl;
}
std::cout << a << " "; std::cout << a << " ";
} }
std::cout << std::endl;
try
{
foo(exception);
}
catch (const DB::Exception & e)
{
std::cout << e.getStackTraceString() << std::endl;
}
}
catch (...)
{
std::cerr << "Uncaught exception\n";
} }