Wes Miller
Wes Miller

Reputation: 2241

C++ delete in daughter thread

It is my understanding that the function called when starting a thread inside an object should not be a class member. The best approach seems to be to launch a friend function, which gets you access back into your object.

In general, the member function (and therefore, the parent thread) that launched the daughter thread can continue or it can return. In every case where I use this technique, I let the launcher method just return to the app in the parent thread that called it; something like Qt threads.

When the daughter thread has finished its work, the final thing it does is return into the friend function which itself returns to something waiting to catch its return (pthread_koin or WaitForSingleEvent) or, if there is no catcher, I guess you'd say it returns to nowhere.

So, here is the question. If there is no catcher for the return from the friend function, that is, the parent thread is not in a member function, can I safely destroy the object that launched the child thread from the friend function?

EDIT --------------------------------------------------------------------------

Obvious from the responses, I need an example. We'll go for Windows. Not that different from Linux. I have left out lots of stuff, the class definition, etc.

  1. Main creates so, a SomeObject on the heap.
  2. Main calls so->run() and goes off to do other stuff.
  3. Run() launches the daughter thread that runs SomeFriend().
  4. SomeFriend() calls so->Worker() (that == so)
  5. Worker() does whatever and returns to SomeFriend().
  6. CAN I DELETE so HERE? i.e. delete that <<<=== the subject of this question.
  7. SomeFriend() returns terminating the daughter thread.
//=================================================================
int main( int argc, char** argv )
{
   SomeObject* so = new SomeObject();
   so->run();
   while(1)
   {
      DoOtherTasks();  // but don't exit!
   }
   return 0;        

//=================================================================
void SomeObject::run();
(
  volatile DWORD      ThreadId;         // Thread ID
  HANDLE              threadHandle;

  try
  {
     threadHandle = CreateThread(
         NULL,                              // default security attributes
         0,                                 // set stack size: default = 0
         (LPTHREAD_START_ROUTINE)(SomeFriend),
         (LPVOID*)this,                     // func args: this
         0,                                 // default creation flags
         (LPDWORD)(&ThreadId)               // ptr to thread identifier
         );
  }
  catch ( ... )
     { throw; }
}   // launches the thread and returns.

//=================================================================
void*    SomeFriend( void* thisPtr )  // is a friend of SomeObject
{
   SomeObject*     that ((SomeObject*)thisPtr);

   that->Worker();

   // HERE IS WHERE THE QUESTION IS TALKING ABOUT
   // CAN I DO THIS SAFELY?
   delete that;

   return (void*)NULL;
}

//=================================================================
void SomeObject::Worker()  // remember, this is run in the daughter thread.
{
   // whatever
   return (void*)NULL;
}

Upvotes: 1

Views: 273

Answers (3)

inetknght
inetknght

Reputation: 4439

To answer your edited question, yes you can delete that; However, remember that main() or any functions it calls might not have a valid so at any point in its logic after so->run() was called because of the way the thread scheduler may have scheduled the threads.

Think of the thread as "owning" so after you've called so->run(). main() and its stack descendants should never touch so again without guarded logic.

Upvotes: 1

Pete Becker
Pete Becker

Reputation: 76305

There's no inherent reason for not launching a member function as the top-level function in a thread. C++11 handles it just fine:

struct S {
    void f();
};

S s;

int main() {
    std::thread thr(&S::f, s);
    thr.join();
    return 0;
}

Upvotes: 0

dascandy
dascandy

Reputation: 7292

Yes.

Your memory management code should be thread-safe already (or threading would be dangerous to start with!) so the free() itself should be fine. The destruction is fine as well, as long as you keep in mind that nobody else may have a reference to this object as they will be pointing to a destructed object.

The reason that people say that it should not be a class member is that member functions have a typically hidden pointer that's also treated differently on a byte level from other parameters, so you can't just call it as a normal function with an extra parameter. This makes it typically incompatible with the pthread_create and CreateThreadEx functions that have a specific calling convention they want. That's why you have a bouncer static / global / friend function that does this calling convention conversion for you (and probably so transparently that you don't notice it yourself).

Upvotes: 0

Related Questions