Search code examples
c++stdthread

std::thread helper class to add thread name and stack size


I would like to make a helper class (or subclass of std::thread) to allow stack size to be set and also thread name. If the thread is running on a platform, which does not support e.g. stack size, the number should just be ignored.

I was thinking of a "ThreadHelper", which has the extended constructor interface, and just returns a std::thread.

To be honest, I have very little experience when it comes to all the template stuff, which std::thread contains.

Right now the thread class is instantiated like: m_thread = new std::thread(&Button::Process, this); I would like something like: m_thread = new ThreadHelper.Create(&Button::Process, this, stackSize, name);

Any advices are appreciated.

Thanks Martin


Solution

  • Here is what you could do: You need a wrapper around your thread start function so that you can call the appropriate functions before (and possibly after) your thread function is running. It might also be a good idea to include a try-catch block and do some error processing.

    template <typename ThreadStartFnc>
    struct ThreadWrapper
    {
       ThreadStartFnc fnc;
       const char *name;     // (1) Note: careful, just a pointer here, not a string. See below.
       size_t priority;
       size_t stack_size;
    
       ThreadWrapper(...) // constructor here...
    
       void operator()()
       {
          SetThreadName(name);         // Whatever that is on your system
          SetThreadPriority(priority); // dito
          SetStackSize(stack_size);    // not all systems allow this
          try {
             fnc();
          }
          catch(...) {
             cerr << "Exception caught"; // Do exception processing here.
          }
       }
    };
    

    And you need a simple way to instantiate an std::thread with the wrapper "inserted", like this:

    template <typename Fnc>
    std::thread make_thread(const Fnc& f, const char *name, size_t priority=0, size_t stack_size=0)
    {
       return std::thread(ThreadWrapper<Fnc>(f, name, priority, stack_size));
    }
    

    And that is basically it. std::thread accepts a Functor object, which is what ThreadWrapper is. It calls Functor() to start the thread, which is void operator()(). This function uses the additional parameters name, priority, stack_size to set everything up and then call your function.

    Enhance this with the C++11/14/17 goodies like variadic template parameters and/or lambda functions as you like.

    Below is the actual wrapper function that we use (although we do not use a Functor, we use boost::bind and a static template function instead).

        template <typename Fnc>
        static inline void run_cstr(const Fnc &f, const char *name, DWORD priority)
        {
            int rc=_configthreadlocale(0);
    /*
            _configthreadlocale(_ENABLE_PER_THREAD_LOCALE);
            setlocale(LC_ALL, "C");
    */
            SetThreadName(name);
            watchdog::set_thread_info(name);
            ::SetThreadPriority(::GetCurrentThread(), priority);
            _set_se_translator(&_se_translator);
            _set_invalid_parameter_handler(&my_invalid_parameter_handler);
            __try {
                f();
            }
            __except (global_seh_handler(GetExceptionCode(), GetExceptionInformation()) ) {
            }
        }
    

    It sets the ThreadName for Visual Studio (SetThreadName), sets the locale, sets the thread priority, connects our software watchdog to the thread and also sets up a global Windows try/catch handler for all exceptions including access violations etc.... The global_seh_handler will be executed if any uncaught exceptions (including invalid std lib parameters) end up here. It will write a crash dump file which we use for post mortem debugging.

    (1) Note: I've used a const char *name for the thread name, because I am assuming that the thread will run immediately, while the string for the thread name is still available. That is actually unsafe, if you are storing objects of type ThreadWrapper for a longer period. You'd need to change const char* to std::string then.