I have a scenario where:
I launch a new thread from within a dll that does some work.
The dlls destructor could be called before the new thread finishes its work.
If so I want to set a boolean flag in the destructor to tell the thread to return and not continue.
If I try the following then I find that because the destructor is called and MyDll goes out of scope then m_cancel is deleted and its value is unreliable (Sometimes false, sometimes true) so I cannot use this method.
Method 1
//member variable declared in header file
bool m_cancel = false;
MyDll:~MyDll()
{
m_cancel = true;
}
//Function to start receiving data asynchronously
void MyDll::GetDataSync()
{
std::thread([&]()
{
SomeFunctionThatCouldTakeAWhile();
if( m_cancel == true )
return;
SomeFunctionThatDoesSomethingElse();
}
}
So I have looked at this example Replacing std::async with own version but where should std::promise live? where a shared pointer is used which can be accessed from both threads.
So I was thinking that I should:
Create a shared pointer to a bool and pass it to the new thread that I have kicked off.
In the destructor, change the value of this shared pointer and check it in the new thread.
Here is what I have come up with but I'm not sure if this is the proper way to solve this problem.
Method 2
//member variable declared in header file
std::shared_ptr<bool> m_Cancel;
//Constructor
MyDll:MyDll()
{
m_Cancel = make_shared<bool>(false);
}
//Destructor
MyDll:~MyDll()
{
std::shared_ptr<bool> m_cancelTrue = make_shared<bool>(true);
m_Cancel = std::move(m_cancelTrue);
}
//Function to start receiving data asynchronously
void MyDll::GetDataSync()
{
std::thread([&]()
{
SomeFunctionThatCouldTakeAWhile();
if( *m_Cancel.get() == true )
return;
SomeFunctionThatDoesSomethingElse();
}
}
If I do the above then the if( *m_Cancel.get() == true ) causes a crash (Access violation)
Do I pass the shared pointer by value or by reference to the std::thread??
Because its a shared pointer, will the copy that the std::thread had still be valid even MyDll goes out of scope??
How can I do this??
Method 3
//Members declared in header file
std::shared_ptr<std::atomic<bool>> m_Cancel;
//Constructor
MyDll:MyDll()
{
//Initialise m_Cancel to false
m_Cancel = make_shared<std::atomic<bool>>(false);
}
//Destructor
MyDll:~MyDll()
{
//Set m_Cancel to true
std::shared_ptr<std::atomic<bool>> m_cancelTrue = make_shared<std::atomic<bool>>(true);
m_Cancel = std::move(m_cancelTrue);
}
//Function to start receiving data asynchronously
void MyDll::GetDataSync()
{
std::thread([=]() //Pass variables by value
{
SomeFunctionThatCouldTakeAWhile();
if( *m_Cancel.get() == true )
return;
SomeFunctionThatDoesSomethingElse();
}
}
What I fund is that when the destructor gets called and then if( *m_Cancel.get() == true ) is called it always crashes.
Am I doing something wrong??

Solution
I have added in a mutex to protect against the dtor returning after cancel has been checked in the new thread.
//Members declared in header file
std::shared_ptr<std::atomic<bool>> m_Cancel;
std::shared_ptr<std::mutex> m_sharedMutex;
//Constructor
MyDll:MyDll()
{
//Initialise m_Cancel to false
m_Cancel = make_shared<std::atomic<bool>>(false);
m_sharedMutex = make_shared<std::mutex>();
}
//Destructor
MyDll:~MyDll()
{
//Set m_Cancel to true
std::shared_ptr<std::atomic<bool>> m_cancelTrue = make_shared<std::atomic<bool>>(true);
std::lock_guard<std::mutex> lock(*m_sharedMutex);//lock access to m_Cancel
{
*m_Cancel = std::move(cancelTrue);
}
}
//Function to start receiving data asynchronously
void MyDll::GetDataSync()
{
auto cancel = this->m_Cancel;
auto mutex = this->m_sharedMutex;
std::thread([=]() //Pass variables by value
{
SomeFunctionThatCouldTakeAWhile();
std::lock_guard<std::mutex> lock(*mutex);//lock access to cancel
{
if( *cancel.get() == true )
return;
SomeFunctionThatDoesSomethingElse();
}
}
}
std::atomic<bool> flagin yourMyDllthat you use to communicate to interrupt the thread. The thread's function then checks thisflagregularly and returns if it's set to true. In your destructor ofMyDllyou setflagtotrueand thenjoin()your thread (wait for its termination) which shouldn't take long anymore, because you sent the interrupt signal.