3

I was recently using an object whose purpose is to allocate and deallocate memory as a singleton. Something like

class MyValue
{
  // ...

  static Allocator& GetAllocator()
  {
    static Allocator allocator;
    return allocator;
  }

  // ...
};

I realized later Allocator is not thread-safe: when multiple threads were using the same allocator concurrently, occasionally strange things were happening, causing assertions and segmentation faults.

Solution: use different allocators for different threads:

class MyValue
{
  // ...

  static Allocator& GetAllocator()
  {
    thread_local static Allocator allocator;
    return allocator;
  }

  // ...
};

Awesome! My problems are gone! Just one question: Will my allocator variable be initialized every time a thread is created, even if the majority of threads won't use this variable?

The initialization of the allocator might be heavy operation, so I would like it to be initialized only when it is actually required, not in every thread.

I read that thread_local variables are allocated by each thread. Does that mean they are also constructed? And does this allocation (or construction) happen systematically for each thread that is created or just for threads that use it?

I faintly remember hearing in a course that most details about threads and thread-local storage are platform dependent. If this is the case, I'm particularly interested in Linux and FreeBSD.


Related (interesting reads, but I could not find the answer there):

Spiros
  • 2,156
  • 2
  • 23
  • 42
  • 2
    Are you sure you should have `static Allocator GetAllocator()` instead of `static Allocator& GetAllocator()`? The singleton pattern is the latter – NathanOliver Apr 13 '18 at 14:57
  • Also, *What does the thread_local mean in C++11?* seems to answer your question: *Something that is thread-local is brought into existence at thread creation and disposed of when the thread stops.* i.e., constructed and destructed – NathanOliver Apr 13 '18 at 14:59
  • http://preshing.com/20130930/double-checked-locking-is-fixed-in-cpp11/ – Hans Passant Apr 13 '18 at 15:11
  • Possible duplicate of [What is dynamic initialization of object in c++?](https://stackoverflow.com/questions/5945897/what-is-dynamic-initialization-of-object-in-c) – Passer By Apr 13 '18 at 15:23
  • @NathanOliver: good point. Right, it returns an l-value reference. I will correct this in the question. – Spiros Apr 13 '18 at 15:34
  • @NathanOliver: I actually wanted to quote that part of the answer. "Brought into existence" does not mean anything to me in this context. Does it mean allocated? Does it mean constructed? – Spiros Apr 13 '18 at 15:37
  • @PasserBy: I don't see any mention of `thread-local` in that question. How is mine related? In fact, that question does not apply entirely to C++11, because the newer standards introduce new storage specifiers: http://en.cppreference.com/w/cpp/language/storage_duration – Spiros Apr 13 '18 at 15:39
  • Because what you are asking for is governed by static/dynamic initialization – Passer By Apr 13 '18 at 15:39
  • According to the documentation I linked, `thread_local` is neither static nor dynamic, or I did not understand well. In any case, I don't see how this brings me to know if the initialization of the object happens in every thread or just in those that use it. – Spiros Apr 13 '18 at 15:44

2 Answers2

7

[basic.stc.thread] states

  1. All variables declared with the thread_local keyword have thread storage duration. The storage for these entities shall last for the duration of the thread in which they are created. There is a distinct object or reference per thread, and use of the declared name refers to the entity associated with the current thread.

  2. A variable with thread storage duration shall be initialized before its first odr-use (6.2) and, if constructed, shall be destroyed on thread exit.

So, you will get storage for the object in each thread. We also have [stmt.dcl]/4 that states

Dynamic initialization of a block-scope variable with static storage duration (6.7.1) or thread storage duration (6.7.2) is performed the first time control passes through its declaration; such a variable is considered initialized upon the completion of its initialization.

So, if we reach the declaration then the object will be initialized and if it has a constructor, it will be called.

If we put that all together you will have a number of constructor and destructor calls equal to the number of threads that actually reach

thread_local static Allocator allocator;
Community
  • 1
  • 1
NathanOliver
  • 171,901
  • 28
  • 288
  • 402
  • You mean a thread local variable, even if not used in a thread, the storage will always be allocated, but won't be initialized (constructor)? – Baiyan Huang Mar 05 '19 at 14:56
  • @BaiyanHuang With my understanding of how everything works, yes. I can't find anything in the standard to say if this has to be the case but I believe all implementations allocate space on the stack for all local variables, regardless if they are initialized or not. – NathanOliver Mar 05 '19 at 15:09
  • yea, standard tends to say this leaves to implementations, I wrote an example by which I could see the constructor is called only in thread where the thread local get used, but not sure if the storage is pre-allocated for all thread or just allocated before calling the constructor only in those thread that referenced it – Baiyan Huang Mar 06 '19 at 01:50
1

I can give you a solution to check whether it creates a new object of type Allocator each time you call GetAllocator(). Just call this method at least 5 times and check the address of all the object return. If address of all the return object are different then yes its creates different object in each call or if not it just return the address of same object each time you call GetAllocator().

Abhijit Pritam Dutta
  • 5,521
  • 2
  • 11
  • 17
  • 1
    The empirical approach you propose is clear, but I would like to understand the theory. I have enough experience to no longer blindly trust the result of simple experiments as a way to uncover the truth behind a complex system... – Spiros Apr 13 '18 at 15:42