Merged revisions 67270 via svnmerge from
authorKevin P. Fleming <kpfleming@digium.com>
Tue, 5 Jun 2007 14:45:48 +0000 (14:45 +0000)
committerKevin P. Fleming <kpfleming@digium.com>
Tue, 5 Jun 2007 14:45:48 +0000 (14:45 +0000)
https://origsvn.digium.com/svn/asterisk/branches/1.4

........
r67270 | kpfleming | 2007-06-05 09:35:52 -0500 (Tue, 05 Jun 2007) | 3 lines

ensure that a burst of full frames (AST_FRAME_DTMF being the prime example) will not be processed out of order... this is a brute force fix, but seems to be the safest fix for now (thanks to the Digium PQ department for finding this bug)

........

git-svn-id: https://origsvn.digium.com/svn/asterisk/trunk@67271 65c4cc65-6c06-0410-ace0-fbb531ad65f3

channels/chan_iax2.c

index e32983b..ed87de5 100644 (file)
@@ -726,6 +726,12 @@ struct iax2_thread {
        time_t checktime;
        ast_mutex_t lock;
        ast_cond_t cond;
+       unsigned short ffcallno;                /* if this thread is processing a full frame, the
+                                                  callno for that frame will be here, so we can
+                                                  avoid dispatching any more full frames for that
+                                                  callno to other threads */
+       struct sockaddr_in ffsin;               /* remember the peer IP/port number for a full frame
+                                                  in process */
 };
 
 /* Thread lists */
@@ -966,6 +972,11 @@ static struct iax2_thread *find_idle_thread(void)
                thread = NULL;
        }
 
+       /* this thread is not processing a full frame (since it is idle),
+          so ensure that the field for the full frame call number is empty */
+       thread->ffcallno = 0;
+       memset(&thread->ffsin, 0, sizeof(thread->ffsin));
+
        return thread;
 }
 
@@ -6471,37 +6482,68 @@ static int socket_read(int *id, int fd, short events, void *cbdata)
        struct iax2_thread *thread;
        socklen_t len;
        time_t t;
-       static time_t last_errtime=0;
+       static time_t last_errtime = 0;
+       struct ast_iax2_full_hdr *fh;
 
-       thread = find_idle_thread();
-       if (thread) {
-               len = sizeof(thread->iosin);
-               thread->iofd = fd;
-               thread->iores = recvfrom(fd, thread->buf, sizeof(thread->buf), 0,(struct sockaddr *) &thread->iosin, &len);
-               if (thread->iores < 0) {
-                       if (errno != ECONNREFUSED && errno != EAGAIN)
-                               ast_log(LOG_WARNING, "Error: %s\n", strerror(errno));
-                       handle_error();
-                       insert_idle_thread(thread);
-                       return 1;
-               }
-               if (test_losspct && ((100.0 * ast_random() / (RAND_MAX + 1.0)) < test_losspct)) { /* simulate random loss condition */
-                       insert_idle_thread(thread);
-                       return 1;
-               }
-               /* Mark as ready and send on its way */
-               thread->iostate = IAX_IOSTATE_READY;
-#ifdef DEBUG_SCHED_MULTITHREAD
-               ast_copy_string(thread->curfunc, "socket_process", sizeof(thread->curfunc));
-#endif
-               signal_condition(&thread->lock, &thread->cond);
-       } else {
+       if (!(thread = find_idle_thread())) {
                time(&t);
                if (t != last_errtime)
                        ast_log(LOG_NOTICE, "Out of idle IAX2 threads for I/O, pausing!\n");
                last_errtime = t;
                usleep(1);
+               return 1;
+       }
+
+       len = sizeof(thread->iosin);
+       thread->iofd = fd;
+       thread->iores = recvfrom(fd, thread->buf, sizeof(thread->buf), 0, (struct sockaddr *) &thread->iosin, &len);
+       if (thread->iores < 0) {
+               if (errno != ECONNREFUSED && errno != EAGAIN)
+                       ast_log(LOG_WARNING, "Error: %s\n", strerror(errno));
+               handle_error();
+               insert_idle_thread(thread);
+               return 1;
        }
+       if (test_losspct && ((100.0 * ast_random() / (RAND_MAX + 1.0)) < test_losspct)) { /* simulate random loss condition */
+               insert_idle_thread(thread);
+               return 1;
+       }
+       
+       /* Determine if this frame is a full frame; if so, and any thread is currently
+          processing a full frame for the same callno from this peer, then drop this
+          frame (and the peer will retransmit it) */
+       fh = (struct ast_iax2_full_hdr *) thread->buf;
+       if (ntohs(fh->scallno) & IAX_FLAG_FULL) {
+               struct iax2_thread *cur = NULL;
+               
+               AST_LIST_LOCK(&active_list);
+               AST_LIST_TRAVERSE(&active_list, cur, list) {
+                       if ((cur->ffcallno == ntohs(fh->scallno)) &&
+                           !memcmp(&cur->ffsin, &thread->iosin, sizeof(cur->ffsin)))
+                               break;
+               }
+               AST_LIST_UNLOCK(&active_list);
+               if (cur) {
+                       /* we found another thread processing a full frame for this call,
+                          so we can't accept this frame */
+                       ast_log(LOG_WARNING, "Dropping full frame from %s (callno %d) received too rapidly\n",
+                               ast_inet_ntoa(thread->iosin.sin_addr), cur->ffcallno);
+                       insert_idle_thread(thread);
+                       return 1;
+               } else {
+                       /* this thread is going to process this frame, so mark it */
+                       thread->ffcallno = ntohs(fh->scallno);
+                       memcpy(&thread->ffsin, &thread->iosin, sizeof(thread->ffsin));
+               }
+       }
+       
+       /* Mark as ready and send on its way */
+       thread->iostate = IAX_IOSTATE_READY;
+#ifdef DEBUG_SCHED_MULTITHREAD
+       ast_copy_string(thread->curfunc, "socket_process", sizeof(thread->curfunc));
+#endif
+       signal_condition(&thread->lock, &thread->cond);
+
        return 1;
 }