views:

131

answers:

5

I have an object (Client * client) which starts multiple threads to handle various tasks (such as processing incoming data). The threads are started like this:

// Start the thread that will process incoming messages and stuff them into the appropriate queues.
mReceiveMessageThread = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)receiveRtpMessageFunction, this, 0, 0);

These threads all have references back to the initial object, like so:

  // Thread initialization function for receiving RTP messages from a newly connected client.
  static int WINAPI receiveRtpMessageFunction(LPVOID lpClient)
  {
     LOG_METHOD("receiveRtpMessageFunction");
     Client * client = (Client *)lpClient;
     while(client ->isConnected())
     {
        if(client ->receiveMessage() == ERROR)
        {
           Log::log("receiveRtpMessageFunction Failed to receive message");
        }
     }

     return SUCCESS;
  }

Periodically, the Client object gets deleted (for various good and sufficient reasons). But when that happens, the processing threads that still have references to the (now deleted) object throw exceptions of one sort or another when trying to access member functions on that object.

So I'm sure that there's a standard way to handle this situation, but I haven't been able to figure out a clean approach. I don't want to just terminate the thread, as that doesn't allow for cleaning up resources. I can't set a property on the object, as it's precisely properties on the object that become inaccessible.

Thoughts on the best way to handle this?

A: 

You'll need to have some other state object the threads can check to verify that the "client" is still valid.

One option is to encapsulate your client reference inside some other object that remains persistent, and provide a reference to that object from your threads.

Reed Copsey
`boost::shared_ptr` instead of raw pointers would do, though you'd still want the event handling to stop processes as soon as possible since they are working for nothing :)
Matthieu M.
Yes, except that would keep the "client" alive, which may not be the desired effect...
Reed Copsey
A: 

You could use the observer pattern with proxy objects for the client in the threads. The proxies act like smart pointers, forwarding access to the real client. When you create them, they register themselves with the client, so that it can invalidate them from its destructor. Once they're invalidated, they stop forwarding and just return errors.

Nathan Kitchen
It's not only about forwarding (that would be easy), but also interrupting already running tasks.
Matthieu M.
+2  A: 

I would solve this problem by introducing a reference count to your object. The worker thread would hold a reference and so would the creator of the object. Instead of using delete, you decrement from the reference count and whoever drops the last reference is the one that actually calls delete.

You can use existing reference counting mechanisms (shared_ptr etc.), or you can roll your own with the Win32 APIs InterlockedIncrement() and InterlockedDecrement() or similar (maybe the reference count is a volatile DWORD starting out at 1...).

The only other thing that's missing is that when the main thread releases its reference, it should signal to the worker thread to drop its own reference. One way you can do this is by an event; you can rewrite the worker thread's loop as calls to WaitForMultipleObjects(), and when a certain event is signalled, you take that to mean that the worker thread should clean up and drop the reference.

asveikau
I agree this would work, I presented an alternative without reference counting... don't know if it would be faster though.
Matthieu M.
A: 

This could be handled by passing a (boost) weak pointer to the threads.

stefaanv
Short answer: no. Not to interrupt an already running thread. Long answer: look mine up.
Matthieu M.
It does help against dereferencing the deleted object, which was the key question. Of course, it is also a good idea to let the threads know that the object disappears, so they can cleanup themselves, but there is no extra synchronization needed next to that when using weak_ptrs.
stefaanv
+1  A: 

You don't have much leeway because of the running threads.

No combination of shared_ptr + weak_ptr may save you... you may call a method on the object when it's valid and then order its destruction (using only shared_ptr would).

The only thing I can imagine is to first terminate the various processes and then destroy the object. This way you ensure that each process terminate gracefully, cleaning up its own mess if necessary (and it might need the object to do that).

This means that you cannot delete the object out of hand, since you must first resynchronize with those who use it, and that you need some event handling for the synchronization part (since you basically want to tell the threads to stop, and not wait indefinitely for them).

I leave the synchronization part to you, there are many alternatives (events, flags, etc...) and we don't have enough data.

You can deal with the actual cleanup from either the destructor itself or by overloading the various delete operations, whichever suits you.

Matthieu M.
Thanks for the pointer. That's what I ended up doing. I added a public method Client::tellThreadsToFinishAndWait(). That sets the various flags which tell the threads to finish what they're doing and exit. It uses WaitForMultipleObjects to determine when the threads have all exited, and then it returns control to the routine responsible for garbage collecting the inactive client.
Ken Smith