I have a logging system, which basically uses a thread-local buffer to log. This helps in reducing locking. A bunch of message can be written into the thread-local buffer and flushed in one shot. And also since it is thread-local, we can avoid allocating buffer per log message.
Anyway the issue is during the process exit. We are seeing crash while accessing the thread-local buffer.
The thread-local object I have is something like std::vector<Buffer>
. [vector
because there are multiple buffers]. A representative code is something like this.
Buffer* getBuffer (int index)
{
static thread_local auto buffers = std::make_unique<std::vector<Buffer>>();
return buffers ? buffers->at(index) : nullptr;
}
Now when the program exits and the global destructors are called and unfortunately some of them logs. The destructors are called from main thread (which otherwise does nothing). So when the first global object is destroyed and it calls the logger, the thread_local buffer is created, but it is immediately destroyed, because the objects are destroyed in reverse order of creation and this is the last static object created. When the next global object destructor calls logger, it is effectively accessing destroyed object, which I think is the problem.
But I looked at the unique_ptr destructor and it does set the pointer inside it to nullptr [or atleast it sets the pointer to default constructed pointer - which I believe is value initialized to zero??]. So my return buffers ? buffers->at(index) : nullptr;
check should have prevented the access to freed object isn't it?
I created a toy program to try this out and I see that the buffers ?
check does prevent the access. But in the real code base that is not happening. At the point of crash the vector is accessed at it is already toast.
Now if someone can tell me a magic solution, it will make my life easy :-). Otherwise any idea why the bool
operator of unique_ptr
does not return false
. Is it because it is classic undefined behavior accessing a destroyed object.
I read in stack-overflow that if the object has a trivial destructor, it is okay to access it after destruction. In that case would my problem is solved if I create a thread-local bool
just above the unique_ptr
, and set it to true in the destructor of a wrapper class containing unique_ptr
?