2
votes

I get a segmentation fault when attempting to delete this.

I know what you think about delete this, but it has been left over by my predecessor. I am aware of some precautions I should take, which have been validated and taken care of.

I don't get what kind of conditions might lead to this crash, only once in a while. About 95% of the time the code runs perfectly fine but sometimes this seems to be corrupted somehow and crash.

The destructor of the class doesn't do anything btw.

Should I assume that something is corrupting my heap somewhere else and that the this pointer is messed up somehow?

Edit : As requested, the crashing code:

long CImageBuffer::Release()
{
  long nRefCount = InterlockedDecrement(&m_nRefCount);
  if(nRefCount == 0)
  {
    delete this;
  }
  return nRefCount;
}

The object has been created with a new, it is not in any kind of array.

3
"It hurts when I do this" Then don't do it ... or show more useful info for more useful answers. (code, stack, etc) - AJG85
A few oldnewthing blog entries on COM object destructors that might be helpful: 1, 2, 3. - user786653
The InterlockedDecrement part bothers me: Is your object living in multiple threads? And is m_nRefCount correctly aligned LONG? - paercebal
Using InterlockedDecrement only makes the decrement atomic. You need the decrement and the test in the if statement to be atomic. - JoeG
@JoeGauterin: Huh? Why does the test need the be atomic? All that matters is that it only be deleted once, right? It doesn't matter how long after the count reaches zero the object is deleted? - David Schwartz

3 Answers

1
votes

The most obvious answer is : don't delete this.

If you insists on doing that, then use common ways of finding bugs :
1. use valgrind (or similar tool) to find memory access problems
2. write unit tests
3. use debugger (prepare for loooong staring at the screen - depends on how big your project is)

1
votes

It seems like you've mismatched new and delete. Note that delete this; can only be used on an object which was allocated using new (and in case of overridden operator new, or multiple copies of the C++ runtime, the particular new that matches delete found in the current scope)

0
votes

Crashes upon deallocation can be a pain: It is not supposed to happen, and when it happens, the code is too complicated to easily find a solution.

Note: The use of InterlockedDecrement have me assume you are working on Windows.

Log everything

My own solution was to massively log the construction/destruction, as the crash could well never happen while debugging:

  1. Log the construction, including the this pointer value, and other relevant data
  2. Log the destruction, including the this pointer value, and other relevant data

This way, you'll be able to see if the this was deallocated twice, or even allocated at all.

... everything, including the stack

My problem happened in Managed C++/.NET code, meaning that I had easy access to the stack, which was a blessing. You seem to work on plain C++, so retrieving the stack could be a chore, but still, it remains very very useful.

You should try to load code from internet to print out the current stack for each log. I remember playing with http://www.codeproject.com/KB/threads/StackWalker.aspx for that.

Note that you'll need to either be in debug build, or have the PDB file along the executable file, to make sure the stack will be fully printed.

... everything, including multiple crashes

I believe you are on Windows: You could try to catch the SEH exception. This way, if multiple crashes are happening, you'll see them all, instead of seeing only the first, and each time you'll be able to mark "OK" or "CRASHED" in your logs. I went even as far as using maps to remember addresses of allocations/deallocations, thus organizing the logs to show them together (instead of sequentially).

I'm at home, so I can't provide you with the exact code, but here, Google is your friend, but the thing to remember is that you can't have a __try/__except handdler everywhere (C++ unwinding and C++ exception handlers are not compatible with SEH), so you'll have to write an intermediary function to catch the SEH exception.

Is your crash thread-related?

Last, but not least, the "I happens only 5% of the time" symptom could be caused by different code path executions, or the fact you have multiple threads playing together with the same data.

The InterlockedDecrement part bothers me: Is your object living in multiple threads? And is m_nRefCount correctly aligned and volatile LONG?

The correctly aligned and LONG part are important, here.

If your variable is not a LONG (for example, it could be a size_t, which is not a LONG on a 64-bit Windows), then the function could well work the wrong way.

The same can be said for a variable not aligned on 32-byte boundaries. Is there #pragma pack() instructions in your code? Does your projet file change the default alignment (I assume you're working on Visual Studio)?

For the volatile part, InterlockedDecrement seem to generate a Read/Write memory barrier, so the volatile part should not be mandatory (see http://msdn.microsoft.com/en-us/library/f20w0x5e.aspx).