app_queue: Add member flag "in_call" to prevent reading wrong lastcall time
authorMartin Tomec <tomec.martin@gmail.com>
Mon, 21 Dec 2015 17:07:14 +0000 (18:07 +0100)
committerMartin Tomec <tomec.martin@gmail.com>
Tue, 5 Jan 2016 16:44:11 +0000 (17:44 +0100)
Member lastcall time is updated later than member status. There was chance to
check wrapuptime for available member with wrong (old) lastcall time.
New boolean flag "in_call" is set to true right before connecting call, and
reset to false after update of lastcall time. Members with "in_call" set to true
are treat as unavailable.

ASTERISK-19820 #close

Change-Id: I1923230cf9859ee51563a8ed420a0628b4d2e500

apps/app_queue.c

index 416ebff..c4025d2 100644 (file)
@@ -1023,6 +1023,13 @@ ASTERISK_REGISTER_FILE()
                                <parameter name="LastCall">
                                        <para>The time this member last took a call, expressed in seconds since 00:00, Jan 1, 1970 UTC.</para>
                                </parameter>
+                               <parameter name="InCall">
+                                       <para>Set to 1 if member is in call. Set to 0 after LastCall time is updated.</para>
+                                       <enumlist>
+                                               <enum name="0"/>
+                                               <enum name="1"/>
+                                       </enumlist>
+                               </parameter>
                                <parameter name="Status">
                                        <para>The numeric device state status of the queue member.</para>
                                        <enumlist>
@@ -1540,6 +1547,7 @@ struct member {
        char reason_paused[80];              /*!< Reason of paused if member is paused */
        int queuepos;                        /*!< In what order (pertains to certain strategies) should this member be called? */
        time_t lastcall;                     /*!< When last successful call was hungup */
+       unsigned int in_call:1;              /*!< True if member is still in call. (so lastcall is not actual) */
        struct call_queue *lastqueue;        /*!< Last queue we received a call */
        unsigned int dead:1;                 /*!< Used to detect members deleted in realtime */
        unsigned int delme:1;                /*!< Flag to delete entry on reload */
@@ -2177,7 +2185,7 @@ static void queue_publish_member_blob(struct stasis_message_type *type, struct a
 
 static struct ast_json *queue_member_blob_create(struct call_queue *q, struct member *mem)
 {
-       return ast_json_pack("{s: s, s: s, s: s, s: s, s: s, s: i, s: i, s: i, s: i, s: i, s: s, s: i}",
+       return ast_json_pack("{s: s, s: s, s: s, s: s, s: s, s: i, s: i, s: i, s: i, s: i, s: i, s: s, s: i}",
                "Queue", q->name,
                "MemberName", mem->membername,
                "Interface", mem->interface,
@@ -2186,6 +2194,7 @@ static struct ast_json *queue_member_blob_create(struct call_queue *q, struct me
                "Penalty", mem->penalty,
                "CallsTaken", mem->calls,
                "LastCall", (int)mem->lastcall,
+               "InCall", mem->in_call,
                "Status", mem->status,
                "Paused", mem->paused,
                "PausedReason", mem->reason_paused,
@@ -2249,6 +2258,10 @@ static int get_member_status(struct call_queue *q, int max_penalty, int min_pena
                        if (member->paused && (conditions & QUEUE_EMPTY_PAUSED)) {
                                ast_debug(4, "%s is unavailable because he is paused'\n", member->membername);
                                break;
+                       } else if ((conditions & QUEUE_EMPTY_WRAPUP) && member->in_call && q->wrapuptime) {
+                               ast_debug(4, "%s is unavailable because still in call, so we can`t check "
+                                       "wrapuptime (%d)\n", member->membername, q->wrapuptime);
+                               break;
                        } else if ((conditions & QUEUE_EMPTY_WRAPUP) && member->lastcall && q->wrapuptime && (time(NULL) - q->wrapuptime < member->lastcall)) {
                                ast_debug(4, "%s is unavailable because it has only been %d seconds since his last call (wrapup time is %d)\n", member->membername, (int) (time(NULL) - member->lastcall), q->wrapuptime);
                                break;
@@ -2316,6 +2329,9 @@ static int is_member_available(struct call_queue *q, struct member *mem)
        }
 
        /* Let wrapuptimes override device state availability */
+       if (q->wrapuptime && mem->in_call) {
+               available = 0; /* member is still in call, cant check wrapuptime to lastcall time */
+       }
        if (mem->lastcall && q->wrapuptime && (time(NULL) - q->wrapuptime < mem->lastcall)) {
                available = 0;
        }
@@ -2668,6 +2684,7 @@ static void clear_queue(struct call_queue *q)
                while ((mem = ao2_iterator_next(&mem_iter))) {
                        mem->calls = 0;
                        mem->lastcall = 0;
+                       mem->in_call = 0;
                        ao2_ref(mem, -1);
                }
                ao2_iterator_destroy(&mem_iter);
@@ -4165,6 +4182,12 @@ static int can_ring_entry(struct queue_ent *qe, struct callattempt *call)
                return 0;
        }
 
+       if (call->member->in_call && call->lastqueue->wrapuptime) {
+               ast_debug(1, "%s is in call, so not available (wrapuptime %d)\n",
+                       call->interface, call->lastqueue->wrapuptime);
+               return 0;
+       }
+
        if ((call->lastqueue && call->lastqueue->wrapuptime && (time(NULL) - call->lastcall < call->lastqueue->wrapuptime))
                || (!call->lastqueue && qe->parent->wrapuptime && (time(NULL) - call->lastcall < qe->parent->wrapuptime))) {
                ast_debug(1, "Wrapuptime not yet expired on queue %s for %s\n",
@@ -5387,6 +5410,9 @@ static int update_queue(struct call_queue *q, struct member *member, int callcom
                                time(&mem->lastcall);
                                mem->calls++;
                                mem->lastqueue = q;
+                               mem->in_call = 0;
+                               ast_debug(4, "Marked member %s as NOT in_call. Lastcall time: %ld \n",
+                                       mem->membername, (long)mem->lastcall);
                                ao2_ref(mem, -1);
                        }
                        ao2_unlock(qtmp);
@@ -5398,6 +5424,9 @@ static int update_queue(struct call_queue *q, struct member *member, int callcom
                time(&member->lastcall);
                member->calls++;
                member->lastqueue = q;
+               member->in_call = 0;
+               ast_debug(4, "Marked member %s as NOT in_call. Lastcall time: %ld \n",
+                       member->membername, (long)member->lastcall);
                ao2_unlock(q);
        }
        ao2_lock(q);
@@ -6376,6 +6405,9 @@ static int try_calling(struct queue_ent *qe, struct ast_flags opts, char **opt_a
        int callcompletedinsl;
        struct ao2_iterator memi;
        struct queue_end_bridge *queue_end_bridge = NULL;
+       struct ao2_iterator queue_iter; /* to iterate through all queues (for shared_lastcall)*/
+       struct member *mem;
+       struct call_queue *queuetmp;
 
        memset(&bridge_config, 0, sizeof(bridge_config));
        tmpid[0] = 0;
@@ -6801,6 +6833,28 @@ static int try_calling(struct queue_ent *qe, struct ast_flags opts, char **opt_a
                        }
                }
                qe->handled++;
+
+               /** mark member as "in_call" in all queues */
+               if (shared_lastcall) {
+                       queue_iter = ao2_iterator_init(queues, 0);
+                       while ((queuetmp = ao2_t_iterator_next(&queue_iter, "Iterate through queues"))) {
+                               ao2_lock(queuetmp);
+                               if ((mem = ao2_find(queuetmp->members, member, OBJ_POINTER))) {
+                                       mem->in_call = 1;
+                                       ast_debug(4, "Marked member %s as in_call \n", mem->membername);
+                                       ao2_ref(mem, -1);
+                               }
+                               ao2_unlock(queuetmp);
+                               queue_t_unref(queuetmp, "Done with iterator");
+                       }
+                       ao2_iterator_destroy(&queue_iter);
+               } else {
+                       ao2_lock(qe->parent);
+                       member->in_call = 1;
+                       ast_debug(4, "Marked member %s as in_call \n", member->membername);
+                       ao2_unlock(qe->parent);
+               }
+
                ast_queue_log(queuename, ast_channel_uniqueid(qe->chan), member->membername, "CONNECT", "%ld|%s|%ld", (long) (time(NULL) - qe->start), ast_channel_uniqueid(peer),
                                                                                                        (long)(orig - to > 0 ? (orig - to) / 1000 : 0));
 
@@ -9255,9 +9309,10 @@ static char *__queues_show(struct mansession *s, int fd, int argc, const char *
 
                                ast_str_append(&out, 0, " (ringinuse %s)", mem->ringinuse ? "enabled" : "disabled");
 
-                               ast_str_append(&out, 0, "%s%s%s%s%s%s",
+                               ast_str_append(&out, 0, "%s%s%s%s%s%s%s%s%s",
                                        mem->dynamic ? ast_term_color(COLOR_CYAN, COLOR_BLACK) : "", mem->dynamic ? " (dynamic)" : "", ast_term_reset(),
-                                       mem->realtime ? ast_term_color(COLOR_MAGENTA, COLOR_BLACK) : "", mem->realtime ? " (realtime)" : "", ast_term_reset());
+                                       mem->realtime ? ast_term_color(COLOR_MAGENTA, COLOR_BLACK) : "", mem->realtime ? " (realtime)" : "", ast_term_reset(),
+                                       mem->in_call ? ast_term_color(COLOR_BROWN, COLOR_BLACK) : "", mem->in_call ? " (in call)" : "", ast_term_reset());
                                if (mem->paused) {
                                        if (ast_strlen_zero(mem->reason_paused)) {
                                                ast_str_append(&out, 0, " %s(paused)%s",
@@ -9636,13 +9691,15 @@ static int manager_queues_status(struct mansession *s, const struct message *m)
                                                "Penalty: %d\r\n"
                                                "CallsTaken: %d\r\n"
                                                "LastCall: %d\r\n"
+                                               "InCall: %d\r\n"
                                                "Status: %d\r\n"
                                                "Paused: %d\r\n"
                                                "PausedReason: %s\r\n"
                                                "%s"
                                                "\r\n",
                                                q->name, mem->membername, mem->interface, mem->state_interface, mem->dynamic ? "dynamic" : "static",
-                                               mem->penalty, mem->calls, (int)mem->lastcall, mem->status, mem->paused, mem->reason_paused, idText);
+                                               mem->penalty, mem->calls, (int)mem->lastcall, mem->in_call, mem->status,
+                                               mem->paused, mem->reason_paused, idText);
                                        ++q_items;
                                }
                                ao2_ref(mem, -1);