chan_lcr: fixed race condition in new ref assignment
authorPeter Schlaile <root@asterisk.schlaile.de>
Sat, 27 Sep 2008 20:03:44 +0000 (22:03 +0200)
committerroot <root@asterisk.schlaile.de>
Sat, 27 Sep 2008 20:03:44 +0000 (22:03 +0200)
LCR assumes, that any call in the list, that has ref set to 0 is waiting for a ref to be assigned.
That can cause trouble, if we have one call waiting for a ref to be assigned and another call
hanging in the list, that was just released in the same moment.

Both have ref == 0 and in some circumstances, the new ref message picks the wrong one for
assignment...

This patch makes chan_lcr distinguish between calls waiting for a new ref and those, that
only have their ref removed due to release. (It is not enough, to check for state, since
new calls can change into release state immediately! That is also one of the race conditions,
when this can get you into trouble: asterisk receives call1 by LCR, makes a SETUP call2 immediately
through LCR and then receives a release for call1 by LCR before call2 got it's ref assigned!)

This patch also removes some dead code, that was #ifdef'd out.

End user notice: if you ever got into the situation, that _all_ calls from asterisk to LCR got released
immediately and only a restart of asterisk got you out of the situation, then you might need
this fix :)

chan_lcr.c

index b28965a..b00bc7e 100644 (file)
@@ -205,46 +205,24 @@ void chan_lcr_log(int type, const char *file, int line, const char *function, st
  */
 struct chan_call *call_first;
 
-struct chan_call *find_call_ref(unsigned int ref)
-{
-       struct chan_call *call = call_first;
-
-       while(call)
-       {
-               if (call->ref == ref)
-                       break;
-               call = call->next;
-       }
-       return(call);
-}
-
-#if 0
-struct chan_call *find_call_ast(struct ast_channel *ast)
-{
-       struct chan_call *call = call_first;
-
-       while(call)
-       {
-               if (call->ast == ast)
-                       break;
-               call = call->next;
-       }
-       return(call);
-}
+/*
+ * find call by ref
+ * special case: 0: find new ref, that has not been assigned a ref yet
+ */
 
-struct chan_call *find_call_handle(unsigned int handle)
+struct chan_call *find_call_ref(unsigned int ref)
 {
        struct chan_call *call = call_first;
-
+       int assigned = (ref > 0);
+       
        while(call)
        {
-               if (call->bchannel_handle == handle)
+               if (call->ref == ref && call->ref_was_assigned == assigned)
                        break;
                call = call->next;
        }
        return(call);
 }
-#endif
 
 void free_call(struct chan_call *call)
 {
@@ -300,7 +278,6 @@ struct chan_call *alloc_call(void)
        return(*callp);
 }
 
-
 unsigned short new_bridge_id(void)
 {
        struct chan_call *call;
@@ -948,7 +925,7 @@ static void lcr_in_disconnect(struct chan_call *call, int message_type, union pa
 }
 
 /*
- * incoming setup acknowledge from LCR
+ * incoming release from LCR
  */
 static void lcr_in_release(struct chan_call *call, int message_type, union parameter *param)
 {
@@ -1199,6 +1176,7 @@ int receive_message(int message_type, unsigned int ref, union parameter *param)
                        call->state = CHAN_LCR_STATE_IN_PREPARE;
                        /* set ref */
                        call->ref = ref;
+                       call->ref_was_assigned = 1;
                        /* wait for setup (or release from asterisk) */
                } else
                {
@@ -1214,6 +1192,7 @@ int receive_message(int message_type, unsigned int ref, union parameter *param)
                        }
                        /* store new ref */
                        call->ref = ref;
+                       call->ref_was_assigned = 1;
                        /* send pending setup info */
                        if (call->state == CHAN_LCR_STATE_OUT_PREPARE)
                                send_setup_to_lcr(call);