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' */
};
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 = obj;
+ const char *key = (flags & OBJ_KEY) ? obj : object->interface;
+ 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 = (flags & OBJ_KEY) ? arg : object_right->interface;
+
+ return strcasecmp(object_left->interface, right_key) ? 0 : CMP_MATCH;
+}
+
+static void pending_members_remove(struct member *mem)
+{
+ ao2_find(pending_members, mem, OBJ_POINTER | OBJ_NODATA | OBJ_UNLINK);
+}
+
struct statechange {
AST_LIST_ENTRY(statechange) entry;
int state;
{
m->status = status;
+ /* Whatever the status is clear the member from the pending members pool */
+ pending_members_remove(m);
+
if (q->maskmemberstatus) {
return 0;
}
*/
static void member_remove_from_queue(struct call_queue *queue, struct member *mem)
{
+ pending_members_remove(mem);
ao2_lock(queue->members);
queue_member_follower_removal(queue, mem);
ao2_unlink(queue->members, mem);
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.
}
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_POINTER | 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
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;
}
}
++*busies;
return 0;
}
- ast_assert(tmp->member->ringinuse || tmp->member->call_pending);
ast_copy_string(tech, tmp->interface, sizeof(tech));
if ((location = strchr(tech, '/'))) {
qe->linpos++;
ao2_unlock(qe->parent);
- member_call_pending_clear(tmp->member);
+ pending_members_remove(tmp->member);
if (ast_channel_cdr(qe->chan)) {
ast_cdr_busy(ast_channel_cdr(qe->chan));
/* 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;
}
ast_verb(3, "Called %s\n", tmp->interface);
}
- member_call_pending_clear(tmp->member);
return 1;
}
}
ao2_iterator_destroy(&q_iter);
devicestate_tps = ast_taskprocessor_unreference(devicestate_tps);
+ ao2_cleanup(pending_members);
ao2_ref(queues, -1);
ast_unload_realtime("queue_members");
return res;
struct ast_config *member_config;
queues = ao2_container_alloc(MAX_QUEUE_BUCKETS, queue_hash_cb, queue_cmp_cb);
+ if (!queues) {
+ 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;