< Previous by Date Date Index Next by Date >
  Thread Index Next in Thread >

[reSIProcate] AppDialogSet Memory Leak


        Hello everyone.

We have encountered a problem that is resulting in a memory leak due to AppDialogSets that are no longer being used but never getting released/destroyed. I have taken a look at the DUM source and have identified the reason it is happening. It looks like the code for handling our particular scenario has not been completed. So I am wondering if what we are doing is correct and the code has just not been written yet or if there is a better way of handling this situation.

Basically the scenario is that we create a SUBSCRIBE request via Dum.makeSubscription() passing in our own AppDialogSet. We then send the request. Now the device we are subscribing to never responds (for whatever reason) and the request eventually times out with an internally generated 408 SipResp. In this simple situation DUM eventually calls destroy() on the AppDialogSet and things get cleaned up nicely.

The problem occurs if we try to kill the request *before* the timeout occurs. We are currently calling end() on the AppDialogSet to do this. This results in the state of the underlying DialogSet being set to WaitingToEnd. Now when the 408 timeout response is then generated, it eventually gets passed through to DialogSet::dispatch() for processing. As can be seen from the code shown below, using a Release build, the resulting action is to do nothing. With a debug build it will assert(). In our scenario mState is WaitingToEnd and the last method was SUBSCRIBE.

So are we doing the right thing? Is it just a matter that no one has written the handling code for SUBSCRIBE yet? The code for handling the same situation for INVITE is present and I would expect that the equivalent code for handling the SUBSCRIBE situation would be similar. Otherwise, is there a better way of killing the pending SUBSCRIBE?

        Thanks.


void
DialogSet::dispatch(const SipMessage& msg)
{
   assert(msg.isRequest() || msg.isResponse());

   if (mState == WaitingToEnd)
   {
      assert(mDialogs.empty());
      if (msg.isResponse())
      {
         int code = msg.header(h_StatusLine).statusCode();
         switch(mCreator->getLastRequest()->header(h_CSeq).method())
         {
            case INVITE:
               if (code / 100 == 1)
               {
                  mState = ReceivedProvisional;
                  end();
               }
               else if (code / 100 == 2)
               {
                  Dialog dialog(mDum, msg, *this);

                  SharedPtr<SipMessage> ack(new SipMessage);
                  dialog.makeRequest(*ack, ACK);
ack->header(h_CSeq).sequence() = msg.header(h_CSeq).sequence();
                  dialog.send(ack);

                  SharedPtr<SipMessage> bye(new SipMessage);
                  dialog.makeRequest(*bye, BYE);
                  dialog.send(bye);

// Note: Destruction of this dialog object will cause DialogSet::possiblyDie to be called thus invoking mDum.destroy
               }
               else
               {
                  mState = Destroying;
                  mDum.destroy(this);
               }
               break;
            case SUBSCRIBE:
               assert(0);
               break;
            default:
               break;
         }
      }