⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 sip_transaction.c

📁 一个开源SIP协议栈
💻 C
📖 第 1 页 / 共 5 页
字号:
    /* 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 + -