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
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.