Somehow, accessing a shared_ptr without dereferencing it is causing a Signal 11 (SIGSEGV) on Android.
I have a run() function in A that acquires a lock for it's instance of B and calls B::top(). There is only one instance of A. A has other public methods that other threads might call to modify mB (thus the mutex), but they are not being called by anything yet.
LogCat Error:
04-17 15:15:16.903: A/libc(11591): Fatal signal 11 (SIGSEGV) at 0x00000024 (code=1)
In class A:
std::thread mControllerThread;
std::mutex mBMutex;
shared_ptr<B> mB;
A() {
mB.reset( new B() );
mControllerThread = std::thread( std::bind(&A::run, this) );
}
//...
void run() {
std::unique_lock<std::mutex > lk(mBMutex);
shared_ptr<const Event> event = mB->top(B::Scope::FUTURE);
}
In class B:
shared_ptr<EventHeap> mFuture;
B() {
mFuture.reset( new EventHeap() );
}
//...
shared_ptr<const Event> top(Scope scope, int mask=EVENT_MASK_SUPPORTED) const {
shared_ptr<const Event> event;
if(scope == Scope::PAST) {
//...
} else if(scope == Scope::FUTURE) {
LOGD(LOG_TAG, "Testing mFuture ptr");
// Fails here with any of these versions
if(mFuture) {
// if(mFuture.get() != NULL) {
// if(mFuture != nullptr) {
LOGD(LOG_TAG, "Getting top from FUTURE");
event = mFuture->top(mask);
} else {
LOGE(LOG_TAG, "mFuture is null");
}
}
return event;
}
So how can accessing a smart pointer without dereferencing it possibly cause a segfault? Thanks!
The statement you point test if the shared_ptr is initialized with a non-null pointer.
if(mFuture) // can be seen as if(mFuture.privateMember_Ptr != nullptr)
It is pretty clear that the pointer itself is not dereferenced, but the value of the pointer is accessed. So this memory location seems invalid. Now where is this memory location? It is part of mFuture which is itself part of B. Let's rewrite the mFuture to show what we really dereference:
if(this->mFuture.privateMember_Ptr != nullptr)
It seems that "this" is invalid, you can start by printing it in the top method, and "unwind" the stack with the same debugging. Looking at the source, "this" should correspond to mB in class A. So you can print mB in A::run() before calling B::top().
mB is initialized in A's ctor with a "new B()". Do you check somewhere that this memory allocation succeed? (exceptions are disabled by default on Android AFAIK, so new can return nullptr)
Related
I have the following code:
MyClass.h:
static MyMutex instanceMutex;
static MyClass* getInstance();
static void deleteInstance();
MyClass.c:
MyMutex MyClass::instanceMutex;
MyClass* MyClass::getInstance()
{
if (theInstance == 0)
{
instanceMutex.acquire();
if (theInstance == 0)
{
theInstance = new MyClass();
}
instanceMutex.release();
}
return theInstance;
}
void MyClass::deleteInstance()
{
if (theInstance != 0)
{
instanceMutex.acquire();
if (theInstance != 0)
{
theInstance->finalize();
delete theInstance;
theInstance = 0;
}
instanceMutex.release();
}
return;
}
I have 2 questions on this:
Is the above code correct and safe?
After I call 'delete theInstance' in MyClass::deleteInstance(), I then call
theInstance = 0;
instanceMutex.release();
But if the instance is deleted than how is that even possible? isn't the memory of the class gone?
If it's a singleton - it is defined to have exactly one instance - if you delete it - this drops to 0
So it seems you should not support delete at all
Here be a problem:
if (theInstance == 0) // <- Some other thread might see a non-null theInstance
{ // before the constructor below returns
instanceMutex.acquire();
if (theInstance == 0)
{
theInstance = new MyClass(); // <- This might set theInstance to something
// before the constructor returns.
}
instanceMutex.release();
}
You may want to implement some sort of reference counting system (like using shared_ptr) and initializing it in a similar manner, though taking care to ensure that its instance pointer is not set before the initialization completes.
If you are feeling adventurous, you could also try:
if (theInstance == 0)
{
instanceMutex.acquire();
if (theInstance == 0)
{
MyClass * volatile ptr = new MyClass();
__FuglyCompilerSpecificFenceHintThatMightNotActuallyDoAnything();
theInstance = ptr; // <- Much hilarity may ensue if this write is not atomic.
}
instanceMutex.release();
}
This might fix it, and it might not. In the second case, it depends on how your compiler handles volatile, and weather or not pointer-sized writes are atomic.
In one project we inherited from some external company I've seen a whole nightmare class of errors caused by someone deleting the singleton. Perhaps in your rare case deleting the singleton doesn't have any side effects for the whole application, as you can be sure there is no instance of this singleton at use, but, generally, it's an excellent example of bad design. Someone could learn from your example and it would be a bad - even harmful - lesson. In case you need to clean up something in the singleton when the application exit, use the pattern ith returning reference to the singleton, like in example:
#include <iostream>
using namespace std;
class singleton {
private:
singleton() {
cout << "construktor\n";
}
~singleton() {
cout << "destructor\n";
}
public:
static singleton &getInstance() {
static singleton instance;
cout << "instance\n";
return instance;
}
void fun() {
cout << "fun\n";
}
};
int main() {
cout << "one\n";
singleton &s = singleton::getInstance();
cout << "two\n";
s.fun();
return 0;
}
I prefer to implement singletons in C++ in the following manner:
class Singleton
{
public:
static Singleton& instance()
{
static Singleton theInstance;
return theInstance;
}
~Singleton()
{
// Free resources that live outside the processes life cycle here,
// if these won't automatically be released when the occupying process
// dies (is killed)!
// Examples you don't have to care of usually are:
// - freeing virtual memory
// - freeing file descriptors (of any kind, plain fd, socket fd, whatever)
// - destroying child processes or threads
}
private:
Singleton()
{
}
// Forbid copies and assignment
Singleton(const Singleton&);
Singleton& operator=(const Singleton&);
};
You can have locking mechanisms also, to prevent concurrent instantiation from multiple threads (will need a static mutex of course!). But deletion is left to the (OS specific) process context here.
Usually you don't need to care about deletion of singleton classes, and how their acquired resources are released, because this is all handled by the OS when a process dies.
Anyway there might be use cases, when you want to have your singleton classes some backup points on program crash situations. Most C++ crt implementations support calling destructors of statically allocated instances, but you should take care not to have ordered dependencies for any of those destructors.
getInstance and delteInstance should be static, so they can only work with static members of the class. The static members don't get destroyed with the instance.
If your code is multithreaded, the code is not safe. There's no way to make sure there's no pointer to the instance held in some running context.
I have the following code:
MyClass.h:
static MyMutex instanceMutex;
static MyClass* getInstance();
static void deleteInstance();
MyClass.c:
MyMutex MyClass::instanceMutex;
MyClass* MyClass::getInstance()
{
if (theInstance == 0)
{
instanceMutex.acquire();
if (theInstance == 0)
{
theInstance = new MyClass();
}
instanceMutex.release();
}
return theInstance;
}
void MyClass::deleteInstance()
{
if (theInstance != 0)
{
instanceMutex.acquire();
if (theInstance != 0)
{
theInstance->finalize();
delete theInstance;
theInstance = 0;
}
instanceMutex.release();
}
return;
}
I have 2 questions on this:
Is the above code correct and safe?
After I call 'delete theInstance' in MyClass::deleteInstance(), I then call
theInstance = 0;
instanceMutex.release();
But if the instance is deleted than how is that even possible? isn't the memory of the class gone?
If it's a singleton - it is defined to have exactly one instance - if you delete it - this drops to 0
So it seems you should not support delete at all
Here be a problem:
if (theInstance == 0) // <- Some other thread might see a non-null theInstance
{ // before the constructor below returns
instanceMutex.acquire();
if (theInstance == 0)
{
theInstance = new MyClass(); // <- This might set theInstance to something
// before the constructor returns.
}
instanceMutex.release();
}
You may want to implement some sort of reference counting system (like using shared_ptr) and initializing it in a similar manner, though taking care to ensure that its instance pointer is not set before the initialization completes.
If you are feeling adventurous, you could also try:
if (theInstance == 0)
{
instanceMutex.acquire();
if (theInstance == 0)
{
MyClass * volatile ptr = new MyClass();
__FuglyCompilerSpecificFenceHintThatMightNotActuallyDoAnything();
theInstance = ptr; // <- Much hilarity may ensue if this write is not atomic.
}
instanceMutex.release();
}
This might fix it, and it might not. In the second case, it depends on how your compiler handles volatile, and weather or not pointer-sized writes are atomic.
In one project we inherited from some external company I've seen a whole nightmare class of errors caused by someone deleting the singleton. Perhaps in your rare case deleting the singleton doesn't have any side effects for the whole application, as you can be sure there is no instance of this singleton at use, but, generally, it's an excellent example of bad design. Someone could learn from your example and it would be a bad - even harmful - lesson. In case you need to clean up something in the singleton when the application exit, use the pattern ith returning reference to the singleton, like in example:
#include <iostream>
using namespace std;
class singleton {
private:
singleton() {
cout << "construktor\n";
}
~singleton() {
cout << "destructor\n";
}
public:
static singleton &getInstance() {
static singleton instance;
cout << "instance\n";
return instance;
}
void fun() {
cout << "fun\n";
}
};
int main() {
cout << "one\n";
singleton &s = singleton::getInstance();
cout << "two\n";
s.fun();
return 0;
}
I prefer to implement singletons in C++ in the following manner:
class Singleton
{
public:
static Singleton& instance()
{
static Singleton theInstance;
return theInstance;
}
~Singleton()
{
// Free resources that live outside the processes life cycle here,
// if these won't automatically be released when the occupying process
// dies (is killed)!
// Examples you don't have to care of usually are:
// - freeing virtual memory
// - freeing file descriptors (of any kind, plain fd, socket fd, whatever)
// - destroying child processes or threads
}
private:
Singleton()
{
}
// Forbid copies and assignment
Singleton(const Singleton&);
Singleton& operator=(const Singleton&);
};
You can have locking mechanisms also, to prevent concurrent instantiation from multiple threads (will need a static mutex of course!). But deletion is left to the (OS specific) process context here.
Usually you don't need to care about deletion of singleton classes, and how their acquired resources are released, because this is all handled by the OS when a process dies.
Anyway there might be use cases, when you want to have your singleton classes some backup points on program crash situations. Most C++ crt implementations support calling destructors of statically allocated instances, but you should take care not to have ordered dependencies for any of those destructors.
getInstance and delteInstance should be static, so they can only work with static members of the class. The static members don't get destroyed with the instance.
If your code is multithreaded, the code is not safe. There's no way to make sure there's no pointer to the instance held in some running context.
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();
}
}
}
Step 2 is just wrong. That's a design fault.
Your first mechanism doesn't work for a simple reason. m_cancel==false may be optimized out by the compiler. When the destructor returns, m_cancel ceases to exist, and no statement in the destructor depends on that write. After the destructor returns, it would be Undefined Behavior to access the memory which previously held m_cancel.
The second mechanism (global) fails for a more complex reason. There's the obvious problem that you have only one global m_Cancel (BTW, m_ is a really misleading prefix for something that's not a member). But assuming you only have one MyDll, it can still fail for threading reasons. What you wanted was not a shared_ptr but a std::atomic<bool>. That is safe for access from multiple threads
[edit]
And your third mechanism fails because [=] captures names from the enclosing scope. m_Cancel isn't in that scope, but this is. You don't want a copy of this for the thread though, because this will be destroyed. Solution: auto cancel = this->m_Cancel; std::thread([cancel](...
[edit 2]
I think you really should read up on basics. In the dtor of version 3, you indeed change the value of m_cancel. That is to say, you change the pointer. You should have changed *m_cancel, i.e. what it points to. As I pointed out above, the thread has a copy of the pointer. If you change the original pointer, the thread will continue to point to the old value. (This is unrelated to smart pointers, dumb pointers behave the same).
I need to make own simple thread-safe shared pointer class for embedded devices.
I made counting master pointer and handle as described in Jeff Alger's book (C++ for real programmers). This is my sources:
template <class T>
class counting_ptr {
public:
counting_ptr() : m_pointee(new T), m_counter(0) {}
counting_ptr(const counting_ptr<T>& sptr) :m_pointee(new T(*(sptr.m_pointee))), m_counter(0) {}
~counting_ptr() {delete m_pointee;}
counting_ptr<T>& operator=(const counting_ptr<T>& sptr)
{
if (this == &sptr) return *this;
delete m_pointee;
m_pointee = new T(*(sptr.m_pointee));
return *this;
}
void grab() {m_counter++;}
void release()
{
if (m_counter > 0) m_counter--;
if (m_counter <= 0)
delete this;
}
T* operator->() const {return m_pointee;}
private:
T* m_pointee;
int m_counter;
};
template <class T>
class shared_ptr {
private:
counting_ptr<T>* m_pointee;
public:
shared_ptr() : m_pointee(new counting_ptr<T>()) { m_pointee->grab(); }
shared_ptr(counting_ptr<T>* a_pointee) : m_pointee(a_ptr) { m_pointee->grab(); }
shared_ptr(const shared_ptr<T>& a_src) : m_pointee(a_src.m_pointee) {m_pointee->grab(); }
~shared_ptr() { m_pointee->release(); }
shared_ptr<T>& operator=(const shared_ptr<T>& a_src)
{
if (this == &a_src) return *this;
if (m_pointee == a_src.m_pointee) return *this;
m_pointee->release();
m_pointee = a_src.m_pointee;
m_pointee->grab();
return *this;
}
counting_ptr<T>* operator->() const {return m_pointee;}
};
This works fine if it used in one thread. Suppose I have two threads:
//thread 1
shared_ptr<T> p = some_global_shared_ptr;
//thread 2
some_global_shared_ptr = another_shared_ptr;
This case I can get undefined behaviour if one of threads will be interrupted between memory allocating/deallocating and counter changing. Of course I can enclose shared_ptr::release() into critical section so deletion of the pointer can be made safety. But what can I do with copy constructor? It is possible that constructor will be interrupted during m_pointee construction by another thread which will delete this m_pointee.
The only way I see to make shared_ptr assignement thread-safe is to enclose the assignment (or creation) into critical section. But this must be done in "user code". In other words user of shared_ptr class must take care about safety.
Is it possible to change this realization somehow to make the shared_ptr class thread safe?
=== EDIT ===
After some investigations (thanks to Jonathan) I realized that my shared_ptr has three unsafe places:
Unatomic counter changing
Unatomic assignment operator (source object can be deleted during copying)
shared_ptr copy constructor (very similar to previous case)
First two cases could be easily fixed by adding crtical sections. But I can't realize how to add critical section into copy constructor? Copy of a_src.m_pointee created before any other code in the constructor executed and can be deleted before calling grab. As Jonathan said in his comment it is very difficult to fix this problem.
I made such test:
typedef shared_ptr<....> Ptr;
Ptr p1, p2;
//thread 1
while (true)
{
Ptr p;
p2 = p;
}
//thread 2
while (!stop)
{
p1 = p2;
Ptr P(p2);
}
Of course, it crashed. But I have tried to use std::shared_ptr in VS 2013 C++. And it works!
So it is possible to make thread-safe copy constructor for shared_ptr. But stl sources too difficult for me and I don't understand how they did the trick. Please anyone explain me how it works in STL?
=== EDIT 2 ===
I am sorry, but the test for std::shared_ptr was made wrong. It doesn't pass too exactly as boost::shared_ptr does. Sometimes copy constructor fails to make a copy because source was deleted during copying. In this case empty pointer will be created.
This is hard to get right, I would seriously consider whether you actually need to support concurrent reads and writes of a single object (boost::shared_ptr and std::shared_ptr do not support that unless all accesses are done through the atomic_xxx() functions that are overloaded for shared_ptr and which typically acquire a lock).
For a start you would need to change shared_ptr<T>::m_pointee to atomic<counting_ptr<T>*> so that you can store a new value in it atomically. counting_ptr<T>::m_counter would need to be atomic<int> so the ref-count updates can be done atomically.
Your assignment operator is a big problem, you would need to at least re-order the operations so you increase the ref-count first, and avoid time of check to time of use bugs, something like this (not even compiled, let alone tested):
shared_ptr<T>& operator=(const shared_ptr<T>& a_src)
{
counter_ptr<T>* new_ptr = a_src.m_pointee.load();
new_ptr->grab();
counter_ptr<T>* old_ptr = m_pointee.exchange(new_ptr);
old_ptr->release();
return *this;
}
This form is safe against self-assignment (it just increases the ref-count then decreases it again if the two objects share the same pointee). It's still not safe against a_src changing while you try to copy it. Consider the case where a_src.m_pointee->m_counter == 1 initially. The current thread could call load() to get the other object's pointer, then a second thread could call release() on that pointer, which would delete it, making the grab() call undefined behaviour because it accesses an object that has been destroyed and memory that has been deallocated. Fixing that requires a pretty major redesign and maybe atomic operations that can operate on two words at once.
Getting this right is possible but is hard and you should really reconsider whether it's necessary, or if the code using it can just avoid modifying objects while other threads are reading them, except while the user has locked a mutex or other form of manual synchronisation.
After some investigations I can conclude that it is impossible to make thread-safe shared_ptr class where thread-safety understood as follow:
//thread 1
shared_ptr<T> p = some_global_shared_ptr;
//thread 2
some_global_shared_ptr = another_shared_ptr;
This example doesn't guarantees that p in first thread will point to old or new value of some_global_shared_ptr. In general this example leads to undefined behavior. The only way to make the example safety is to wrap both operators into critical sections or mutial exclusions.
The main problem caused by copy constructor of shared_ptr class. Other problems could be solved using critical sections inside shared_ptr methods.
Just inherit your class from CmyLock and you can make everything thread safe.
I use this class already many years in all my code, usually combined with class CmyThread, which creates a thread that has a very safe mutex. Maybe my answer is a little late, but above answers are not good practice.
/** Constructor */
CmyLock::CmyLock()
{
(void) pthread_mutexattr_init( &m_attr);
pthread_mutexattr_settype( &m_attr, PTHREAD_MUTEX_RECURSIVE);
pthread_mutex_init( &m_mutex, &m_attr);
}
/** Lock the thread for other threads. */
void CmyLock::lock()
{
pthread_mutex_lock( &m_mutex);
}
/** Unlock the thread for other threads. */
void CmyLock::unlock()
{
pthread_mutex_unlock( &m_mutex);
}
Here also the thread class. Try Please copy CmyLock and CmyThread classes to your project and tell when it's working! Although it's made for Linux, also Windows and Mac should be able to run this.
For the include file:
// #brief Class to create a single thread.
class CmyThread : public CmyLock
{
friend void *mythread_interrupt(void *ptr);
public:
CmyThread();
virtual ~CmyThread();
virtual void startWorking() {}
virtual void stopWorking() {}
virtual void work();
virtual void start();
virtual void stop();
bool isStopping() { return m_stopThread; }
bool isRunning() { return m_running && !m_stopThread; }
private:
virtual void run();
private:
bool m_running; ///< Thread is now running.
pthread_t m_thread; ///< Pointer to thread.
bool m_stopThread; ///< Indicate to stop thread.
};
The C++ file:
/** #brief Interrupt handler.
* #param ptr [in] SELF pointer for the instance.
*/
void *mythread_interrupt(void *ptr)
{
CmyThread *irq =
static_cast<CmyThread*> (ptr);
if (irq != NULL)
{
irq->run();
}
return NULL;
}
/** Constructor new thread. */
CmyThread::CmyThread()
: m_running( false)
, m_thread( 0)
, m_stopThread( false)
{
}
/** Start thread. */
void CmyThread::start()
{
m_running =true;
m_stopThread =false;
pthread_attr_t attr;
pthread_attr_init(&attr);
pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE);
int stack_size =8192*1024;
pthread_attr_setstacksize(&attr, stack_size);
pthread_create(&m_thread, &attr, mythread_interrupt, (void*) this);
}
/** Thread function running. */
void CmyThread::run()
{
startWorking();
while (m_running && m_stopThread==false)
{
work();
}
m_running =false;
stopWorking();
pthread_exit(0);
}
/** Function to override for a thread. */
virtual void CmyThread::work()
{
delay(5000);
}
For example, here a simplistic example to store and retrieve 1000 data:
class a : public CmyLock
{
set_safe(int *data)
{
lock();
fileContent =std::make_shared<string>(data);
unlock();
}
get_safe(char *data)
{
lock();
strcpy( data, fileContent->c_str());
unlock();
}
std::shared_ptr<string> fileContent;
};
I have c++ console an application that looks something like this:
SomeObj* obj;
BOOL WINAPI closeHandler(DWORD cEvent)
{
obj->Stop();
while( obj != 0 )
{
Sleep(100);
}
return TRUE;
}
int main(int argc, char* argv[])
{
SetConsoleCtrlHandler( (PHANDLER_ROUTINE)SignalHandler, true );
obj = new SomeObj();
obj->Execute();
delete obj;
return 0;
}
SomeObj::Execute() is essentially a loop that keeps running until SomeObj::Stop() is called. When I do a CTRL+C, I can see that the application deletes obj properly before exiting. However, when I click on the close button on the console window, I find out that obj doesn't get deleted properly.
Further debugging showed that closeHandler is actually called, but somehow obj just doesn't get deleted. Strangely, if I put a breakpoint on the return 0 line and try to close the console window, I end up hitting that breakpoint and see that obj was deleted.
What am I doing wrong here? Is there a better way to deallocate stuff on a console window close event?
I'm guessing your closeHandler() routine never actually completes because it is waiting for obj to become 0 (presumably you mean "NULL" or C++11-style "nullptr" here). Deleting an object does not set pointers to it to null. I'm honestly not sure why it has this loop at all?
Yes, there is a way that doesn't require you to manually call delete or set the pointer to null. Use a smart pointer like std::unique_ptr. You also don't have to wait for the object to be deleted since cleanup will be handled properly once Execute finishes and main returns.
#include <iostream>
#include <memory>
// sample test object that oeprates as described in your question.
struct Object
{
Object() : running_(true) {}
~Object()
{
std::cout << "Object deleted" << std::endl;
}
void Stop() { running_ = false; }
void Execute() { while(running_ == true); }
bool running_;
};
// incredibly smart pointer!
std::unique_ptr<Object> obj;
BOOL WINAPI closeHandler(DWORD)
{
// We need to call stop on the object if it exists
// use appropriate locks for multithreaded environment
if(obj != nullptr)
{
obj->Stop();
// no need to wait
}
return TRUE;
}
int main()
{
SetConsoleCtrlHandler( closeHandler, true );
// Allocate the object and execute
obj.reset(new Object());
obj->Execute();
}
I would suggest that your handler isn't actually deleting the object. You specify obj as a global but do not actually delete it in the closeHandler. Maybe something like the following...
BOOL WINAPI HandlerRoutine(DWORD dwCtrlType)
{
if (CTRL_CLOSE_EVENT == dwCtrlType)
{
if (NULL != obj)
{
delete obj;
obj = NULL
}
}
}