RSS
Facebook
Twitter

Monday, September 09, 2013

C++ Bug hunt - shared_ptr misuse

C makes it easy to shoot yourself in the foot; C++ makes it harder, but when you do it blows your whole leg off.
from Bjarne Stroustrup's FAQ: Did you really say that?
I want to share with you a strange bug I’ve been struggling with last week.
I had an application that crashed whenever a user logged out and then logged back. I couldn’t understand why during the 2nd login attempt all hell broke loose – I got the finger equivalent of the C++ world - “corrupted memory” exception.
I had the best minds try and use their debugging powers to try and catch the cause of that memory issue thing but to no avail. That is until a simple breakpoint solved the mystery.

The cast

Due to NDA and time constraint let me simplify the problem- I had two classes:
  1. a Factory of sorts
  2. Some Object created by that factory
The factory definition looks something like this:
#pragma once

#include <memory>
#include "MyObject.h"

class MyFactory
{
public:
    std::shared_ptr<myobject> GetObject();

    //...
};

The “Object” class had many important methods that did interesting stuff to the benefit of the users – however for this example we only care about its constructor:

#pragma once
#include <memory>

class MyFactory;

class MyObject
{
public:
    MyObject(std::shared_ptr<myfactory> factory);
    ~MyObject(void);

    //...
};

And that’s it – see the problem yet?

Obviously this cyclic reference (factory is passed to object that was created by factory)  is asking for trouble  but that’s not the problem (yet).

The problem

Let me show you how GetObject was implemented:

using namespace std;

std::shared_ptr<myobject> MyFactory::GetObject()
{
    return make_shared<MyObject>(shared_ptr<myfactory>(this));
}

Do you see the problem? If not - take a minute to think about it…

In case you’re not familiar with shared_ptr – it’s essentially a smart pointerwhich was introduced in C++ 11 (and before that in Boost):
Manages the storage of a pointer, providing a limited garbage-collection facility, possibly sharing that management with other objects.
from cplusplus.com

The problem is that a misguided soul (read: yours truly) has used it the wrong way.

shared_ptr works by reference counting – each new assignment increase that count and every time the object goes out of scope the count is decreased.
  • When I’ve created a new shared_ptr instance from this it was created with count of one.
  • All was good and well until after MyObject constructor ended in which point the count was decreased.
  • Since  no other shared_ptr objects were holding the pointer – MyFactory desructor was called
  • An exception – not right away (we’re talking C++ here) but most of the time the next time you try and do something with MyFactory
The fix was simple – I’ve changed the c’tor to receive MyFactory as a plain-old-honest-to-god reference.

The lesson learnt

shared_ptr is a great thing – it’s one the useful new gadgets that made it into the new C++ 11.

I’m using it all the time – it help me write simple, elegant C++ code, But like everything else in the C++ world – make sure you understand what you’re doing.


Happy coding…

0 comments:

Post a Comment

Related Posts Plugin for WordPress, Blogger...