Есть ли хак для установки размера стека потока std::thread?

Мне нужно указать размер стека потока для std::thread. К сожалению, не могу воспользоваться boost::thread. В системе std::thread имплементированы через pthread. Может, можно указать размер стека потока для конкретного файла в опциях компилятора или что то подобное?


Ответы (1 шт):

Автор решения: Pak Uula

Если компилятор g++ и в системе используется glibc, то можно изменять дефолтные атрибуты, с которыми создаются pthread-ы.

При чём тут g++

В библиотеке libtdc++ класс std::thread создаёт новые потоки с атрибутами по-умолчанию:

  • новый поток создаётся методом void thread::_M_start_thread(_State_ptr, void (*)())
  • этот метод вызывает __gthread_create - встроенную реализацию многопоточности из libgcc
  • функция __gthread_create создаёт поток с атрибутами по-умолчанию:
    static inline int
    __gthread_create (__gthread_t *__threadid, void *(*__func) (void*),
                      void *__args)
    {
      return __gthrw_(pthread_create) (__threadid, NULL, __func, __args);
    }
    

Второй аргумент, который NULL, означает, что поток создаётся с атрибутами по-умолчанию.

При чём тут glibc

В glibc есть расширение pthread_setattr_default_np. Эта функция сохраняет значения атрибутов для последующих pthread_create.

Пример: увеличить размер стека в два раза:

    pthread_attr_t attr;
    pthread_attr_init(&attr); // инициализируется дефолтными значениями
    size_t default_stack_size;
    pthread_attr_getstacksize(&attr, &default_stack_size);
    std::cout << "default stack size = " << default_stack_size << std::endl;

    pthread_attr_setstacksize(&attr, 2 * default_stack_size);
    pthread_setattr_default_np(&attr);

Полный пример

В примере создаются потоки, которые умеют печатать свои атрибуты. Сначала создаётся поток с системными атрибутами по-умолчанию, затем размер стека увеличивается в два раза.

#ifndef _GNU_SOURCE
#define _GNU_SOURCE /* Non-posix extensions */
#endif

#include <pthread.h>
#include <stdlib.h> // EXIT_FAILURE

#include <thread>
#include <iostream>

// Exits if status != 0
static void
check_status(int status, const char *msg);

static void
display_pthread_attr(pthread_attr_t *attr, const char *prefix)
{
    int s, i;
    size_t v;
    void *stkaddr;
    struct sched_param sp;

    s = pthread_attr_getdetachstate(attr, &i);
    check_status(s, "pthread_attr_getdetachstate");
    std::cout << prefix << "Detach state        = " << ((i == PTHREAD_CREATE_DETACHED) ? "PTHREAD_CREATE_DETACHED" : (i == PTHREAD_CREATE_JOINABLE) ? "PTHREAD_CREATE_JOINABLE"
                                                                                                                                                    : "???")
              << std::endl;

    s = pthread_attr_getscope(attr, &i);
    check_status(s, "pthread_attr_getscope");
    std::cout << prefix << "Scope               = " << ((i == PTHREAD_SCOPE_SYSTEM) ? "PTHREAD_SCOPE_SYSTEM" : (i == PTHREAD_SCOPE_PROCESS) ? "PTHREAD_SCOPE_PROCESS"
                                                                                                                                            : "???")
              << std::endl;

    s = pthread_attr_getinheritsched(attr, &i);
    check_status(s, "pthread_attr_getinheritsched");
    std::cout << prefix << "Inherit scheduler   = " << ((i == PTHREAD_INHERIT_SCHED) ? "PTHREAD_INHERIT_SCHED" : (i == PTHREAD_EXPLICIT_SCHED) ? "PTHREAD_EXPLICIT_SCHED"
                                                                                                                                               : "???")
              << std::endl;

    s = pthread_attr_getschedpolicy(attr, &i);
    check_status(s, "pthread_attr_getschedpolicy");
    std::cout << prefix << "Scheduling policy   = " << ((i == SCHED_OTHER) ? "SCHED_OTHER" : (i == SCHED_FIFO) ? "SCHED_FIFO"
                                                                                         : (i == SCHED_RR)     ? "SCHED_RR"
                                                                                                               : "???")
              << std::endl;

    s = pthread_attr_getschedparam(attr, &sp);
    check_status(s, "pthread_attr_getschedparam");
    std::cout << prefix << "Scheduling priority = " << sp.sched_priority << std::endl;

    s = pthread_attr_getguardsize(attr, &v);
    check_status(s, "pthread_attr_getguardsize");
    std::cout << prefix << "Guard size          = " << v << " bytes" << std::endl;

    s = pthread_attr_getstack(attr, &stkaddr, &v);
    check_status(s, "pthread_attr_getstack");
    std::cout << prefix << "Stack address       = " << stkaddr << std::endl;
    std::cout << prefix << "Stack size          = " << v << std::endl;
}

static void
thread_fn(const std::string name)
{
    int s;
    pthread_attr_t gattr;

    // pthread_getattr_np() is a non-standard GNU extension that
    // retrieves the attributes of the thread specified in its
    // first argument. 

    s = pthread_getattr_np(pthread_self(), &gattr);
    check_status(s, "pthread_getattr_np");

    std::cout << "Thread '" << name << "' attributes:\n";
    display_pthread_attr(&gattr, "\t");

    return;
}

int main(int argc, char *argv[])
{
    std::thread th1{thread_fn, "Before doubling stack size"};
    th1.join();

    pthread_attr_t attr;
    pthread_attr_init(&attr);
    size_t default_stack_size;
    auto s = pthread_attr_getstacksize(&attr, &default_stack_size);
    check_status(s, "default stack size");
    std::cout << "default stack size = " << default_stack_size << std::endl;

    s = pthread_attr_setstacksize(&attr, 2 * default_stack_size);
    check_status(s, "pthread_attr_setstacksize");
    // The pthread_setattr_default_np() is GNU extension of pthreads
    // This function sets the default
    // attributes used for creation of a new thread—that is, the
    // attributes that are used when pthread_create(3) is called with a
    // second argument that is NULL.
    s = pthread_setattr_default_np(&attr);
    check_status(s, "pthread_setattr_default_np");

    std::thread th2{thread_fn, "After doubling stack size"};
    th2.join();
}

static void
check_status(int status, const char *msg)
{
    if (status != 0)
    {
        std::cerr << msg << ": status : " << status << std::endl;
        std::exit(EXIT_FAILURE);
    }
}

Результат работы в Ubuntu-22:

Thread 'Before doubling stack size' attributes:
        Detach state        = PTHREAD_CREATE_JOINABLE
        Scope               = PTHREAD_SCOPE_SYSTEM
        Inherit scheduler   = PTHREAD_INHERIT_SCHED
        Scheduling policy   = SCHED_OTHER
        Scheduling priority = 0
        Guard size          = 4096 bytes
        Stack address       = 0x7f61d5af8000
        Stack size          = 8388608
default stack size = 8388608
Thread 'After doubling stack size' attributes:
        Detach state        = PTHREAD_CREATE_JOINABLE
        Scope               = PTHREAD_SCOPE_SYSTEM
        Inherit scheduler   = PTHREAD_INHERIT_SCHED
        Scheduling policy   = SCHED_OTHER
        Scheduling priority = 0
        Guard size          = 4096 bytes
        Stack address       = 0x7f61d4af7000
        Stack size          = 16777216

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

→ Ссылка