Welcome to the next pikoTutorial !
What’s the problem?
Let’s take a look at the simple example below:
#include <iostream>
#include <future>
#include <thread>
#include <memory>
// define a class spinning up a thread
class SomeClass
{
public:
SomeClass()
: value_(std::make_unique<int>(12))
{
// spin up a separate thread
future_ = std::async([this](){
// wait for 1 second before executing any actions in the thread
std::this_thread::sleep_for(std::chrono::seconds(1));
std::cout << "Starting a separate thread!" << std::endl;
std::cout << "Value: " << *value_ << std::endl;
});
}
std::future<void> future_;
std::unique_ptr<int> value_;
};
int main(int argc, char** argv)
{
SomeClass obj;
}Let’s quickly sum up what’s going on here and what could be the expected output of such program:
- we have a class which holds a pointer to an integer value and which is allocated and set to 12 on the constructor’s initializer list
- the class spins up a thread in the constructor using
std::asyncand stores its result in astd::future, so that thestd::asynccall does not block the code execution, but only start the thread and move forward instead - the created thread will always have a chance to finish because
std::futurecallsget()in its destructor, so as soon as our class is being destroyed, the destructor of the member variablefuture_also gets called which ends up in a blockingget()call which waits for the associated thread to finish - the thread informs us when it starts and prints the value that the member pointer points to (in this case 12)
Nothing special here, but when we run this application we get the following output:
Starting a separate thread!
Segmentation fault (core dumped)To understand why it happens, let’s add a custom deleter to the unique pointer, just to be able to print something at the moment when value_ gets destroyed. I’ll add also a debug print to the destructor of SomeClass:
Note: I omitted all the standard includes to save on vertical space.
// define a custom deleter
void custom_int_deleter(int* ptr)
{
std::cout << "Destroying int pointer..." << std::endl;
delete ptr;
}
// define a class spinning up a thread
class SomeClass
{
public:
SomeClass()
: value_(new int(12), &custom_int_deleter)
{
// spin up a separate thread
future_ = std::async([this](){
// wait for 1 second before executing any actions in the thread
std::this_thread::sleep_for(std::chrono::seconds(1));
std::cout << "Starting a separate thread!" << std::endl;
std::cout << "Value: " << *value_ << std::endl;
});
}
~SomeClass() { std::cout << "Destroying SomeClass..." << std::endl; }
std::future<void> future_;
std::unique_ptr<int, void(*)(int*)> value_;
};
int main(int argc, char** argv)
{
SomeClass obj;
}Note for beginners: If you don’t like function pointer syntax in the unique pointer declaration, you can replace
void(*)(int*)withdecltype(&custom_int_deleter). Just remember, that it’s not just a matter of personal preferences, but a matter of value which each of these ways brings. The first one abstracts you from the function name, the second one abstracts you from the function signature. The signature of the deleter is alwaysvoid(T*)and that’s why I chose in the example to abstract from the deleter’s name.

AI is powerful. Snippets are instant.
Stop prompting for the same patterns repeatedly. Get almost 100 free VS Code snippets for C++, Python, CMake and Bazel from piko::snippets GitHub repository.
Now there’s still a segmentation fault, but the output gives us more information of what’s going on:
Destroying SomeClass...
Destroying int pointer...
Starting a separate thread!
Segmentation fault (core dumped)It looks like the pointer gets destroyed before the thread starts what means that in line 19 we’re dereferencing a deleted pointer!
The reason behind this behavior is the order of destruction of the class member variables – the same as in every other scope in C++, the member variables are destroyed in the reverse order of how they were created. By default, the construction order is just the order of the members in the class definition, so in case of our example it’s the following:
std::future<void> future_;
std::unique_ptr<int, void(*)(int*)> value_;What means that if value_ is created after the future_, it will be destroyed before the destructor of future_ gets called, potentially allowing the associated thread to outlive the member variables which it may be using before finishing – exactly as in our example. The fix for that may be as simple as changing the order of the members in the class definition:
std::unique_ptr<int, void(*)(int*)> value_;
std::future<void> future_;After that, the destructor of future_ will wait in a blocking way for the thread to finish before other members are destroyed:
Destroying SomeClass...
Starting a separate thread!
Value: 12
Destroying int pointer...What about real life?
The above example was pretty easy to debug because we were explicitly using a resource after it has been explicitly deleted, but remember that in real life such kind of bugs may be much more tricky to track down because your thread’s code may look more like this:
future_ = std::async([this](){
std::lock_guard<std::mutex> lock(mtx_);
// lots of code and some time-consuming processing
});After looking at this example you can say:
“Lock guard locks mutex at the beginning of the thread, so I’m fairly sure that the mutex will be valid at that point of time”.
However, in the above example it’s not the locking which is the problematic part – it’s the unlocking. See, the std::lock_guard holds a reference to the mutex which is then locked in the lock guard’s constructor and unlocked in the destructor. Are you sure that by the time the thread scope exits and lock guard attempts to unlock the mutex, the mutex will still be there?
Read also:
- Storing your ATTiny program in…EEPROM?
- Bug of the week #12
- Sharing variable between bash scripts
- A 40-line LLM-based bash command executor in Python
- GTest and short-circuit evaluation in C++
- AI is powerful. Snippets are instant.
- From AUTOSAR to S-Core: the first C++ pub/sub implementation
- How to write Arduino Uno code with Python?
- Combining Bazel with Docker
- Running commands with timeout on Linux
- Running Python unit tests with CMake
- Thirdparty dependencies with FetchContent
- Bug of the week #11
- Combining CMake with Docker
- How to search the internet from Linux terminal?
- Folding expressions in C++
- How to derive from an enum in Python?
- Bug of the week #10
- Trying ROS2: client/server within a single container
- Make C++ a better place #4: Go as an alternative
- How to convert hex to dec in Linux terminal?
- Setting up a Python project with CMake
- Separating builds for different configs with Bazel
- Trying ROS2: pub/sub within a single container
- Bug of the week #9
- UDP multicasting with Python
- Destruction order vs thread safety in C++
- Let’s review some code: C++ #2
- Make C++ a better place #3: D as an alternative
- Registering callback using std::function in C++
- Bug of the week #8
- TCP client/server with Python
- Simple menus in Bash scripts with select
- Calling member function on a nullptr in C++
- Bug of the week #7
- Python lru_cache explained
- How to dockerize a Python application?
- Make C++ a better place #2: CppFront as an alternative
- Parameters combinations in GoogleTest
- Data transfer with curl
- Python reduce explained
- Bug of the week #6
- Custom literals in C++
- Linux and hash command
- 5 Python good practices which make life easier
- Let’s review some code: Python #1
- Make C++ a better place #1: What does better mean
- Enums vs enum class in C++
- Bug of the week #5
- UDP client/server with Python
- Hard links in Linux
- Functions calling order in unit tests in C++
- Bug of the week #4
- Yield in Python – state machines, coroutines and more
- Copy files from another branch with Git
- Make C++ a better place #0: Introduction
- 5 misconceptions about std::move in C++
- How to use xargs on Linux?
- How to test method call order with unittest in Python?
- Bug of the week #3









