Deleting pointer sometimes results in heap corruption

前端 未结 7 607
深忆病人
深忆病人 2021-01-07 03:34

I have a multithreaded application that runs using a custom thread pool class. The threads all execute the same function, with different parameters.

These parameters

相关标签:
7条回答
  • 2021-01-07 04:06

    All access to the job queue must be synchronized, i.e. performed only from 1 thread at a time by locking the job queue prior to access. Do you already have a critical section or some similar pattern to guard the shared resource? Synchronization issues often lead to weird behaviour and bugs which are hard to reproduce.

    0 讨论(0)
  • 2021-01-07 04:08

    Using operator delete on pointer to void results in undefined behavior according to the specification.

    Chapter 5.3.5 of the draft of the C++ specification. Paragraph 3.

    In the first alternative (delete object), if the static type of the operand is different from its dynamic type, the static type shall be a base class of the operand’s dynamic type and the static type shall have a virtual destructor or the behavior is undefined. In the second alternative (delete array) if the dynamic type of the object to be deleted differs from its static type, the behavior is undefined.73)

    And corresponding footnote.

    This implies that an object cannot be deleted using a pointer of type void* because there are no objects of type void

    0 讨论(0)
  • 2021-01-07 04:09

    Thanks for extra code. Now we can see a problem -

    in getNextJob

    if (!this->jobs.empty())
    {
        job = &(this->jobs.front());
        this->jobs.pop();
    

    After the "pop", the memory pointed to by 'job' is undefined. Don't use a reference, copy the actual data!

    Try something like this (it's still generic, because JobData is generic):

    jobData ThreadPool::getNextJob()    // get the data of the next job
    {
      jobData job;
    
      WaitForSingleObject(this->mutex, INFINITE);
    
      if (!this->jobs.empty())
      {
        job = (this->jobs.front());
        this->jobs.pop();
      }
    
      // we're done with the exclusive part !
      ReleaseMutex(this->mutex);
    
      return job;
    

    }

    Also, while you're adding jobs to the queue you must ALSO lock the mutex, to prevent list corruption. AFAIK std::lists are NOT inherently thread-safe...?

    0 讨论(0)
  • 2021-01-07 04:12

    If you try to delete an object twice, the second time will fail, because the heap is already freed. This is the normal behavior.

    Now, since you are in a multithreading context... it might be that the deletions are done "almost" in parallel, which might avoid the error on the second deletion, because the first one is not yet finalized.

    0 讨论(0)
  • 2021-01-07 04:13

    It's hard to give a definitive answer with this amount of code. But generally speaking, multithreaded programming is all about synchronizing access to data that might be accessed from multiple threads. If there is no long or other synchronization primitive protecting access to the threadpool class itself, then you can potentially have multiple threads reaching your deletion loop at the same time, at which point you're pretty much guaranteed to be double-freeing memory.

    The reason you're getting no crash when you delete a job's params at the end of the job function might be because access to a single job's params is already implicitly serialized by your work queue. Or you might just be getting lucky. In either case, it's best to think about locks and synchronization primitive as not being something that protects code, but as being something that protects data (I've always thought the term "critical section" was a bit misleading here, as it tends to lead people to think of a 'section of lines of code' rather than in terms of data access).. In this case, since you want to access your jobs data from multiple thread, you need to be protecting it via a lock or some other synchronization primitive.

    0 讨论(0)
  • 2021-01-07 04:23

    Let's turn this on its head: Why are you using pointers at all?

    class Params
    {
    int value1, value2; // etc...
    }
    
    class ThreadJob
    {
      int jobID;  // or whatever...
      Params params;
    }
    
    class ThreadPool
    {
      std::list<ThreadJob> jobs;
    
      void addJob(int job, const Params & p)
      {
         ThreadJob j(job, p);
         jobs.push_back(j);
      }
    }
    

    No new, delete or pointers... Obviously some of the implementation details may be cocked, but you get the overall picture.

    0 讨论(0)
提交回复
热议问题