PJ_DEF(pj_status_t) pjsip_transport_add_state_listener (
					    pjsip_transport *tp,
					    pjsip_tp_state_callback cb,
					    void *user_data,
					    pjsip_tp_state_listener_key **key)
{
    transport_data *tp_data;
    tp_state_listener *entry;

    PJ_ASSERT_RETURN(tp && cb && key, PJ_EINVAL);

    if (tp->is_shutdown || tp->is_destroying) {
	*key = NULL;
	return PJ_EINVALIDOP;
    }

    pj_lock_acquire(tp->lock);

    /* Init transport data, if it hasn't */
    if (!tp->data)
	init_tp_data(tp);

    tp_data = (transport_data*)tp->data;

    /* Init the new listener entry. Use available empty slot, if any,
     * otherwise allocate it using the transport pool.
     */
    if (!pj_list_empty(&tp_data->st_listeners_empty)) {
	entry = tp_data->st_listeners_empty.next;
	pj_list_erase(entry);
    } else {
	entry = PJ_POOL_ZALLOC_T(tp->pool, tp_state_listener);
    }
    entry->cb = cb;
    entry->user_data = user_data;

    /* Add the new listener entry to the listeners list */
    pj_list_push_back(&tp_data->st_listeners, entry);

    *key = entry;

    pj_lock_release(tp->lock);

    return PJ_SUCCESS;
}
PJ_DEF(pj_status_t) pjsip_transport_add_state_listener (
					    pjsip_transport *tp,
					    pjsip_tp_state_callback cb,
					    void *user_data,
					    pjsip_tp_state_listener_key **key)
{
    transport_data *tp_data;
    tp_state_listener *entry;

    PJ_ASSERT_RETURN(tp && cb && key, PJ_EINVAL);

    if (tp->is_shutdown || tp->is_destroying) {
	*key = NULL;
	return PJ_EINVALIDOP;
    }

    pj_lock_acquire(tp->lock);

     
    if (!tp->data)
	init_tp_data(tp);

    tp_data = (transport_data*)tp->data;

     
    if (!pj_list_empty(&tp_data->st_listeners_empty)) {
	entry = tp_data->st_listeners_empty.next;
	pj_list_erase(entry);
    } else {
	entry = PJ_POOL_ZALLOC_T(tp->pool, tp_state_listener);
    }
    entry->cb = cb;
    entry->user_data = user_data;

     
    pj_list_push_back(&tp_data->st_listeners, entry);

    *key = entry;

    pj_lock_release(tp->lock);

    return PJ_SUCCESS;
}