static void rx_task_data_destroy(void *obj) { struct rx_task_data *task_data = obj; pjsip_rx_data_free_cloned(task_data->rdata); ao2_cleanup(task_data->endpoint); ao2_cleanup(task_data->aor); }
/// Worker threads handle most SIP message processing. static int worker_thread(void* p) { // Set up data to always process incoming messages at the first PJSIP // module after our module. pjsip_process_rdata_param rp; pjsip_process_rdata_param_default(&rp); rp.start_mod = &mod_stack; rp.idx_after_start = 1; LOG_DEBUG("Worker thread started"); struct rx_msg_qe qe = {0}; while (rx_msg_q.pop(qe)) { pjsip_rx_data* rdata = qe.rdata; if (rdata) { LOG_DEBUG("Worker thread dequeue message %p", rdata); pjsip_endpt_process_rx_data(stack_data.endpt, rdata, &rp, NULL); LOG_DEBUG("Worker thread completed processing message %p", rdata); pjsip_rx_data_free_cloned(rdata); struct timespec done_time; if (clock_gettime(CLOCK_MONOTONIC, &done_time) == 0) { long latency_us = (done_time.tv_nsec - qe.rx_time.tv_nsec) / 1000L + (done_time.tv_sec - qe.rx_time.tv_sec) * 1000000L; LOG_DEBUG("Request latency = %ldus", latency_us); latency_accumulator->accumulate(latency_us); latency_accumulator->refresh(); } else { LOG_ERROR("Failed to get done timestamp: %s", strerror(errno)); } } } LOG_DEBUG("Worker thread ended"); return 0; }
/// Worker threads handle most SIP message processing. static int worker_thread(void* p) { // Set up data to always process incoming messages at the first PJSIP // module after our module. pjsip_process_rdata_param rp; pjsip_process_rdata_param_default(&rp); rp.start_mod = &mod_stack; rp.idx_after_start = 1; LOG_DEBUG("Worker thread started"); struct rx_msg_qe qe = {0}; while (rx_msg_q.pop(qe)) { pjsip_rx_data* rdata = qe.rdata; if (rdata) { LOG_DEBUG("Worker thread dequeue message %p", rdata); pjsip_endpt_process_rx_data(stack_data.endpt, rdata, &rp, NULL); LOG_DEBUG("Worker thread completed processing message %p", rdata); pjsip_rx_data_free_cloned(rdata); unsigned long latency_us; if (qe.stop_watch.read(latency_us)) { LOG_DEBUG("Request latency = %ldus", latency_us); latency_accumulator->accumulate(latency_us); load_monitor->request_complete(latency_us); } else { LOG_ERROR("Failed to get done timestamp: %s", strerror(errno)); } } } LOG_DEBUG("Worker thread ended"); return 0; }
static int distribute(void *data) { static pjsip_process_rdata_param param = { .start_mod = &distributor_mod, .idx_after_start = 1, }; pj_bool_t handled = PJ_FALSE; pjsip_rx_data *rdata = data; int is_request = rdata->msg_info.msg->type == PJSIP_REQUEST_MSG; int is_ack = is_request ? rdata->msg_info.msg->line.req.method.id == PJSIP_ACK_METHOD : 0; struct ast_sip_endpoint *endpoint; pjsip_endpt_process_rx_data(ast_sip_get_pjsip_endpoint(), rdata, ¶m, &handled); if (!handled && is_request && !is_ack) { pjsip_endpt_respond_stateless(ast_sip_get_pjsip_endpoint(), rdata, 501, NULL, NULL, NULL); } /* The endpoint_mod stores an endpoint reference in the mod_data of rdata. This * is the only appropriate spot to actually decrement the reference. */ endpoint = rdata->endpt_info.mod_data[endpoint_mod.id]; ao2_cleanup(endpoint); pjsip_rx_data_free_cloned(rdata); return 0; } struct ast_sip_endpoint *ast_pjsip_rdata_get_endpoint(pjsip_rx_data *rdata) { struct ast_sip_endpoint *endpoint = rdata->endpt_info.mod_data[endpoint_mod.id]; if (endpoint) { ao2_ref(endpoint, +1); } return endpoint; } static int suspects_sort(const void *obj, const void *arg, int flags) { const struct unidentified_request *object_left = obj; const struct unidentified_request *object_right = arg; const char *right_key = arg; int cmp; switch (flags & OBJ_SEARCH_MASK) { case OBJ_SEARCH_OBJECT: right_key = object_right->src_name; /* Fall through */ case OBJ_SEARCH_KEY: cmp = strcmp(object_left->src_name, right_key); break; case OBJ_SEARCH_PARTIAL_KEY: cmp = strncmp(object_left->src_name, right_key, strlen(right_key)); break; default: cmp = 0; break; } return cmp; } static int suspects_compare(void *obj, void *arg, int flags) { const struct unidentified_request *object_left = obj; const struct unidentified_request *object_right = arg; const char *right_key = arg; int cmp = 0; switch (flags & OBJ_SEARCH_MASK) { case OBJ_SEARCH_OBJECT: right_key = object_right->src_name; /* Fall through */ case OBJ_SEARCH_KEY: if (strcmp(object_left->src_name, right_key) == 0) { cmp = CMP_MATCH; } break; case OBJ_SEARCH_PARTIAL_KEY: if (strncmp(object_left->src_name, right_key, strlen(right_key)) == 0) { cmp = CMP_MATCH; } break; default: cmp = 0; break; } return cmp; } static int suspects_hash(const void *obj, int flags) { const struct unidentified_request *object; const char *key; switch (flags & OBJ_SEARCH_MASK) { case OBJ_SEARCH_KEY: key = obj; break; case OBJ_SEARCH_OBJECT: object = obj; key = object->src_name; break; default: /* Hash can only work on something with a full key. */ ast_assert(0); return 0; } return ast_str_hash(key); } static struct ao2_container *cli_unid_get_container(const char *regex) { struct ao2_container *s_container; s_container = ao2_container_alloc_list(AO2_ALLOC_OPT_LOCK_NOLOCK, 0, suspects_sort, suspects_compare); if (!s_container) { return NULL; } if (ao2_container_dup(s_container, unidentified_requests, 0)) { ao2_ref(s_container, -1); return NULL; } return s_container; } static int cli_unid_iterate(void *container, ao2_callback_fn callback, void *args) { ao2_callback(container, 0, callback, args); return 0; }
static pj_bool_t distributor(pjsip_rx_data *rdata) { pjsip_dialog *dlg; struct distributor_dialog_data *dist = NULL; struct ast_taskprocessor *serializer = NULL; pjsip_rx_data *clone; if (!ast_test_flag(&ast_options, AST_OPT_FLAG_FULLY_BOOTED)) { /* * Ignore everything until we are fully booted. Let the * peer retransmit messages until we are ready. */ return PJ_TRUE; } dlg = find_dialog(rdata); if (dlg) { ast_debug(3, "Searching for serializer associated with dialog %s for %s\n", dlg->obj_name, pjsip_rx_data_get_info(rdata)); dist = ao2_find(dialog_associations, dlg, OBJ_SEARCH_KEY); if (dist) { ao2_lock(dist); serializer = ao2_bump(dist->serializer); ao2_unlock(dist); if (serializer) { ast_debug(3, "Found serializer %s associated with dialog %s\n", ast_taskprocessor_name(serializer), dlg->obj_name); } } } if (serializer) { /* We have a serializer so we know where to send the message. */ } else if (rdata->msg_info.msg->type == PJSIP_RESPONSE_MSG) { ast_debug(3, "No dialog serializer for %s. Using request transaction as basis.\n", pjsip_rx_data_get_info(rdata)); serializer = find_request_serializer(rdata); if (!serializer) { /* * Pick a serializer for the unmatched response. * We couldn't determine what serializer originally * sent the request or the serializer is gone. */ serializer = ast_sip_get_distributor_serializer(rdata); } } else if (!pjsip_method_cmp(&rdata->msg_info.msg->line.req.method, &pjsip_cancel_method) || !pjsip_method_cmp(&rdata->msg_info.msg->line.req.method, &pjsip_bye_method)) { /* We have a BYE or CANCEL request without a serializer. */ pjsip_endpt_respond_stateless(ast_sip_get_pjsip_endpoint(), rdata, PJSIP_SC_CALL_TSX_DOES_NOT_EXIST, NULL, NULL, NULL); ao2_cleanup(dist); return PJ_TRUE; } else { if (ast_taskprocessor_alert_get()) { /* * When taskprocessors get backed up, there is a good chance that * we are being overloaded and need to defer adding new work to * the system. To defer the work we will ignore the request and * rely on the peer's transport layer to retransmit the message. * We usually work off the overload within a few seconds. The * alternative is to send back a 503 response to these requests * and be done with it. */ ast_debug(3, "Taskprocessor overload alert: Ignoring '%s'.\n", pjsip_rx_data_get_info(rdata)); ao2_cleanup(dist); return PJ_TRUE; } /* Pick a serializer for the out-of-dialog request. */ serializer = ast_sip_get_distributor_serializer(rdata); } if (pjsip_rx_data_clone(rdata, 0, &clone) != PJ_SUCCESS) { ast_taskprocessor_unreference(serializer); ao2_cleanup(dist); return PJ_TRUE; } if (dist) { ao2_lock(dist); clone->endpt_info.mod_data[endpoint_mod.id] = ao2_bump(dist->endpoint); ao2_unlock(dist); ao2_cleanup(dist); } if (ast_sip_push_task(serializer, distribute, clone)) { ao2_cleanup(clone->endpt_info.mod_data[endpoint_mod.id]); pjsip_rx_data_free_cloned(clone); } ast_taskprocessor_unreference(serializer); return PJ_TRUE; }