struct ast_taskprocessor *ast_sip_get_distributor_serializer(pjsip_rx_data *rdata) { int hash; pj_str_t *remote_tag; struct ast_taskprocessor *serializer; if (!rdata->msg_info.msg) { return NULL; } if (rdata->msg_info.msg->type == PJSIP_REQUEST_MSG) { remote_tag = &rdata->msg_info.from->tag; } else { remote_tag = &rdata->msg_info.to->tag; } /* Compute the hash from the SIP message call-id and remote-tag */ hash = pjstr_hash(&rdata->msg_info.cid->id); hash = pjstr_hash_add(remote_tag, hash); hash = ast_str_hash_restrict(hash); serializer = ao2_bump(distributor_pool[hash % ARRAY_LEN(distributor_pool)]); if (serializer) { ast_debug(3, "Calculated serializer %s to use for %s\n", ast_taskprocessor_name(serializer), pjsip_rx_data_get_info(rdata)); } return serializer; }
/*! * \brief Add threads to the threadpool * * This function is called from the threadpool's control taskprocessor thread. * \param pool The pool that is expanding * \delta The number of threads to add to the pool */ static void grow(struct ast_threadpool *pool, int delta) { int i; int current_size = ao2_container_count(pool->active_threads) + ao2_container_count(pool->idle_threads); if (pool->options.max_size && current_size + delta > pool->options.max_size) { delta = pool->options.max_size - current_size; } ast_debug(3, "Increasing threadpool %s's size by %d\n", ast_taskprocessor_name(pool->tps), delta); for (i = 0; i < delta; ++i) { struct worker_thread *worker = worker_thread_alloc(pool); if (!worker) { return; } if (ao2_link(pool->idle_threads, worker)) { if (worker_thread_start(worker)) { ast_log(LOG_ERROR, "Unable to start worker thread %d. Destroying.\n", worker->id); ao2_unlink(pool->active_threads, worker); } } else { ast_log(LOG_WARNING, "Failed to activate worker thread %d. Destroying.\n", worker->id); } ao2_ref(worker, -1); } }
/*! * \brief Remove threads from the threadpool * * The preference is to kill idle threads. However, if there are * more threads to remove than there are idle threads, then active * threads will be zombified instead. * * This function is called from the threadpool control taskprocessor thread. * * \param pool The threadpool to remove threads from * \param delta The number of threads to remove */ static void shrink(struct ast_threadpool *pool, int delta) { /* * Preference is to kill idle threads, but * we'll move on to deactivating active threads * if we have to */ int idle_threads = ao2_container_count(pool->idle_threads); int idle_threads_to_kill = MIN(delta, idle_threads); int active_threads_to_zombify = delta - idle_threads_to_kill; ast_debug(3, "Destroying %d idle threads in threadpool %s\n", idle_threads_to_kill, ast_taskprocessor_name(pool->tps)); ao2_callback(pool->idle_threads, OBJ_UNLINK | OBJ_NOLOCK | OBJ_NODATA | OBJ_MULTIPLE, kill_threads, &idle_threads_to_kill); ast_debug(3, "Destroying %d active threads in threadpool %s\n", active_threads_to_zombify, ast_taskprocessor_name(pool->tps)); ao2_callback_data(pool->active_threads, OBJ_UNLINK | OBJ_NOLOCK | OBJ_NODATA | OBJ_MULTIPLE, zombify_threads, pool, &active_threads_to_zombify); }
/*! * \internal * \brief Record the task's serializer name on the tdata structure. * \since 14.0.0 * * \param tdata The outgoing message. * * \retval PJ_SUCCESS. */ static pj_status_t record_serializer(pjsip_tx_data *tdata) { struct ast_taskprocessor *serializer; serializer = ast_threadpool_serializer_get_current(); if (serializer) { const char *name; name = ast_taskprocessor_name(serializer); if (!ast_strlen_zero(name) && (!tdata->mod_data[distributor_mod.id] || strcmp(tdata->mod_data[distributor_mod.id], name))) { char *tdata_name; /* The serializer in use changed. */ tdata_name = pj_pool_alloc(tdata->pool, strlen(name) + 1); strcpy(tdata_name, name);/* Safe */ tdata->mod_data[distributor_mod.id] = tdata_name; } } return PJ_SUCCESS; }
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; }