views:

285

answers:

3

Hi!

I've got a strange problem and really don't understand what's going on.

I made my application multithreaded using the MFC multithreadclasses.

Everything works well so far, but now:

somewhere in the beginning of the code i create the threads:

            m_bucketCreator = new BucketCreator(128,128,32);
 CEvent* updateEvent = new CEvent(FALSE, FALSE);
 CWinThread** threads = new CWinThread*[numThreads];
 for(int i=0; i<8; i++){
  threads[i]=AfxBeginThread(&MyClass::threadfunction, updateEvent);
  m_activeRenderThreads++;
 }

this creates 8 threads working on this function:

UINT MyClass::threadfunction( LPVOID params ) //executed in new Thread
{

Bucket* bucket=m_bucketCreator.getNextBucket();

    ...do something with bucket...

delete bucket;

}

m_bucketCreator is a static member. now i get some thread error in the deconstructor of Bucket on the attempt to delete a buffer (however, the way i understand it this buffer should be in the memory of this thread, so i don't get why there is an error.) on the attempt of delete[] buffer, the error happens in _CrtIsValidHeapPointer() in dbgheap.c

Visual studio outputs the message that it trapped a halting point and this can be either due to heap corruption or because the user pressed f12 (i didn't ;) )

class BucketCreator {
public:
    BucketCreator();

~BucketCreator(void);

void init(int resX, int resY, int bucketSize);

Bucket* getNextBucket(){

Bucket* bucket=NULL;
//enter critical section
CSingleLock singleLock(&m_criticalSection);
singleLock.Lock();

int height = min(m_resolutionY-m_nextY,m_bucketSize);
int width = min(m_resolutionX-m_nextX,m_bucketSize);

bucket = new Bucket(width, height);

//leave critical section
singleLock.Unlock();
return bucket;
}

private:

int m_resolutionX;
int m_resolutionY;
int m_bucketSize;

int m_nextX;
int m_nextY;

//multithreading:
CCriticalSection m_criticalSection;
};

and class Bucket:

class Bucket : public CObject{
DECLARE_DYNAMIC(RenderBucket)
public:

Bucket(int a_resX, int a_resY){

resX = a_resX;
resY = a_resY;
buffer = new float[3 * resX * resY];

int buffersize = 3*resX * resY; 
for (int i=0; i<buffersize; i++){
 buffer[i] = 0;
}
}

~Bucket(void){
delete[] buffer;
buffer=NULL;
}


int getResX(){return resX;}
int getResY(){return resY;}
float* getBuffer(){return buffer;}

private:
int resX;
int resY;
float* buffer;

Bucket& operator = (const Bucket& other) { /*..*/}
Bucket(const Bucket& other) {/*..*/}
};

can anyone tell me what could be the problem here? thanks a lot!

edit: this is the other static function i'm calling from the threads. is this safe to do?

static std::vector<Vector3> generate_poisson(double width, double height, double min_dist, int k, std::vector<std::vector<Vector3> > existingPoints)
{
 CSingleLock singleLock(&m_criticalSection);
 singleLock.Lock();

 std::vector<Vector3> samplePoints = std::vector<Vector3>();

            ...fill the vector...

            singleLock.Unlock();
            return samplePoints;
     }
A: 
Heath Hunnicutt
sorry, this was a typing error. the constructor is there - i just named it wrong. now i corrected the mistake
Mat
Heath Hunnicutt
hi - i tried this and the problem is still exactly the same
Mat
Did you also take care of the default constructor "Bucket()"?
Heath Hunnicutt
i don't really understand what you mean by other uses of new and delete.. the error happens on the line where delete[] buffer is called. on the call stack, the next position is the delete[] function in afxmem.cpp, from there delete(void* p) is called (also in afxmem.cpp), then _free_dbg in dbgheap.c, then _free_dbg_nolock() in dgbheap.c and finally _CrtIsValidHeapPointer (also in dgbheap.c) where the execution stops
Mat
What I mean is that you might have a big program of which this Bucket class is a small part. You may be calling new and delete from the other part of your program, and a buffer in that part is being corrupted.
Heath Hunnicutt
Mat
hmmmm - but the call stack tells me that the error happens on this line - is it possible that it's caused by another delete function? what do i have to look for?
Mat
That's what I mean about the heap data structure. The heap blocks are adjacent to the linked-list pointers which define the heap. If you write beyond the end of a heap block, you can easily corrupt these pointers. MUCH later, when delete is called, it attempts to use these pointers which you corrupted earlier. The heap corruption is never detected when it occurs, because the memory containing the linked list pointers is not being actively examined by any part of the process at the time it is being accidentally over-written.
Heath Hunnicutt
I'm pretty sure this is the problem! I actually added something in another static function which has a critical section and does memory allocations (but has nothing to do with the Bucket stuff). so i'm going to check this now
Mat
I added the function i'm calling from the threads at the end of my problem description. is this safe to do? in my Bucket class I had to use the DECLARE_DYNAMIC(RenderBucket) macro (i don't really understand why, but it didn't work without) - so could my problem be that i return in this function an std::vector, but std::vector does not use this macro?
Mat
Huh. Try sprinkling _CrtCheckMemory() before and after you return this vector. Also add it prior to the delete which fails, so you can verify that your problem is corrupt heap (rather than double free).
Heath Hunnicutt
I didn't know about this - you was right :) _CrtCheckMemory() is very helpful! thank you very much!
Mat
A: 

You're constructing a RenderBucket. Are you sure you're calling the 'Bucket' class's constructor from there? It should look like this:

class RenderBucket : public Bucket {
  RenderBucket( int a_resX, int a_resY )
    : Bucket( a_resX, a_resY )
  {
  }
}

Initializers in the Bucket class to set the buffer to NULL is a good idea... Also making the Default constructor and copy constructor private will help to make double sure those aren't being used. Remember.. the compiler will create these automatically if you don't:

Bucket();  <-- default constructor
Bucket( int a_resx = 0, int a_resy = 0 )  <-- Another way to make your default constructor
Bucket(const class Bucket &B)  <-- copy constructor
Kieveli
damn - same copy paste error again - sorry :( it should read new Bucket
Mat
If you simplify the problem, it might go away... Your '...' with doing something with bucket may be the source of your problems - it's too difficult to tell! Although suspicions on the constructor not initializing the memory for the buffer variable would make sense if it's failing on the destructor's delete.
Kieveli
Oh hey good point about the copy constructor, only 42 minutes after I posted the same thing. LOL
Heath Hunnicutt
+1  A: 

All the previous replies are sound. For the copy constructor, make sure that it doesn't just copy the buffer pointer, otherwise that will cause the problem. It needs to allocate a new buffer, not the pointer value, which would cause an error in 'delete'. But I don't get the impression that the copy contructor will get called in your code.

I've looked at the code and I am not seeing any error in it as is. Note that the thread synchronization isn't even necessary in this GetNextBucket code, since it's returning a local variable and those are pre-thread.

Errors in ValidateHeapPointer occur because something has corrupted the heap, which happens when a pointer writes past a block of memory. Often it's a for() loop that goes too far, a buffer that wasn't allocated large enough, etc.

The error is reported during a call to 'delete' because that's when the heap is validated for bugs in debug mode. However, the error has occurred before that time, it just happens that the heap is checked only in 'new' and 'delete'. Also, it isn't necessarily related to the 'Bucket' class.

What you need to need to find this bug, short of using tools like BoundsChecker or HeapValidator, is comment out sections of your code until it goes away, and then you'll find the offending code.

There is another method to narrow down the problem. In debug mode, include in your code, and sprinkle calls to _CrtCheckMemory() at various points of interest. That will generate the error when the heap is corrupted. Simply move the calls in your code to narrow down at what point the corruption begins to occur.

I don't know which version of Visual C++ you are using. If you're using a earlier one like VC++ 6.0, make sure that you are using the Multitreaded DLL version of the C Run Time Library in the compiler option.

fredr
hey!Thank you very much! _CrtCheckMemory() helped me to track down where the heap got corrupted. I indeed had a prolbem with calculating the index in one of the loops! :)
Mat