@ -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 ) ) {