Author Topic: Memory leak - parameter copy not disposed of?  (Read 66880 times)

JohnWS

  • Newbie
  • *
  • Posts: 6
    • View Profile
Memory leak - parameter copy not disposed of?
« on: August 27, 2009, 10:54:03 AM »
Hi!

I'm firing-off threads using a class with an operator() method containing the thread code and passing it an object as a parameter.
Looking at the output of the instrumentation (logging) that I have included in the object's constructor, copy constructor, assignment operator and destructor methods, it looks like the thread library is creating two temporaries before creating the actual instance that is passed to the new thread but only destroying one (the first) of those temporaries.

Has anyone come across this problem before?
I'm wondering if I'm doing something wrong or if there's a subtle bug in the library code.

JohnWS

  • Newbie
  • *
  • Posts: 6
    • View Profile
Re: Memory leak - parameter copy not disposed of?
« Reply #1 on: August 27, 2009, 12:12:52 PM »
Further to my earlier post - I get the same symptoms if I just construct a thread using a simple function and object as a parameter.
Here is a minimal program that demonstrates the point:

// ThreadTest.cpp : Defines the entry point for the console application.
//

#include "stdafx.h"

#include <iostream>
#include <sstream>

#include <thread>
#include <mutex>

void log(std::string & message) {
   static std::mutex logMutex;

   // Lock the log mutex to prevent overlapped log I/O.
   std::lock_guard<std::mutex> log_lock(logMutex);

   std::cout << message;
}

class MyObject {
   public:
      MyObject(void) : data(0) {
         std::ostringstream message;
         message << __FUNCTION__ << " called. this=" << this << ".\n";
         log(message.str());
      }
      MyObject(const MyObject & source) {
         std::ostringstream message;
         message << __FUNCTION__ << " called. this=" << this << ", source=" << &source << ".\n";
         log(message.str());
         this->data = source.data;
      }
      ~MyObject(void) {
         std::ostringstream message;
         message << __FUNCTION__ << " called. this=" << this << ".\n";
         log(message.str());
      }
      MyObject & operator=(const MyObject & source) {
         std::ostringstream message;
         message << __FUNCTION__ << " called. this=" << this << ", source=" << &source << ".\n";
         log(message.str());
         this->data = source.data;
      }

      int data;
};

void threadTest(MyObject myObject) {
   std::cout << __FUNCTION__ << ": myObject is at " << &myObject << ".\n";
}

int _tmain(int argc, _TCHAR* argv[])
{
   std::ostringstream message;
   MyObject myObject;
   myObject.data = 1;

   message << __FUNCTION__ << ": myObject is at " << &myObject << ".\n";
   log(message.str());

   std::thread myThread(threadTest, myObject);

   return 0;
}


Built as a Win32 console application using Microsoft Visual C++ 2008 (Express Edition) on my machine, it produces the following output:

MyObject::MyObject called. this=0012FECC.
wmain: myObject is at 0012FECC.
MyObject::MyObject called. this=0012FDA0, source=0012FECC.
MyObject::MyObject called. this=0052FFF4, source=0012FDA0.
MyObject::~MyObject called. this=0012FDA0.
MyObject::MyObject called. this=0052FC80, source=0052FFF4.
threadTest: myObject is at 0052FC80.
MyObject::~MyObject called. this=0052FC80.

which appears to show the constructor being called twice for temporaries but the destructor only being called for one of them.

Am I missing something here?

JohnWS

  • Newbie
  • *
  • Posts: 6
    • View Profile
Re: Memory leak - parameter copy not disposed of?
« Reply #2 on: August 27, 2009, 12:22:28 PM »
To answer my own question - I was missing the join the synchronized the main thread with the child one.
Once I had added that, the second temporary got deleted (but only at the point of the join).

So, here's the amended program:

// ThreadTest.cpp : Defines the entry point for the console application.
//

#include "stdafx.h"

#include <iostream>
#include <sstream>

#include <thread>
#include <mutex>

void log(std::string & message) {
   static std::mutex logMutex;

   // Lock the log mutex to prevent overlapped log I/O.
   std::lock_guard<std::mutex> log_lock(logMutex);

   std::cout << message;
}

class MyObject {
   public:
      MyObject(void) : data(0) {
         std::ostringstream message;
         message << __FUNCTION__ << " called. this=" << this << ".\n";
         log(message.str());
      }
      MyObject(const MyObject & source) {
         std::ostringstream message;
         message << __FUNCTION__ << " called. this=" << this << ", source=" << &source << ".\n";
         log(message.str());
         this->data = source.data;
      }
      ~MyObject(void) {
         std::ostringstream message;
         message << __FUNCTION__ << " called. this=" << this << ".\n";
         log(message.str());
      }
      MyObject & operator=(const MyObject & source) {
         std::ostringstream message;
         message << __FUNCTION__ << " called. this=" << this << ", source=" << &source << ".\n";
         log(message.str());
         this->data = source.data;
      }

      int data;
};

void threadTest(MyObject myObject) {
   std::cout << __FUNCTION__ << ": myObject is at " << &myObject << ".\n";
}

int _tmain(int argc, _TCHAR* argv[])
{
   std::ostringstream message;
   MyObject myObject;
   myObject.data = 1;

   message << __FUNCTION__ << ": myObject is at " << &myObject << ".\n";
   log(message.str());

   std::thread myThread(threadTest, myObject);

   myThread.join();

   return 0;
}

and the new results:

MyObject::MyObject called. this=0012FECC.
wmain: myObject is at 0012FECC.
MyObject::MyObject called. this=0012FDA0, source=0012FECC.
MyObject::MyObject called. this=0052FFF4, source=0012FDA0.
MyObject::~MyObject called. this=0012FDA0.
MyObject::MyObject called. this=0052FC80, source=0052FFF4.
threadTest: myObject is at 0052FC80.
MyObject::~MyObject called. this=0052FC80.
MyObject::~MyObject called. this=0052FFF4.
MyObject::~MyObject called. this=0012FECC.

Sometimes, all it takes is to simplify the problem and to try to explain it...  :)

Anthony Williams

  • Administrator
  • Full Member
  • *****
  • Posts: 103
    • View Profile
    • just::thread C++ Thread Library
Re: Memory leak - parameter copy not disposed of?
« Reply #3 on: August 30, 2009, 09:03:36 PM »
I'm glad you figured out the problem. The internal copy of the thread parameters should be cleaned up when the thread exits. Of course, if you don't join with the thread then there is no guarantee as to when that happens.