1

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 <...> {
// ...
private:
    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?

benjist
  • 2,740
  • 3
  • 31
  • 58

2 Answers2

4

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

Community
  • 1
  • 1
ShadowRanger
  • 143,180
  • 12
  • 188
  • 271
2

The syntax you are looking for is static thread_local on a member variable:

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

Rather than performing manual cleanup it would be better to encapsulate _someClass in a unique_ptr, since thread locals are destroyed on thread exit:

    static thread_local std::unique_ptr<SomeClass> _someClass;
ecatmur
  • 152,476
  • 27
  • 293
  • 366