0

I'm trying to write a very simple memory size counter using a volatile static variable to keep track of the allocations inside a certain class.

I've written the class' destructor to atomically decrement this static volatile integer, but the compiler (VC VS2010) optimizes it away (inlining it, although it should not - the variable inside the destructor is volatile, perhaps it should take that into account..?). Instead, that destructor seems to never get called (i.e. the class's members are disposed off correctly, but apparently not using the custom destructor).

I can't write code snippets (it's from a "classified" source code). But, basically, it all looks like this:

the header:

class CSomething
{
  CObject m_object;
  ...
  public:
  static volatile int ms_counter;
  ~CSomething();

}

the .cpp:

CSomething::CSomething()
{
    DoStuffWith(m_object);
    AtomicAdd(ms_counter, m_object.GetTotalSize());
}

CSomething::~CSomething()
{
    AtomicDecrement(ms_counter, m_object.GetTotalSize());
} 

To resume: the ~CSomething() destructor gets inlined or optimized away (?) since I can't place a breakpoint inside it. The value of ms_counter only increases, but never decreases (although the m_object's destructor clearly gets called). (NO, I cannot decrement the counter inside of m_object's own destructor :( ).

The question, again is: what could really happen? How can I avoid this issue? Forcing no inline via compiler flags does not do the trick either.. (and I wouldn't want it not to be inlined since it might damage the performance).

teodron
  • 1,410
  • 1
  • 20
  • 41
  • Why don't you use /Fc compiler flag and examine the resulting assembly code yourself? – Tomek Aug 14 '13 at 09:28
  • 10x, will do it when I have the time - I was hoping someone had a list of 2 or 3 possible causes for this behaviour. – teodron Aug 14 '13 at 09:30
  • Are you sure `m_object.GetTotalSize()` is returning the right thing in your destructor? It would be a terrible bug to do this, so I suspect it's "something else". Looking at the assembler code or compiling with a different compiler version may be reasonable steps to rule out compiler bug. – Mats Petersson Aug 14 '13 at 09:31
  • Hm, I'll see what I can do with the assembler.. I believe the total size to be correct.. but, since I can't place a breakpoint, I can't tell (maybe I'll spam the console output with its values). – teodron Aug 14 '13 at 09:36
  • I assume you initialize CSomething::ms_counter correctly? – Max Truxa Aug 14 '13 at 09:54
  • yes, it is defined in the .cpp file and set to zero. – teodron Aug 14 '13 at 09:54
  • I've found the mangled symbol in the assembly output, but I don't know how to see where it might get called.. it does not appear in any other parts of the asm output.. I can see that the assembly generated for this destructor performs the decrement, after which it destroys the `m_object`. Any other ideas? – teodron Aug 14 '13 at 10:19

1 Answers1

0

The problem here is, that m_object is already destroyed, when you reach the body of CSomething's destructor. (edited because it is plain wrong) Consider the following code:

#include <iostream>

using std::cout;
using std::endl;

class A
{
    public:
    A(){cout << "A()" << endl;}
    ~A(){cout << "~A()" << endl;}
};

class B
{
    A a;
    public:
    B(){cout << "B()" << endl;}
    ~B(){cout << "~B()" << endl;}
};

int main(void)
{
    B b;
    return 0;
}

compile and run:

$ g++ test.cc -o test && ./test
A()
B()
~B()
~A()

So you will have to track and save the size of m_object before the destructor is called.

steffen
  • 8,572
  • 11
  • 52
  • 90
  • Probably out of haste, but you made a mistake here. If `B=CSomething` and `A=CObject`, the behaviour is correct as I said. If what you said were true, it would defeat the purpose of a destructor's mechanism (i.e. it lets you handle the way an object's insides are "cleaned"). So, in my case, the code inside `~CSomething` is called __before__ `~CObject` for the reason I gave just previously. – teodron Aug 14 '13 at 13:21
  • @teodron: try it! Igave you the complete code and the line to compile it. It takes you 10 seconds... If you want to handle the destruction of the class' contents, you have to have pointers as members. Then you can call delete on them in the destructor. – steffen Aug 15 '13 at 05:28
  • I did try it. If you have two classes, X and Y, and if X is a member of Y, then when you create an `Y` instance, the X object member has to be created first (e.g. in the initialization list explicitly, or internally), then the logic in Y() is invoked. When destroying something, you have the freedom of invoking the clean-up logic _first_ so that you can decide _how_ to dispose of the class's managed data. If it were otherwise, then you'd be in serious trouble because of dangling pointers, memory leaks and undefined behaviour. If `B=CSomething` and `A=CObject` it all fits my scenario. – teodron Aug 15 '13 at 06:15
  • Some references: constructor destructor order http://stackoverflow.com/questions/2254263/order-of-member-constructor-and-destructor-calls and something explaining that members get cleaned up after the destructor of their owner finishes its logic http://stackoverflow.com/questions/8825339/c-destructors-how-to-manually-destroy-member-variables . If you need further explanation, I can post the assembly listings to clarify my scenario. – teodron Aug 15 '13 at 06:29
  • @teodron: argh! You are totally right, I am wrong. Sorry. The output that I posted is right (I copied it from the terminal). The explanation is wrong. This is how blind one is at times... – steffen Aug 15 '13 at 12:59