2

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.

Brian Tompsett - 汤莱恩
  • 5,753
  • 72
  • 57
  • 129
Eric
  • 19,525
  • 19
  • 84
  • 147
  • 1
    "It hurts when I do this" Then don't do it ... or show more useful info for more useful answers. (code, stack, etc) – AJG85 Sep 16 '11 at 19:22
  • A few oldnewthing blog entries on COM object destructors that might be helpful: [1](http://blogs.msdn.com/b/oldnewthing/archive/2005/09/27/474384.aspx), [2](http://blogs.msdn.com/b/oldnewthing/archive/2005/09/28/474855.aspx), [3](http://blogs.msdn.com/b/oldnewthing/archive/2009/11/11/9920543.aspx). – user786653 Sep 18 '11 at 09:16
  • The `InterlockedDecrement` part bothers me: Is your object living in multiple threads? And is m_nRefCount correctly aligned `LONG`? – paercebal Sep 18 '11 at 09:39
  • 1
    Using `InterlockedDecrement` only makes the decrement atomic. You need the decrement *and the test in the if statement* to be atomic. – JoeG Sep 18 '11 at 12:00
  • @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 Jul 26 '12 at 00:01

3 Answers3

1

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)

BЈовић
  • 62,405
  • 41
  • 173
  • 273
1

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)

Ben Voigt
  • 277,958
  • 43
  • 419
  • 720
  • 1
    Thats a bit of a leap of faith isn't it? I'd be shocked if thats his problem. Its far more likely to be memory trashing than unmatched new/delete pairs ... – Goz Sep 16 '11 at 19:43
  • @Goz: This encompasses the case pmr mentioned, of objects with static or automatic lifetime, which is one of the more likely scenarios. – Ben Voigt Sep 16 '11 at 19:56
  • 2
    I still think its far more likely that hes trashing his heap somewhere along the line and thus getting dealloc errors. I've seen people making this sort of error far too many times (Then blaming me because they write through my DMA chains that you can't error check post writing) – Goz Sep 16 '11 at 20:22
  • @Goz: That's the problem with crystal ball debugging, isn't it? Different balls give different explanations. – Ben Voigt Sep 16 '11 at 20:29
  • @Goz: Any hints on how to track this? my main problem is that I'm running on an embedded device so debugging is rather painful. I can get gdb to run but I'm not exactly sure about how to use it for this specific problem – Eric Sep 16 '11 at 20:58
  • 2
    @Eric: Print out (or log) the pointer returned by new, also the pointer passed to delete. – Ben Voigt Sep 16 '11 at 21:22
0

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).

Community
  • 1
  • 1
paercebal
  • 81,378
  • 38
  • 130
  • 159
  • I'm working on Linux, my predecessor however comes from a windows background and reimplemented stuff like IRefObj and InterlockedDecrement on Linux. Yes, my thoughts exactly – Eric Sep 19 '11 at 01:04
  • @Eric : o.O . . . Ok, so it does complicate the answer... :-D ... Anyway, if you have multiple threads, then the Linux API behind InterlockedDecrement could well need a volatile, plus a correctly aligned value of the right size (unless your predecessor just put a lock there... Ahem)... As for the stack, then I guess there must be some kind of stack explorer code for Linux. And for the crash handler, I'm not knowledgeable enough of Linux to point you to the right solution (a SIGSEGV signal?)... Do you believe the COM-like ref-counted object implementation was necessary for your projet? – paercebal Sep 19 '11 at 17:09