static void nua_stack_timer(nua_t *nua, su_timer_t *t, su_timer_arg_t *a);
+static int nua_client_request_complete(nua_client_request_t *cr);
+
/* ---------------------------------------------------------------------- */
/* Constant data */
nea_server_destroy(nh->nh_notifier), nh->nh_notifier = NULL;
while (nh->nh_ds->ds_cr)
- nua_client_request_destroy(nh->nh_ds->ds_cr);
+ nua_client_request_complete(nh->nh_ds->ds_cr);
while (nh->nh_ds->ds_sr)
nua_server_request_destroy(nh->nh_ds->ds_sr);
* crm_report().
*
* The final responses are processed by crm_recv() and and preliminary ones
- * by crm_preliminary(). Both functions call nua_base_client_response() after
- * method-specific processing.
+ * by crm_preliminary(). All virtual functions should call
+ * nua_base_client_response() beside method-specific processing.
*
* The nua_base_client_response() relays the response to the application with
* nua_client_restart() and crm_report().
* When a terminating request completes the dialog usage is removed and the
* dialog is destroyed (unless there is an another active usage).
*/
+static void nua_client_request_destroy(nua_client_request_t *cr);
+static int nua_client_init_request0(nua_client_request_t *cr);
static int nua_client_request_try(nua_client_request_t *cr);
static int nua_client_request_sendmsg(nua_client_request_t *cr,
msg_t *msg, sip_t *sip);
if (tags && cr->cr_tags == NULL)
cr->cr_tags = tl_tlist(nh->nh_home, TAG_NEXT(tags));
+#if HAVE_MEMLEAK_LOG
+ SU_DEBUG_0(("%p %s() for %s\n", cr, __func__, cr->cr_methods->crm_method_name));
+#endif
+
if (nua_client_request_queue(cr))
return 0;
return retval;
}
+#if HAVE_MEMLEAK_LOG
+nua_client_request_t *
+nua_client_request_ref_by(nua_client_request_t *cr,
+ char const *where, unsigned line, char const *who)
+{
+ SU_DEBUG_0(("%p ref %s to %u by %s:%u: %s()\n",
+ cr, cr->cr_methods->crm_method_name,
+ ++(cr->cr_refs), where, line, who));
+ return cr;
+}
+
+int nua_client_request_unref_by(nua_client_request_t *cr,
+ char const *where, unsigned line, char const *who)
+{
+ SU_DEBUG_0(("%p unref %s to %u by %s:%u: %s()\n",
+ cr, cr->cr_methods->crm_method_name,
+ cr->cr_refs - 1, where, line, who));
+
+ if (cr->cr_refs > 1) {
+ cr->cr_refs--;
+ return 0;
+ }
+ else {
+ cr->cr_refs = 0;
+ nua_client_request_destroy(cr);
+ return 1;
+ }
+}
+#else
+nua_client_request_t *nua_client_request_ref(nua_client_request_t *cr)
+{
+ cr->cr_refs++;
+ return cr;
+}
+
+int nua_client_request_unref(nua_client_request_t *cr)
+{
+ if (cr->cr_refs > 1) {
+ cr->cr_refs--;
+ return 0;
+ }
+ else {
+ cr->cr_refs = 0;
+ nua_client_request_destroy(cr);
+ return 1;
+ }
+}
+#endif
+
int nua_client_request_queue(nua_client_request_t *cr)
{
int queued = 0;
cr->cr_status = 0;
+ nua_client_request_ref(cr);
+
if (cr->cr_method != sip_method_invite &&
cr->cr_method != sip_method_cancel) {
while (*queue) {
return queued;
}
-void nua_client_request_remove(nua_client_request_t *cr)
+int
+nua_client_request_remove(nua_client_request_t *cr)
{
if (cr->cr_prev)
if ((*cr->cr_prev = cr->cr_next))
cr->cr_next->cr_prev = cr->cr_prev;
cr->cr_prev = NULL, cr->cr_next = NULL;
+
+ if (cr->cr_timer) {
+ su_timer_destroy(cr->cr_timer), cr->cr_timer = NULL;
+ nua_client_request_unref(cr);
+ }
+
+ return nua_client_request_unref(cr);
}
-void nua_client_request_clean(nua_client_request_t *cr)
+int
+nua_client_request_clean(nua_client_request_t *cr)
{
- nta_outgoing_destroy(cr->cr_orq), cr->cr_orq = NULL, cr->cr_acked = 0;
+ if (cr->cr_orq) {
+ nta_outgoing_destroy(cr->cr_orq), cr->cr_orq = NULL, cr->cr_acked = 0;
+ return nua_client_request_unref(cr);
+ }
+ return 0;
}
-void nua_client_request_complete(nua_client_request_t *cr)
+static int
+nua_client_request_complete(nua_client_request_t *cr)
{
- nua_client_request_remove(cr);
- if (cr && cr->cr_methods->crm_complete)
- cr->cr_methods->crm_complete(cr);
+ if (cr->cr_orq) {
+ if (cr && cr->cr_methods->crm_complete)
+ cr->cr_methods->crm_complete(cr);
+ nta_outgoing_destroy(cr->cr_orq), cr->cr_orq = NULL, cr->cr_acked = 0;
+ nua_client_request_unref(cr);
+ }
+
+ return nua_client_request_remove(cr);
}
-void nua_client_request_destroy(nua_client_request_t *cr)
+static void
+nua_client_request_destroy(nua_client_request_t *cr)
{
nua_handle_t *nh;
if (cr == NULL)
return;
- nua_client_request_complete(cr);
+ /* Possible references: */
+ assert(cr->cr_prev == NULL); /* queue */
+ assert(cr->cr_orq == NULL); /* transaction callback */
+ assert(cr->cr_timer == NULL); /* timer callback */
nh = cr->cr_owner;
nua_destroy_signal(cr->cr_signal);
nua_client_bind(cr, NULL);
-
+
+#if HAVE_MEMLEAK_LOG
+ SU_DEBUG_0(("%p %s for %s\n", cr, __func__, cr->cr_methods->crm_method_name));
+#endif
+
if (cr->cr_msg)
msg_destroy(cr->cr_msg);
cr->cr_msg = NULL, cr->cr_sip = NULL;
nta_outgoing_destroy(cr->cr_orq);
cr->cr_orq = NULL;
- if (cr->cr_timer)
- su_timer_destroy(cr->cr_timer), cr->cr_timer = NULL;
-
if (cr->cr_target)
su_free(nh->nh_home, cr->cr_target);
return -1;
if (du == NULL) {
- if (cr->cr_usage && cr->cr_usage->du_cr == cr)
- cr->cr_usage->du_cr = NULL;
+ du = cr->cr_usage;
cr->cr_usage = NULL;
+ if (du && du->du_cr == cr) {
+ du->du_cr = NULL;
+ nua_client_request_unref(cr);
+ }
return 0;
}
- if (du->du_cr && cr != du->du_cr) {
- /* This should never happen (but it does):
- assert(!nua_client_is_queued(du->du_cr));
- */
- if (nua_client_is_queued(du->du_cr))
- return -1;
- if (nua_client_is_reporting(du->du_cr)) {
- du->du_cr->cr_usage = NULL;
- du->du_cr = NULL;
- }
- else
- nua_client_request_destroy(du->du_cr);
+ if (du->du_cr && cr == du->du_cr)
+ return 0;
+
+ if (du->du_cr) {
+ nua_client_bind(du->du_cr, NULL);
}
- du->du_cr = cr, cr->cr_usage = du;
+ du->du_cr = nua_client_request_ref(cr), cr->cr_usage = du;
return 0;
}
/**Initialize client request for sending.
*
- * This function is called only first time the request is sent.
+ * This function is called when the request is taken from queue and sent.
*
* @retval 0 if request is pending
* @retval >=1 if error event has been sent
*/
int nua_client_init_request(nua_client_request_t *cr)
+{
+ int retval;
+ nua_client_request_ref(cr);
+ retval = nua_client_init_request0(cr);
+ nua_client_request_unref(cr);
+ return retval;
+}
+
+/**Initialize client request for sending.
+ *
+ * This function is called when the request is taken from queue and sent.
+ *
+ * @retval 0 if request is pending
+ * @retval >=1 if error event has been sent
+ */
+static
+int nua_client_init_request0(nua_client_request_t *cr)
{
nua_handle_t *nh = cr->cr_owner;
nua_t *nua = nh->nh_nua;
cr->cr_sip = sip;
return nua_client_request_try(cr);
-
}
msg_t *nua_client_request_template(nua_client_request_t *cr)
tagi_t const *t = nh->nh_tags;
/* Use the From header from the dialog.
- From is always first tag in the handle */
+ If From is set, it is always first tag in the handle */
if (ds->ds_leg && t->t_tag == siptag_from)
t++;
cr->cr_seq = sip->sip_cseq->cs_seq; /* Save last sequence number */
+ assert(cr->cr_orq == NULL);
+
cr->cr_orq = nta_outgoing_mcreate(nh->nh_nua->nua_nta,
- nua_client_orq_response, cr,
- NULL,
+ nua_client_orq_response,
+ nua_client_request_ref(cr),
+ NULL,
msg,
TAG_IF(proxy_is_set,
NTATAG_DEFAULT_PROXY(proxy)),
TAG_NEXT(tags));
- return cr->cr_orq ? 0 : -1;
+ if (cr->cr_orq == NULL) {
+ nua_client_request_unref(cr);
+ return -1;
+ }
+
+ return 0;
}
/** Callback for nta client transaction */
{
nua_handle_t *nh = cr->cr_owner;
nua_dialog_usage_t *du = cr->cr_usage;
+ int retval = 0;
if (cr->cr_restarting)
return 0;
+ nua_client_request_ref(cr);
+
cr->cr_status = status;
cr->cr_phrase = phrase;
/* Xyzzy */
}
else if (sip && nua_client_check_restart(cr, status, phrase, sip)) {
+ nua_client_request_unref(cr);
return 0;
}
else if (status < 300) {
else if (graceful)
cr->cr_graceful = 1;
}
-
+
if (status < 200) {
if (cr->cr_methods->crm_preliminary)
cr->cr_methods->crm_preliminary(cr, status, phrase, sip);
else
nua_base_client_response(cr, status, phrase, sip, NULL);
cr->cr_phrase = NULL;
- return 0;
}
+ else {
+ if (cr->cr_methods->crm_recv)
+ retval = cr->cr_methods->crm_recv(cr, status, phrase, sip);
+ else
+ retval = nua_base_client_response(cr, status, phrase, sip, NULL);
+ }
- if (cr->cr_methods->crm_recv)
- return cr->cr_methods->crm_recv(cr, status, phrase, sip);
- else
- return nua_base_client_response(cr, status, phrase, sip, NULL);
+ nua_client_request_unref(cr);
+
+ return retval;
}
/** Check if request should be restarted.
nua_client_report(cr, status, phrase, NULL, orq, NULL);
nta_outgoing_destroy(orq);
cr->cr_status = 0, cr->cr_phrase = NULL;
+ nua_client_request_unref(cr);
return 1;
}
if (500 <= status && status < 600 &&
sip->sip_retry_after &&
sip->sip_retry_after->af_delta < 32) {
+ su_timer_t *timer;
char phrase[18]; /* Retry After XXXX\0 */
- if (cr->cr_timer == NULL)
- cr->cr_timer = su_timer_create(su_root_task(nh->nh_nua->nua_root), 0);
+ timer = su_timer_create(su_root_task(nh->nh_nua->nua_root), 0);
- if (su_timer_set_interval(cr->cr_timer, nua_client_restart_after, cr,
- sip->sip_retry_after->af_delta * 1000) < 0)
+ if (su_timer_set_interval(timer, nua_client_restart_after, cr,
+ sip->sip_retry_after->af_delta * 1000) < 0) {
+ su_timer_destroy(timer);
return 0; /* Too bad */
+ }
+
+ cr->cr_timer = timer; /* This takes over reference from orq */
snprintf(phrase, sizeof phrase, "Retry After %u",
(unsigned)sip->sip_retry_after->af_delta);
orq = cr->cr_orq, cr->cr_orq = NULL;
- cr->cr_waiting = cr->cr_wait_for_timer = 1;
+ cr->cr_waiting = 1;
nua_client_report(cr, 100, phrase, NULL, orq, NULL);
nta_outgoing_destroy(orq);
cr->cr_status = 0, cr->cr_phrase = NULL;
su_timer_t *timer,
nua_client_request_t *cr)
{
- if (!cr->cr_wait_for_timer)
- return;
-
- cr->cr_waiting = cr->cr_wait_for_timer = 0;
+ cr->cr_waiting = 0;
+ su_timer_destroy(cr->cr_timer), cr->cr_timer = NULL;
nua_client_restart_request(cr, cr->cr_terminating, NULL);
+ nua_client_request_unref(cr);
}
/** Restart request.
nua_client_report(cr, status, phrase, NULL, orq, NULL);
nta_outgoing_destroy(orq);
+ nua_client_request_unref(cr); /* ... reference used by old orq */
return 1;
}
nua_handle_t *nh = cr->cr_owner;
sip_method_t method = cr->cr_method;
nua_dialog_usage_t *du;
- nua_client_request_t *cr_next;
cr->cr_reporting = 1, nh->nh_ds->ds_reporting = 1;
return 1;
}
- if (cr->cr_orq)
- nta_outgoing_destroy(cr->cr_orq), cr->cr_orq = NULL, cr->cr_acked = 0;
+ nua_client_request_clean(cr);
du = cr->cr_usage;
cr->cr_phrase = NULL;
cr->cr_reporting = 0, nh->nh_ds->ds_reporting = 0;
- cr_next = nh->nh_ds->ds_cr;
-
- if (!nua_client_is_queued(cr) && !nua_client_is_bound(cr))
- nua_client_request_destroy(cr);
-
if (method == sip_method_cancel)
return 1;
- return nua_client_next_request(cr_next, method == sip_method_invite);
+ return nua_client_next_request(nh->nh_ds->ds_cr, method == sip_method_invite);
}
/** Send event, zap transaction but leave cr in list */
break;
}
- if (cr && cr->cr_orq == NULL)
+ if (cr && cr->cr_orq == NULL) {
nua_client_init_request(cr);
+ }
return 1;
}