mirror of
https://github.com/asterisk/asterisk.git
synced 2025-09-29 18:19:30 +00:00
Merge "app_queue: queue members can receive multiple calls"
This commit is contained in:
145
apps/app_queue.c
145
apps/app_queue.c
@@ -1554,7 +1554,6 @@ struct member {
|
||||
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 */
|
||||
unsigned int call_pending:1; /*!< TRUE if the Q is attempting to place a call to the member. */
|
||||
char rt_uniqueid[80]; /*!< Unique id of realtime member entry */
|
||||
unsigned int ringinuse:1; /*!< Flag to ring queue members even if their status is 'inuse' */
|
||||
};
|
||||
@@ -2289,6 +2288,70 @@ static int get_member_status(struct call_queue *q, int max_penalty, int min_pena
|
||||
return -1;
|
||||
}
|
||||
|
||||
/*
|
||||
* A "pool" of member objects that calls are currently pending on. If an
|
||||
* agent is a member of multiple queues it's possible for that agent to be
|
||||
* called by each of the queues at the same time. This happens because device
|
||||
* state is slow to notify the queue app of one of it's member's being rung.
|
||||
* This "pool" allows us to track which members are currently being rung while
|
||||
* we wait on the device state change.
|
||||
*/
|
||||
static struct ao2_container *pending_members;
|
||||
#define MAX_CALL_ATTEMPT_BUCKETS 353
|
||||
|
||||
static int pending_members_hash(const void *obj, const int flags)
|
||||
{
|
||||
const struct member *object;
|
||||
const char *key;
|
||||
|
||||
switch (flags & OBJ_SEARCH_MASK) {
|
||||
case OBJ_SEARCH_KEY:
|
||||
key = obj;
|
||||
break;
|
||||
case OBJ_SEARCH_OBJECT:
|
||||
object = obj;
|
||||
key = object->interface;
|
||||
break;
|
||||
default:
|
||||
ast_assert(0);
|
||||
return 0;
|
||||
}
|
||||
return ast_str_case_hash(key);
|
||||
}
|
||||
|
||||
static int pending_members_cmp(void *obj, void *arg, int flags)
|
||||
{
|
||||
const struct member *object_left = obj;
|
||||
const struct member *object_right = arg;
|
||||
const char *right_key = arg;
|
||||
int cmp;
|
||||
|
||||
switch (flags & OBJ_SEARCH_MASK) {
|
||||
case OBJ_SEARCH_OBJECT:
|
||||
right_key = object_right->interface;
|
||||
/* Fall through */
|
||||
case OBJ_SEARCH_KEY:
|
||||
cmp = strcasecmp(object_left->interface, right_key);
|
||||
break;
|
||||
case OBJ_SEARCH_PARTIAL_KEY:
|
||||
/* Not supported by container. */
|
||||
ast_assert(0);
|
||||
return 0;
|
||||
default:
|
||||
cmp = 0;
|
||||
break;
|
||||
}
|
||||
if (cmp) {
|
||||
return 0;
|
||||
}
|
||||
return CMP_MATCH;
|
||||
}
|
||||
|
||||
static void pending_members_remove(struct member *mem)
|
||||
{
|
||||
ao2_find(pending_members, mem, OBJ_POINTER | OBJ_NODATA | OBJ_UNLINK);
|
||||
}
|
||||
|
||||
/*! \brief set a member's status based on device state of that member's state_interface.
|
||||
*
|
||||
* Lock interface list find sc, iterate through each queues queue_member list for member to
|
||||
@@ -2298,6 +2361,9 @@ static void update_status(struct call_queue *q, struct member *m, const int stat
|
||||
{
|
||||
m->status = status;
|
||||
|
||||
/* Whatever the status is clear the member from the pending members pool */
|
||||
pending_members_remove(m);
|
||||
|
||||
queue_publish_member_blob(queue_member_status_type(), queue_member_blob_create(q, m));
|
||||
}
|
||||
|
||||
@@ -3157,6 +3223,7 @@ static void member_add_to_queue(struct call_queue *queue, struct member *mem)
|
||||
*/
|
||||
static void member_remove_from_queue(struct call_queue *queue, struct member *mem)
|
||||
{
|
||||
pending_members_remove(mem);
|
||||
ao2_lock(queue->members);
|
||||
ast_devstate_changed(QUEUE_UNKNOWN_PAUSED_DEVSTATE, AST_DEVSTATE_CACHABLE, "Queue:%s_pause_%s", queue->name, mem->interface);
|
||||
queue_member_follower_removal(queue, mem);
|
||||
@@ -4133,41 +4200,6 @@ static int member_status_available(int status)
|
||||
return status == AST_DEVICE_NOT_INUSE || status == AST_DEVICE_UNKNOWN;
|
||||
}
|
||||
|
||||
/*!
|
||||
* \internal
|
||||
* \brief Clear the member call pending flag.
|
||||
*
|
||||
* \param mem Queue member.
|
||||
*
|
||||
* \return Nothing
|
||||
*/
|
||||
static void member_call_pending_clear(struct member *mem)
|
||||
{
|
||||
ao2_lock(mem);
|
||||
mem->call_pending = 0;
|
||||
ao2_unlock(mem);
|
||||
}
|
||||
|
||||
/*!
|
||||
* \internal
|
||||
* \brief Set the member call pending flag.
|
||||
*
|
||||
* \param mem Queue member.
|
||||
*
|
||||
* \retval non-zero if call pending flag was already set.
|
||||
*/
|
||||
static int member_call_pending_set(struct member *mem)
|
||||
{
|
||||
int old_pending;
|
||||
|
||||
ao2_lock(mem);
|
||||
old_pending = mem->call_pending;
|
||||
mem->call_pending = 1;
|
||||
ao2_unlock(mem);
|
||||
|
||||
return old_pending;
|
||||
}
|
||||
|
||||
/*!
|
||||
* \internal
|
||||
* \brief Determine if can ring a queue entry.
|
||||
@@ -4210,12 +4242,31 @@ static int can_ring_entry(struct queue_ent *qe, struct callattempt *call)
|
||||
}
|
||||
|
||||
if (!call->member->ringinuse) {
|
||||
if (member_call_pending_set(call->member)) {
|
||||
ast_debug(1, "%s has another call pending, can't receive call\n",
|
||||
call->interface);
|
||||
struct member *mem;
|
||||
|
||||
ao2_lock(pending_members);
|
||||
|
||||
mem = ao2_find(pending_members, call->member,
|
||||
OBJ_SEARCH_OBJECT | OBJ_NOLOCK);
|
||||
if (mem) {
|
||||
/*
|
||||
* If found that means this member is currently being attempted
|
||||
* from another calling thread, so stop trying from this thread
|
||||
*/
|
||||
ast_debug(1, "%s has another call trying, can't receive call\n",
|
||||
call->interface);
|
||||
ao2_ref(mem, -1);
|
||||
ao2_unlock(pending_members);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* If not found add it to the container so another queue
|
||||
* won't attempt to call this member at the same time.
|
||||
*/
|
||||
ao2_link(pending_members, call->member);
|
||||
ao2_unlock(pending_members);
|
||||
|
||||
/*
|
||||
* The queue member is available. Get current status to be sure
|
||||
* because the device state and extension state callbacks may
|
||||
@@ -4224,7 +4275,7 @@ static int can_ring_entry(struct queue_ent *qe, struct callattempt *call)
|
||||
if (!member_status_available(get_queue_member_status(call->member))) {
|
||||
ast_debug(1, "%s actually not available, can't receive call\n",
|
||||
call->interface);
|
||||
member_call_pending_clear(call->member);
|
||||
pending_members_remove(call->member);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
@@ -4261,7 +4312,6 @@ static int ring_entry(struct queue_ent *qe, struct callattempt *tmp, int *busies
|
||||
++*busies;
|
||||
return 0;
|
||||
}
|
||||
ast_assert(tmp->member->ringinuse || tmp->member->call_pending);
|
||||
|
||||
ast_copy_string(tech, tmp->interface, sizeof(tech));
|
||||
if ((location = strchr(tech, '/'))) {
|
||||
@@ -4278,7 +4328,7 @@ static int ring_entry(struct queue_ent *qe, struct callattempt *tmp, int *busies
|
||||
qe->linpos++;
|
||||
ao2_unlock(qe->parent);
|
||||
|
||||
member_call_pending_clear(tmp->member);
|
||||
pending_members_remove(tmp->member);
|
||||
|
||||
publish_dial_end_event(qe->chan, tmp, NULL, "BUSY");
|
||||
tmp->stillgoing = 0;
|
||||
@@ -4349,7 +4399,7 @@ static int ring_entry(struct queue_ent *qe, struct callattempt *tmp, int *busies
|
||||
/* Again, keep going even if there's an error */
|
||||
ast_verb(3, "Couldn't call %s\n", tmp->interface);
|
||||
do_hang(tmp);
|
||||
member_call_pending_clear(tmp->member);
|
||||
pending_members_remove(tmp->member);
|
||||
++*busies;
|
||||
return 0;
|
||||
}
|
||||
@@ -4369,7 +4419,6 @@ static int ring_entry(struct queue_ent *qe, struct callattempt *tmp, int *busies
|
||||
|
||||
ast_verb(3, "Called %s\n", tmp->interface);
|
||||
|
||||
member_call_pending_clear(tmp->member);
|
||||
return 1;
|
||||
}
|
||||
|
||||
@@ -10934,6 +10983,7 @@ static int unload_module(void)
|
||||
ast_extension_state_del(0, extension_state_cb);
|
||||
|
||||
ast_unload_realtime("queue_members");
|
||||
ao2_cleanup(pending_members);
|
||||
ao2_cleanup(queues);
|
||||
queues = NULL;
|
||||
return 0;
|
||||
@@ -10962,6 +11012,13 @@ static int load_module(void)
|
||||
return AST_MODULE_LOAD_DECLINE;
|
||||
}
|
||||
|
||||
pending_members = ao2_container_alloc(
|
||||
MAX_CALL_ATTEMPT_BUCKETS, pending_members_hash, pending_members_cmp);
|
||||
if (!pending_members) {
|
||||
unload_module();
|
||||
return AST_MODULE_LOAD_DECLINE;
|
||||
}
|
||||
|
||||
use_weight = 0;
|
||||
|
||||
if (reload_handler(0, &mask, NULL)) {
|
||||
|
Reference in New Issue
Block a user