Минимально возможный размер стека в Windows при использовании исключений C++ (с использованием усиленных контекстных волокон)

Я использую boost context 1.67 для создания волокна (fcontext API) с минимально возможным размером стека в Windows 10.

Вероятно, эта проблема характерна не только для повышения контекста и применима к любому сценарию, в котором мы используем поток Windows с минимальным размером стека.

Я столкнулся с проблемами при использовании очень маленьких стеков (менее 10 КБ) из-за исключений stackoverflow, которые вызваны внутренним исключением раскрутки стека, вызванным контекстом повышения, как показано ниже:

введите здесь описание изображения

При использовании большего стека (> 10 КБ) проблем не возникает.

Для воспроизведения достаточно следующего примера:

#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 используется здесь только для выполнения переключения контекста с выделенным пользователем стеком, возможно, проблема вызвана некоторыми ограничениями исключений MSVC C++, которые, вероятно, требуют для работы определенного минимального размера стека. Также функция SetThreadStackGuarantee WinAPI никак не влияет на проблему.

Стек выделяется через malloc, как показано в примере.

Можно ли использовать стеки меньше 10 КБ в Windows при использовании исключений C++? Какое обстоятельство, возможно, вызывает здесь ограничение?


person Denis Blank    schedule 29.06.2018    source источник
comment
в системе Windows выделяйте не менее 64 КБ для Стек тем   -  person RbMm    schedule 30.06.2018
comment
@RbMm есть ли ссылка на это ограничение? Например, язык Go по умолчанию использует стеки размером 2 КБ (автоматически увеличивающиеся) (вероятно, и в Windows).   -  person Denis Blank    schedule 03.07.2018
comment
но я вставляю ссылку. 2кб стека быть не может в принципе. размер страницы 4кб   -  person RbMm    schedule 03.07.2018
comment
Как сказал @RbMm, система округляет размер стека до кратного 64 КБ .. см. ссылку. Вы можете поиграть с GetCurrentThreadStackLimits, чтобы увидеть, что там происходит, попробуйте вызвать это с/без Boost и с/без исключения, чтобы увидеть, где находится узкое место.   -  person AndreiM    schedule 05.07.2018
comment
@AndreiM, как система может округлить предоставленный пользователем стек до 64 КБ? Я изменил пример, чтобы отразить то обстоятельство, что стек выделяется через malloc (который может поддерживаться распределителем, таким как jemalloc). Вероятно, весь код будет выполняться в пользовательском пространстве и не вызовет системного вызова, поскольку переключение контекста осуществляется жестко запрограммировано в x86_64 контекстом повышения.   -  person Denis Blank    schedule 05.07.2018
comment
@DenisBlank Я думаю, что это недоразумение. Win32 API не предоставляет никакого метода выделения собственного пространства стека (одна проблема, которую я вижу, заключается в том, что вместо исключения переполнения стека вы получите отказ в доступе/сегментацию). Но из того, что я вижу в boost fiber docu(), вы выделяете память не под стек, а НА ВЕРХУ стека. В вашем примере это будет означать, что чем больше места, по вашему мнению, вы выделяете для стека, вы фактически выделяете его поверх стека, поэтому остается меньше доступного места в стеке. Возможно, я ошибаюсь, поэтому, пожалуйста, посмотрите, так ли это.   -  person AndreiM    schedule 06.07.2018


Ответы (1)


К сожалению, Windows API не предоставляет функцию или константу, возвращающую минимально необходимое пространство стека.

Только в 32-битной Windows исключения (SEH) вызывают записи в стеке. Win x64 использует обработку исключений на основе таблиц — записи для обработчиков исключений хранятся в разделе pdata. Так что обработка исключений на x64 не должна влиять на мин. пространство стека. Соглашение о вызовах x64 требует некоторого пространства - например, 32 байта «теневого пространства» + пространство для регистров XMM ... но для хранения регистров требуется всего несколько байтов.

Я предполагаю, что мин. пространство стека ограничено «инструментальным кодом» (например, файлами cookie стека и т. д.) — вероятно, это можно контролировать с помощью флагов компилятора.

boost.context выделяет память и использует ее как стек (+ реализует переключение контекста). boost.fiber использует стек из boost.context и резервирует место (размещение новое) для структуры управления в верхней части стека каждого волокна (но это ‹ 1 КБ).

У вас есть возможность использовать интервальную вложенность, чтобы проверить минимально необходимое пространство стека для вашего приложения.

person xlrg    schedule 09.07.2018