I'm using boost context 1.67 to create a fiber (fcontext API) with a minimal as possible stack size on Windows 10.
Probably this issue isn't only specific to boost context and applies to any scenario where we use a Windows thread with a minimal stack size.
I encountered issues when using really small stacks (below 10kb) through stackoverflow exceptions that are caused by the internal stack unwind exception thrown by boost context as shown below:
When using a larger stack (> 10 kb) I don't encounter any issues.
For a reproduction the following example is sufficient:
#include <memory>
#include <utility>
#include <boost/context/all.hpp>
#define STACK_SIZE 8000
struct my_allocator
{
boost::context::stack_context allocate()
{
void* memory = std::malloc(STACK_SIZE);
return {STACK_SIZE,
static_cast<char*>(memory) +
STACK_SIZE};
}
void deallocate(
boost::context::stack_context& context)
{
std::free(static_cast<char*>(context.sp) -
STACK_SIZE);
}
};
int main(int, char**)
{
boost::context::fiber fiber(
std::allocator_arg, my_allocator{},
[](boost::context::fiber&& sink) mutable {
// ...
return std::move(sink);
});
// Will cause a stack unwind exception and
// reproduces the issue
return 0;
}
Boost context is only used here for performing the context switch with a user allocated stack, probably the issue is caused through some limitations of MSVC C++ exceptions which probably require a certain minimal stack size to work. Also the SetThreadStackGuarantee
WinAPI function doesn't have any effect on the issue.
The stack is allocated through malloc as depicted by the example.
Is it possible to use smaller stacks than 10kb on Windows when using C++ exceptions? Which circumstance possibly causes the limitation here?
Unfortunately the Windows API does not provide a function or a constant returning the minimal reuired stack space.
Only on 32bit Windows exceptions (SEH) causes entries on the stack. Win x64 uses tables-based exception handling - entries for the exception handlers are stored in the pdata section. So exception handling on x64 should not influence the min. stack space. The x64 calling convention requires some space - for instance 32 bytes of "shadow space" + space for XMM registers ... but only few bytes are required to store the registers.
I guess that the min. stack space is limited by the 'instrumentation code' (for instance stack cookies etc.) - probably this can be controlled by the compiler flags.
boost.context allocates memory and uses it as stack (+ implements the context switching). boost.fiber uses the stack from boost.context and reserves space (placement new) for a control structure at the top of each fiber's stack (but this is < 1kB).
You have the option to use interval nesting in order to test what's the minimal required stack space of your app.