2

I have a class member I would like to construct that should be local to each thread that accesses it. The constructor requires a few arguments though, so I can't rely on static zero initialization.

class ThreadMem{
public:
    ThreadMem(uint32 cachelineSize, uint32 cachelineCount);
};    

class ThreadPool{
public:
    ThreadPool(uint32 cachelineSize, uint32 cachelineCount){
        // I need to prepare the `m_mem` in other threads with these arguments somehow
    }
    ThreadMem & mem() {
        return m_mem;
    }
private:
    static thread_local ThreadMem m_mem;
};

Where would be the best place to construct static thread_local ThreadMem ThreadPool::m_mem so that it's only constructed once per thread, with values only ThreadPool's constructing thread can calculate at runtime?

Anne Quinn
  • 12,609
  • 8
  • 54
  • 101
  • 8
    Personally, I find it simpler to just have a `static` member function that returns a reference to a `thread_local`, like how [Meyers' Singleton](https://stackoverflow.com/questions/17712001/how-is-meyers-implementation-of-a-singleton-actually-a-singleton) is implemented. It makes it pretty easy to control when and how they are initialized. – François Andrieux Mar 05 '19 at 17:34
  • @FrançoisAndrieux That implies you'd have to pass the arguments to its constructor every time you call that function with that function static, right? – Maxim Egorushkin Mar 05 '19 at 17:46
  • @MaximEgorushkin Ya, but you can hold on to the reference you get the first time it's called. – François Andrieux Mar 05 '19 at 17:47
  • 1
    This seems odd to me. In a ThreadPool, which the name suggests is a thread pool manager, why would it need thread local memory? Why not build this into the pooled threads? – user4581301 Mar 05 '19 at 17:50
  • 1
    @user4581301 - any thread (workers and non-workers) can create jobs or allocate memory, so having a pool for each thread lets them avoid contending over one when allocating – Anne Quinn Mar 05 '19 at 17:54
  • Sounds like you are complicating with good reason, but once you allocate the pooled threads, this data store can be allocated with the worker at the start of their task loop. This will be an allocation hit once early in thread start-up, but no different from what you are aiming for now. In addition, `ThreadPoo` could allocate one big block and hand out pieces to its minions to sub-allocate. – user4581301 Mar 05 '19 at 18:08
  • @user4581301 You dropped an L from `ThreadPool`. The result of the typo is unfortunate. – François Andrieux Mar 05 '19 at 18:12
  • @FrançoisAndrieux Indeed. A shitty typo. – user4581301 Mar 05 '19 at 18:14

1 Answers1

1

That static class member is constructed by C++ run-time at dynamic initialization phase before main is entered. The arguments to its constructor must be available by that time, which may be infeasible.

Maxim Egorushkin
  • 131,725
  • 17
  • 180
  • 271
  • Aw, bummer, it does look to be infeasible (the library I use to discover cacheline size has to be initialized after that phase). I'll redesign it to not use thread_local in this case, thank you. – Anne Quinn Mar 05 '19 at 18:07