views:

144

answers:

3

This question is a refinement of this one, which went in a different direction than expected.

In my multithreaded application, the main thread creates parameters and stores them :

typedef struct {
    int parameter1;
    double parameter2;
    float* parameter3;
} jobParams;

typedef struct {
    int ID;
    void* params;
} jobData;

std::vector<jobData> jobs;

// main thread
for (int i = 0; i < nbJobs; ++i) {
    jobParams* p = new jobParams;
    // fill and store params

    jobData data;
    data.ID = i;
    data.params = p;

    jobs.push_back(data);
}

// start threads and wait for their execution

// delete parameters
for (int i = 0; i < jobs.size(); ++i) {
    delete jobs[i].params;
}

Then, each thread gets a pointer to a set of parameters, and calls a job function with it :

// thread (generic for any job function and any type of params)
jobData* job = main->getNextParams();
jobFunction(job->ID, job->params);

The whole thing takes void* as argument to be able to use any structure for the parameters, but then the job function casts it back to the right struct :

void* jobFunction(void* param) {
    jobParams* params = (jobParams*) param;
    // do stuff
    return 0;
}

My problem is the following : if I delete params at the end of jobFunction(), it works perfectly. However, I'd prefer to have the deletion taken care of by the threads or the main thread, such that I don't have to remember to delete the params for each jobFunction() that I write.

If I try to delete params just after calling jobFunction() in the treads, or even in the main thread after being sure that all threads are done (and thus the params are not needed anymore), I get a heap corruption error :

HEAP[prog]: Invalid Address specified to RtlFreeHeap( 02E90000, 03C2EE38 )

I'm using Visual Studio 2008 Pro, and I thus can't use valgrind or other *nix tools for debugging. All access to the main thread from the "child threads" are synchronized using a mutex, so the problem is not that I delete the same parameters twice.

In fact, by using VS memory viewer, I know that the memory pointed by the jobParams pointer does not change between the end of jobFunction() and the point where I try do delete it (either in the main thread or in the "child threads").

EDIT: I added the definition of both structures, as well as the way I'd like to delete the params.

+1  A: 

That sort of bug generally means you have a data race somewhere. Does main->getNextParams() do the right thing even if it's called by several threads at once? If it gives the same params to both, you could have a double-free in your hands.

Also, instead of

jobFunction(jobData->ID, jobData->params);

You probably meant

jobFunction(job->ID, job->params);
redtuna
Yes, getNextParams() is thread safe. I use a mutex to make sure that I don't return the same parameters twice. Anyway, if it was such problem, deleting params in jobFunction() wouldn't work either.
Wookai
OK. I thought it was worth checking, because debugging based on "if the problem were X then my program should have crashed earlier" sometimes fail me, as "undefined behavior" sometimes just does the right thing anyways: so for all I know a double delete might sometimes work and sometimes not, for reasons beyond mere mortals like me.
redtuna
Yep, I definitely agree. I double checked that sort of synchronization issue before looking anywhere else.
Wookai
+1  A: 

To debug it you could add a deleted member to the jobParams class and set that to true instead of actually deleting the object. Then see check the deleted flag in every method of jobParams and throw an exception if it's true. Then see where the exception gets thrown.

jon hanson
It's not exactly a class and all, but I got your point. I'll try that.
Wookai
Oops. Corrected JobFunction to jobParams.
jon hanson
+2  A: 

Just as a thought .. can you try

for (int i = 0; i < jobs.size(); ++i) {
    delete (jobParams*)jobs[i].params;
}

newing a type jobParams and then deleteing a void* might be the cause of your problems.

Is there any reason you store params as a void* in jobData? I'd argue if you wish to have different types of jobParams then you should be using an inheritance hierarchy and not blindly casting to a void*.

Goz
Yes, the reason I'm using void* is to be able to have different kinds of parameters. I could use inheritance but I find the void* solution less complex.
Wookai
and does casting before delete fix your problem? Because any which way you look at it it is not the "right" solution.
Goz
to use void* that is :)
Goz
Have just checked it under VS2008 and a simplified attempt does not throw the error you talk of ...
Goz
it does if i delete the item twice though. I think we need to see more of your code because it definitely appears you are freeing more than once.
Goz
accepted this answer as you were the most involved. I did it differently finally, I couldn't find the reason of this behavior ! Thanks anyway !
Wookai