📄 sip_transaction.c
字号:
/* Create hash table. */
mod_tsx_layer.htable = pj_hash_create( pool, PJSIP_MAX_TSX_COUNT );
if (!mod_tsx_layer.htable) {
pjsip_endpt_release_pool(endpt, pool);
return PJ_ENOMEM;
}
/* Create mutex. */
status = pj_mutex_create_recursive(pool, "tsxlayer", &mod_tsx_layer.mutex);
if (status != PJ_SUCCESS) {
pjsip_endpt_release_pool(endpt, pool);
return status;
}
/*
* Register transaction layer module to endpoint.
*/
status = pjsip_endpt_register_module( endpt, &mod_tsx_layer.mod );
if (status != PJ_SUCCESS) {
pj_mutex_destroy(mod_tsx_layer.mutex);
pjsip_endpt_release_pool(endpt, pool);
return status;
}
/* Register mod_stateful_util module (sip_util_statefull.c) */
status = pjsip_endpt_register_module(endpt, &mod_stateful_util);
if (status != PJ_SUCCESS) {
return status;
}
return PJ_SUCCESS;
}
/*
* Get the instance of transaction layer module.
*/
PJ_DEF(pjsip_module*) pjsip_tsx_layer_instance(void)
{
return &mod_tsx_layer.mod;
}
/*
* Unregister and destroy transaction layer module.
*/
PJ_DEF(pj_status_t) pjsip_tsx_layer_destroy(void)
{
/* Are we registered? */
PJ_ASSERT_RETURN(mod_tsx_layer.endpt!=NULL, PJ_EINVALIDOP);
/* Unregister from endpoint.
* Clean-ups will be done in the unload() module callback.
*/
return pjsip_endpt_unregister_module( mod_tsx_layer.endpt,
&mod_tsx_layer.mod);
}
/*
* Register the transaction to the hash table.
*/
static pj_status_t mod_tsx_layer_register_tsx( pjsip_transaction *tsx)
{
pj_assert(tsx->transaction_key.slen != 0);
/* Lock hash table mutex. */
pj_mutex_lock(mod_tsx_layer.mutex);
/* Check if no transaction with the same key exists.
* Do not use PJ_ASSERT_RETURN since it evaluates the expression
* twice!
*/
pj_assert(pj_hash_get( mod_tsx_layer.htable,
&tsx->transaction_key.ptr,
tsx->transaction_key.slen,
NULL) == NULL);
TSX_TRACE_((THIS_FILE,
"Transaction %p registered with hkey=0x%p and key=%.*s",
tsx, tsx->hashed_key, tsx->transaction_key.slen,
tsx->transaction_key.ptr));
/* Register the transaction to the hash table. */
#ifdef PRECALC_HASH
pj_hash_set( tsx->pool, mod_tsx_layer.htable, tsx->transaction_key.ptr,
tsx->transaction_key.slen, tsx->hashed_key, tsx);
#else
pj_hash_set( tsx->pool, mod_tsx_layer.htable, tsx->transaction_key.ptr,
tsx->transaction_key.slen, 0, tsx);
#endif
/* Unlock mutex. */
pj_mutex_unlock(mod_tsx_layer.mutex);
return PJ_SUCCESS;
}
/*
* Unregister the transaction from the hash table.
*/
static void mod_tsx_layer_unregister_tsx( pjsip_transaction *tsx)
{
pj_assert(tsx->transaction_key.slen != 0);
//pj_assert(tsx->state != PJSIP_TSX_STATE_NULL);
/* Lock hash table mutex. */
pj_mutex_lock(mod_tsx_layer.mutex);
/* Register the transaction to the hash table. */
#ifdef PRECALC_HASH
pj_hash_set( NULL, mod_tsx_layer.htable, tsx->transaction_key.ptr,
tsx->transaction_key.slen, tsx->hashed_key, NULL);
#else
pj_hash_set( NULL, mod_tsx_layer.htable, tsx->transaction_key.ptr,
tsx->transaction_key.slen, 0, NULL);
#endif
TSX_TRACE_((THIS_FILE,
"Transaction %p unregistered, hkey=0x%p and key=%.*s",
tsx, tsx->hashed_key, tsx->transaction_key.slen,
tsx->transaction_key.ptr));
/* Unlock mutex. */
pj_mutex_unlock(mod_tsx_layer.mutex);
}
/*
* Find a transaction.
*/
PJ_DEF(pjsip_transaction*) pjsip_tsx_layer_find_tsx( const pj_str_t *key,
pj_bool_t lock )
{
pjsip_transaction *tsx;
pj_uint32_t hval = 0;
pj_mutex_lock(mod_tsx_layer.mutex);
tsx = pj_hash_get( mod_tsx_layer.htable, key->ptr, key->slen, &hval );
pj_mutex_unlock(mod_tsx_layer.mutex);
TSX_TRACE_((THIS_FILE,
"Finding tsx with hkey=0x%p and key=%.*s: found %p",
hval, key->slen, key->ptr, tsx));
/* Race condition!
* Transaction may gets deleted before we have chance to lock it.
*/
PJ_TODO(FIX_RACE_CONDITION_HERE);
if (tsx && lock)
pj_mutex_lock(tsx->mutex);
return tsx;
}
/* This module callback is called when module is being loaded by
* endpoint. It does nothing for this module.
*/
static pj_status_t mod_tsx_layer_load(pjsip_endpoint *endpt)
{
PJ_UNUSED_ARG(endpt);
return PJ_SUCCESS;
}
/* This module callback is called when module is being started by
* endpoint. It does nothing for this module.
*/
static pj_status_t mod_tsx_layer_start(void)
{
return PJ_SUCCESS;
}
/* This module callback is called when module is being stopped by
* endpoint.
*/
static pj_status_t mod_tsx_layer_stop(void)
{
pj_hash_iterator_t it_buf, *it;
PJ_LOG(4,(THIS_FILE, "Stopping transaction layer module"));
pj_mutex_lock(mod_tsx_layer.mutex);
/* Destroy all transactions. */
it = pj_hash_first(mod_tsx_layer.htable, &it_buf);
while (it) {
pjsip_transaction *tsx = pj_hash_this(mod_tsx_layer.htable, it);
pj_hash_iterator_t *next = pj_hash_next(mod_tsx_layer.htable, it);
if (tsx) {
mod_tsx_layer_unregister_tsx(tsx);
tsx_destroy(tsx);
}
it = next;
}
pj_mutex_unlock(mod_tsx_layer.mutex);
return PJ_SUCCESS;
}
/* This module callback is called when module is being unloaded by
* endpoint.
*/
static pj_status_t mod_tsx_layer_unload(void)
{
/* Only self destroy when there's no transaction in the table.
* Transaction may refuse to destroy when it has pending
* transmission. If we destroy the module now, application will
* crash when the pending transaction finally got error response
* from transport and when it tries to unregister itself.
*/
if (pj_hash_count(mod_tsx_layer.htable) != 0)
return PJ_EBUSY;
/* Destroy mutex. */
pj_mutex_destroy(mod_tsx_layer.mutex);
/* Release pool. */
pjsip_endpt_release_pool(mod_tsx_layer.endpt, mod_tsx_layer.pool);
/* Free TLS */
pj_thread_local_free(pjsip_tsx_lock_tls_id);
/* Mark as unregistered. */
mod_tsx_layer.endpt = NULL;
PJ_LOG(4,(THIS_FILE, "Transaction layer module destroyed"));
return PJ_SUCCESS;
}
/* This module callback is called when endpoint has received an
* incoming request message.
*/
static pj_bool_t mod_tsx_layer_on_rx_request(pjsip_rx_data *rdata)
{
pj_str_t key;
pj_uint32_t hval = 0;
pjsip_transaction *tsx;
pjsip_tsx_create_key(rdata->tp_info.pool, &key, PJSIP_ROLE_UAS,
&rdata->msg_info.cseq->method, rdata);
/* Find transaction. */
pj_mutex_lock( mod_tsx_layer.mutex );
tsx = pj_hash_get( mod_tsx_layer.htable, key.ptr, key.slen, &hval );
TSX_TRACE_((THIS_FILE,
"Finding tsx for request, hkey=0x%p and key=%.*s, found %p",
hval, key.slen, key.ptr, tsx));
if (tsx == NULL || tsx->state == PJSIP_TSX_STATE_TERMINATED) {
/* Transaction not found.
* Reject the request so that endpoint passes the request to
* upper layer modules.
*/
pj_mutex_unlock( mod_tsx_layer.mutex);
return PJ_FALSE;
}
/* Unlock hash table. */
pj_mutex_unlock( mod_tsx_layer.mutex );
/* Race condition!
* Transaction may gets deleted before we have chance to lock it
* in pjsip_tsx_recv_msg().
*/
PJ_TODO(FIX_RACE_CONDITION_HERE);
/* Pass the message to the transaction. */
pjsip_tsx_recv_msg(tsx, rdata );
return PJ_TRUE;
}
/* This module callback is called when endpoint has received an
* incoming response message.
*/
static pj_bool_t mod_tsx_layer_on_rx_response(pjsip_rx_data *rdata)
{
pj_str_t key;
pj_uint32_t hval = 0;
pjsip_transaction *tsx;
pjsip_tsx_create_key(rdata->tp_info.pool, &key, PJSIP_ROLE_UAC,
&rdata->msg_info.cseq->method, rdata);
/* Find transaction. */
pj_mutex_lock( mod_tsx_layer.mutex );
tsx = pj_hash_get( mod_tsx_layer.htable, key.ptr, key.slen, &hval );
TSX_TRACE_((THIS_FILE,
"Finding tsx for response, hkey=0x%p and key=%.*s, found %p",
hval, key.slen, key.ptr, tsx));
if (tsx == NULL || tsx->state == PJSIP_TSX_STATE_TERMINATED) {
/* Transaction not found.
* Reject the request so that endpoint passes the request to
* upper layer modules.
*/
pj_mutex_unlock( mod_tsx_layer.mutex);
return PJ_FALSE;
}
/* Unlock hash table. */
pj_mutex_unlock( mod_tsx_layer.mutex );
/* Race condition!
* Transaction may gets deleted before we have chance to lock it
* in pjsip_tsx_recv_msg().
*/
PJ_TODO(FIX_RACE_CONDITION_HERE);
/* Pass the message to the transaction. */
pjsip_tsx_recv_msg(tsx, rdata );
return PJ_TRUE;
}
/*
* Get transaction instance in the rdata.
*/
PJ_DEF(pjsip_transaction*) pjsip_rdata_get_tsx( pjsip_rx_data *rdata )
{
return rdata->endpt_info.mod_data[mod_tsx_layer.mod.id];
}
/*
* Dump transaction layer.
*/
PJ_DEF(void) pjsip_tsx_layer_dump(pj_bool_t detail)
{
#if PJ_LOG_MAX_LEVEL >= 3
pj_hash_iterator_t itbuf, *it;
/* Lock mutex. */
pj_mutex_lock(mod_tsx_layer.mutex);
PJ_LOG(3, (THIS_FILE, "Dumping transaction table:"));
PJ_LOG(3, (THIS_FILE, " Total %d transactions",
pj_hash_count(mod_tsx_layer.htable)));
if (detail) {
it = pj_hash_first(mod_tsx_layer.htable, &itbuf);
if (it == NULL) {
PJ_LOG(3, (THIS_FILE, " - none - "));
} else {
while (it != NULL) {
pjsip_transaction *tsx = pj_hash_this(mod_tsx_layer.htable,it);
PJ_LOG(3, (THIS_FILE, " %s %s|%d|%s",
tsx->obj_name,
(tsx->last_tx?
pjsip_tx_data_get_info(tsx->last_tx):
"none"),
tsx->status_code,
pjsip_tsx_state_str(tsx->state)));
it = pj_hash_next(mod_tsx_layer.htable, it);
}
}
}
/* Unlock mutex. */
pj_mutex_unlock(mod_tsx_layer.mutex);
#endif
}
/*****************************************************************************
**
** Transaction
**
*****************************************************************************
**/
/*
* Lock transaction and set the value of Thread Local Storage.
*/
static void lock_tsx(pjsip_transaction *tsx, struct tsx_lock_data *lck)
{
struct tsx_lock_data *prev_data;
pj_mutex_lock(tsx->mutex);
prev_data = (struct tsx_lock_data *)
pj_thread_local_get(pjsip_tsx_lock_tls_id);
lck->prev = prev_data;
lck->tsx = tsx;
lck->is_alive = 1;
pj_thread_local_set(pjsip_tsx_lock_tls_id, lck);
}
/*
* Unlock transaction.
* This will selectively unlock the mutex ONLY IF the transaction has not been
* destroyed. The function knows whether the transaction has been destroyed
* because when transaction is destroyed the is_alive flag for the transaction
* will be set to zero.
*/
static pj_status_t unlock_tsx( pjsip_transaction *tsx,
struct tsx_lock_data *lck)
{
pj_assert( (void*)pj_thread_local_get(pjsip_tsx_lock_tls_id) == lck);
pj_assert( lck->tsx == tsx );
pj_thread_local_set(pjsip_tsx_lock_tls_id, lck->prev);
if (lck->is_alive)
pj_mutex_unlock(tsx->mutex);
return lck->is_alive ? PJ_SUCCESS : PJSIP_ETSXDESTROYED;
}
/* Create and initialize basic transaction structure.
* This function is called by both UAC and UAS creation.
*/
static pj_status_t tsx_create( pjsip_module *tsx_user,
pjsip_transaction **p_tsx)
{
pj_pool_t *pool;
pjsip_transaction *tsx;
pj_status_t status;
pool = pjsip_endpt_create_pool( mod_tsx_layer.endpt, "tsx",
PJSIP_POOL_TSX_LEN, PJSIP_POOL_TSX_INC );
if (!pool)
return PJ_ENOMEM;
tsx = pj_pool_zalloc(pool, sizeof(pjsip_transaction));
tsx->pool = pool;
tsx->tsx_user = tsx_user;
tsx->endpt = mod_tsx_layer.endpt;
pj_ansi_snprintf(tsx->obj_name, sizeof(tsx->obj_name),
"tsx%p", tsx);
tsx->handle_200resp = 1;
tsx->retransmit_timer.id = TSX_TIMER_RETRANSMISSION;
tsx->retransmit_timer._timer_id = -1;
tsx->retransmit_timer.user_data = tsx;
tsx->retransmit_timer.cb = &tsx_timer_callback;
tsx->timeout_timer.id = TSX_TIMER_TIMEOUT;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -