benjist benjist - 4 months ago 33
C++ Question

Declare, allocate and deallocate thread-local pointer variables outside the run loop

I have a threaded pipe-and-filter implementation where I want to use thread-local copies in one of my filters. I do not implement a run loop myself. Instead, the base Filter class calls a process() method on each of the filters whenever it gets data to be processed.

I have two issues with using thread_locals in this scenario:
1) I cannot declare thread_locals within the process() method, because the point is to reuse the thread locals whenever the process() method is called.

Example code below:

void process(SomeInput in) {
thread_local SomeClass* someClass = nullptr;
if (someClass == nullptr) someClass = new SomeClass(params);

// do stuff here...

So above I initialize a thread_local instance of SomeClass. But I do not deallocate it, because process() will be called by the same thread's run loop whenever new data arrives. Obviously, it the classes will never get freed. Bad.

2) I've added a threadCleanup() method to the filter implementation which gets now called whenever a filter is stopped (and it's thread(s) are stopped). Though that would require to declare thread_local member variables like:

class SomeFilter : public Filter <...> {
// ...
thread_local SomeClass* _someClass;

But that doesn't work with classes and throws:
"thread_local is only allowed on variable declarations"

What is the proper way to declare, allocate and deallocate thread-locals in such scenario?


Answering with fix for your original problem instead of the new one you created for yourself:

Just make the original code use std::unique_ptr. You can even one-line it, since thread_local implies static, so it will only be initialized once without needing to perform per call tests for nullptr:

void process(SomeInput in) {
    thread_local std::unique_ptr<SomeClass> someClass(new SomeClass(params));

    // do stuff here...

The first time any given thread calls process, the unique_ptr is initialized for that thread; when that thread exits, the unique_ptr is destroyed and the SomeClass instance for that thread is collected, because thread_local destructors are called on thread exit.

Mind you, if someClass is small, you could store it directly in thread_local storage instead of storing the unique_ptr there pointing to the heap, which would let you avoid unique_ptr entirely, since as noted, thread_local implies static and calls destructors on thread exit:

void process(SomeInput in) {
    thread_local SomeClass someClass(params);

    // do stuff here,
    // using someClass. instead of someClass-> for member/method access,
    // and &someClass where you used to use someClass if something needs a raw
    // pointer (the raw pointer is definitely sticking around until thread
    // exit after all)

Using the unique_ptr approach might still be advantageous (thread local storage can be limited/slow, so it may be worthwhile to store the rest of the class in normal heap memory).