| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667 |
- /*
- * $Id$
- *
- *
- * Copyright (C) 2001-2003 FhG Fokus
- *
- * This file is part of ser, a free SIP server.
- *
- * ser is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version
- *
- * For a license to use the ser software under conditions
- * other than those described here, or to purchase support for this
- * software, please contact iptel.org by e-mail at the following addresses:
- * [email protected]
- *
- * ser is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- */
- /*
- timer.c is where we implement TM timers. It has been designed
- for high performance using some techniques of which timer users
- need to be aware.
- One technique is "fixed-timer-length". We maintain separate
- timer lists, all of them include elements of the same time
- to fire. That allows *appending* new events to the list as
- opposed to inserting them by time, which is costly due to
- searching time spent in a mutex. The performance benefit is
- noticeable. The limitation is you need a new timer list for
- each new timer length.
- Another technique is the timer process slices off expired elements
- from the list in a mutex, but executes the timer after the mutex
- is left. That saves time greatly as whichever process wants to
- add/remove a timer, it does not have to wait until the current
- list is processed. However, be aware the timers may hit in a delayed
- manner; you have no guarantee in your process that after resetting a timer,
- it will no more hit. It might have been removed by timer process,
- and is waiting to be executed. The following example shows it:
- PROCESS1 TIMER PROCESS
- 0. timer hits, it is removed from queue and
- about to be executed
- 1. process1 decides to
- reset the timer
- 2. timer is executed now
- 3. if the process1 naively
- thinks the timer could not
- have been executed after
- resetting the timer, it is
- WRONG -- it was (step 2.)
- So be careful when writing the timer handlers. Currently defined timers
- don't hurt if they hit delayed, I hope at least. Retransmission timer
- may results in a useless retransmission -- not too bad. FR timer not too
- bad either as timer processing uses a REPLY mutex making it safe to other
- processing affecting transaction state. Wait timer not bad either -- processes
- putting a transaction on wait don't do anything with it anymore.
- Example when it does not hurt:
- P1 TIMER
- 0. RETR timer removed from list and
- scheduled for execution
- 1. 200/BYE received->
- reset RETR, put_on_wait
- 2. RETR timer executed -- too late but it does
- not hurt
- 3. WAIT handler executed
- The rule of thumb is don't touch data you put under a timer. Create data,
- put them under a timer, and let them live until they are safely destroyed from
- wait/delete timer. The only safe place to manipulate the data is
- from timer process in which delayed timers cannot hit (all timers are
- processed sequentially).
- A "bad example" -- rewriting content of retransmission buffer
- in an unprotected way is bad because a delayed retransmission timer might
- hit. Thats why our reply retransmission procedure is enclosed in
- a REPLY_LOCK.
- */
- /*
- * History:
- * --------
- * 2003-06-27 timers are not unlinked if timerlist is 0 (andrei)
- * 2004-02-13 t->is_invite, t->local, t->noisy_ctimer replaced;
- * timer_link.payload removed (bogdan)
- * 2005-10-03 almost completely rewritten to use the new timers (andrei)
- * 2005-12-12 on final response marked the rb as removed to avoid deleting
- * it from the timer handle; timer_allow_del() (andrei)
- * 2006-08-11 final_response_handler dns failover support for timeout-ed
- * invites (andrei)
- * 2006-09-28 removed the 480 on fr_inv_timeout reply: on timeout always
- * return a 408
- * set the corresponding "faked" failure route sip_msg->msg_flags
- * on timeout or if the branch received a reply (andrei)
- * 2007-03-15 TMCB_ONSEND callbacks support (andrei)
- * 2007-05-29 delete on transaction ref_count==0 : removed the delete timer
- * (andrei)
- * 2007-06-01 support for different retransmissions intervals per transaction;
- * added maximum inv. and non-inv. transaction life time (andrei)
- */
- #include "defs.h"
- #include "config.h"
- #include "h_table.h"
- #include "timer.h"
- #include "../../dprint.h"
- #include "lock.h"
- #include "t_stats.h"
- #include "../../hash_func.h"
- #include "../../dprint.h"
- #include "../../config.h"
- #include "../../parser/parser_f.h"
- #include "../../ut.h"
- #include "../../timer_ticks.h"
- #include "../../compiler_opt.h"
- #include "t_funcs.h"
- #include "t_reply.h"
- #include "t_cancel.h"
- #include "t_hooks.h"
- #ifdef USE_DNS_FAILOVER
- #include "t_fwd.h" /* t_send_branch */
- #include "../../cfg_core.h" /* cfg_get(core, core_cfg, use_dns_failover) */
- #endif
- #ifdef USE_DST_BLACKLIST
- #include "../../dst_blacklist.h"
- #endif
- struct msgid_var user_fr_timeout;
- struct msgid_var user_fr_inv_timeout;
- #ifdef TM_DIFF_RT_TIMEOUT
- struct msgid_var user_rt_t1_timeout;
- struct msgid_var user_rt_t2_timeout;
- #endif
- struct msgid_var user_inv_max_lifetime;
- struct msgid_var user_noninv_max_lifetime;
- /* internal use, val should be unsigned or positive
- * <= instead of < to get read of gcc warning when
- * sizeof(cell_member)==sizeof(val) (Note that this limits
- * maximum value to max. type -1) */
- #define SIZE_FIT_CHECK(cell_member, val, cfg_name) \
- if (MAX_UVAR_VALUE(((struct cell*)0)->cell_member) <= (val)){ \
- ERR("tm_init_timers: " cfg_name " too big: %lu (%lu ticks) " \
- "- max %lu (%lu ticks) \n", TICKS_TO_MS((unsigned long)(val)),\
- (unsigned long)(val), \
- TICKS_TO_MS(MAX_UVAR_VALUE(((struct cell*)0)->cell_member)), \
- MAX_UVAR_VALUE(((struct cell*)0)->cell_member)); \
- goto error; \
- }
- /* fix timer values to ticks */
- int tm_init_timers()
- {
- default_tm_cfg.fr_timeout=MS_TO_TICKS(default_tm_cfg.fr_timeout);
- default_tm_cfg.fr_inv_timeout=MS_TO_TICKS(default_tm_cfg.fr_inv_timeout);
- default_tm_cfg.wait_timeout=MS_TO_TICKS(default_tm_cfg.wait_timeout);
- default_tm_cfg.delete_timeout=MS_TO_TICKS(default_tm_cfg.delete_timeout);
- default_tm_cfg.rt_t1_timeout=MS_TO_TICKS(default_tm_cfg.rt_t1_timeout);
- default_tm_cfg.rt_t2_timeout=MS_TO_TICKS(default_tm_cfg.rt_t2_timeout);
- default_tm_cfg.tm_max_inv_lifetime=MS_TO_TICKS(default_tm_cfg.tm_max_inv_lifetime);
- default_tm_cfg.tm_max_noninv_lifetime=MS_TO_TICKS(default_tm_cfg.tm_max_noninv_lifetime);
- /* fix 0 values to 1 tick (minimum possible wait time ) */
- if (default_tm_cfg.fr_timeout==0) default_tm_cfg.fr_timeout=1;
- if (default_tm_cfg.fr_inv_timeout==0) default_tm_cfg.fr_inv_timeout=1;
- if (default_tm_cfg.wait_timeout==0) default_tm_cfg.wait_timeout=1;
- if (default_tm_cfg.delete_timeout==0) default_tm_cfg.delete_timeout=1;
- if (default_tm_cfg.rt_t2_timeout==0) default_tm_cfg.rt_t2_timeout=1;
- if (default_tm_cfg.rt_t1_timeout==0) default_tm_cfg.rt_t1_timeout=1;
- if (default_tm_cfg.tm_max_inv_lifetime==0) default_tm_cfg.tm_max_inv_lifetime=1;
- if (default_tm_cfg.tm_max_noninv_lifetime==0) default_tm_cfg.tm_max_noninv_lifetime=1;
-
- /* size fit checks */
- SIZE_FIT_CHECK(fr_timeout, default_tm_cfg.fr_timeout, "fr_timer");
- SIZE_FIT_CHECK(fr_inv_timeout, default_tm_cfg.fr_inv_timeout, "fr_inv_timer");
- #ifdef TM_DIFF_RT_TIMEOUT
- SIZE_FIT_CHECK(rt_t1_timeout, default_tm_cfg.rt_t1_timeout, "retr_timer1");
- SIZE_FIT_CHECK(rt_t2_timeout, default_tm_cfg.rt_t2_timeout, "retr_timer2");
- #endif
- SIZE_FIT_CHECK(end_of_life, default_tm_cfg.tm_max_inv_lifetime, "max_inv_lifetime");
- SIZE_FIT_CHECK(end_of_life, default_tm_cfg.tm_max_noninv_lifetime, "max_noninv_lifetime");
-
- memset(&user_fr_timeout, 0, sizeof(user_fr_timeout));
- memset(&user_fr_inv_timeout, 0, sizeof(user_fr_inv_timeout));
- #ifdef TM_DIFF_RT_TIMEOUT
- memset(&user_rt_t1_timeout, 0, sizeof(user_rt_t1_timeout));
- memset(&user_rt_t2_timeout, 0, sizeof(user_rt_t2_timeout));
- #endif
- memset(&user_inv_max_lifetime, 0, sizeof(user_inv_max_lifetime));
- memset(&user_noninv_max_lifetime, 0, sizeof(user_noninv_max_lifetime));
-
- DBG("tm: tm_init_timers: fr=%d fr_inv=%d wait=%d delete=%d t1=%d t2=%d"
- " max_inv_lifetime=%d max_noninv_lifetime=%d\n",
- default_tm_cfg.fr_timeout, default_tm_cfg.fr_inv_timeout,
- default_tm_cfg.wait_timeout, default_tm_cfg.delete_timeout,
- default_tm_cfg.rt_t1_timeout, default_tm_cfg.rt_t2_timeout,
- default_tm_cfg.tm_max_inv_lifetime, default_tm_cfg.tm_max_noninv_lifetime);
- return 0;
- error:
- return -1;
- }
- /* internal macro for timer_fixup()
- * performs size fit check if the timer name matches
- */
- #define IF_IS_TIMER_NAME(cell_member, cfg_name) \
- if ((name->len == sizeof(cfg_name)-1) && \
- (memcmp(name->s, cfg_name, sizeof(cfg_name)-1)==0)) { \
- SIZE_FIT_CHECK(cell_member, t, cfg_name); \
- }
- /* fixup function for the timer values
- * (called by the configuration framework)
- */
- int timer_fixup(void *handle, str *gname, str *name, void **val)
- {
- ticks_t t;
- t = MS_TO_TICKS((unsigned int)(long)(*val));
- /* fix 0 values to 1 tick (minimum possible wait time ) */
- if (t == 0) t = 1;
- /* size fix checks */
- IF_IS_TIMER_NAME(fr_timeout, "fr_timer")
- else IF_IS_TIMER_NAME(fr_inv_timeout, "fr_inv_timer")
- #ifdef TM_DIFF_RT_TIMEOUT
- else IF_IS_TIMER_NAME(rt_t1_timeout, "retr_timer1")
- else IF_IS_TIMER_NAME(rt_t2_timeout, "retr_timer2")
- #endif
- else IF_IS_TIMER_NAME(end_of_life, "max_inv_lifetime")
- else IF_IS_TIMER_NAME(end_of_life, "max_noninv_lifetime")
- *val = (void *)(long)t;
- return 0;
- error:
- return -1;
- }
- /******************** handlers ***************************/
- #ifndef TM_DEL_UNREF
- /* returns number of ticks before retrying the del, or 0 if the del.
- * was succesfull */
- inline static ticks_t delete_cell( struct cell *p_cell, int unlock )
- {
- /* there may still be FR/RETR timers, which have been reset
- (i.e., time_out==TIMER_DELETED) but are stilled linked to
- timer lists and must be removed from there before the
- structures are released
- */
- unlink_timers( p_cell );
- /* still in use ... don't delete */
- if ( IS_REFFED_UNSAFE(p_cell) ) {
- if (unlock) UNLOCK_HASH(p_cell->hash_index);
- DBG("DEBUG: delete_cell %p: can't delete -- still reffed (%d)\n",
- p_cell, p_cell->ref_count);
- /* delay the delete */
- /* TODO: change refcnts and delete on refcnt==0 */
- return cfg_get(tm, tm_cfg, delete_timeout);
- } else {
- if (unlock) UNLOCK_HASH(p_cell->hash_index);
- #ifdef EXTRA_DEBUG
- DBG("DEBUG: delete transaction %p\n", p_cell );
- #endif
- free_cell( p_cell );
- return 0;
- }
- }
- #endif /* TM_DEL_UNREF */
- /* generate a fake reply
- * it assumes the REPLY_LOCK is already held and returns unlocked */
- static void fake_reply(struct cell *t, int branch, int code )
- {
- branch_bm_t cancel_bitmap;
- short do_cancel_branch;
- enum rps reply_status;
- do_cancel_branch = is_invite(t) && prepare_cancel_branch(t, branch, 0);
- /* mark branch as canceled */
- t->uac[branch].request.flags|=F_RB_CANCELED;
- if ( is_local(t) ) {
- reply_status=local_reply( t, FAKED_REPLY, branch,
- code, &cancel_bitmap );
- } else {
- /* rely reply, but don't put on wait, we still need t
- * to send the cancels */
- reply_status=relay_reply( t, FAKED_REPLY, branch, code,
- &cancel_bitmap, 0 );
- }
- /* now when out-of-lock do the cancel I/O */
- if (do_cancel_branch) cancel_branch(t, branch, 0);
- /* it's cleaned up on error; if no error occurred and transaction
- completed regularly, I have to clean-up myself
- */
- if (reply_status == RPS_COMPLETED)
- put_on_wait(t);
- }
- /* return (ticks_t)-1 on error/disable and 0 on success */
- inline static ticks_t retransmission_handler( struct retr_buf *r_buf )
- {
- #ifdef EXTRA_DEBUG
- if (r_buf->my_T->flags & T_IN_AGONY) {
- LOG( L_ERR, "ERROR: transaction %p scheduled for deletion and"
- " called from RETR timer (flags %x)\n",
- r_buf->my_T, r_buf->my_T->flags );
- abort();
- }
- #endif
- if ( r_buf->activ_type==TYPE_LOCAL_CANCEL
- || r_buf->activ_type==TYPE_REQUEST ) {
- #ifdef EXTRA_DEBUG
- DBG("DEBUG: retransmission_handler : "
- "request resending (t=%p, %.9s ... )\n",
- r_buf->my_T, r_buf->buffer);
- #endif
- if (SEND_BUFFER( r_buf )==-1) {
- /* disable retr. timers => return -1 */
- fake_reply(r_buf->my_T, r_buf->branch, 503 );
- return (ticks_t)-1;
- }
- #ifdef TMCB_ONSEND
- if (unlikely(has_tran_tmcbs(r_buf->my_T, TMCB_REQUEST_SENT)))
- run_onsend_callbacks(TMCB_REQUEST_SENT, r_buf,
- 0, 0, TMCB_RETR_F);
- #endif
- } else {
- #ifdef EXTRA_DEBUG
- DBG("DEBUG: retransmission_handler : "
- "reply resending (t=%p, %.9s ... )\n",
- r_buf->my_T, r_buf->buffer);
- #endif
- t_retransmit_reply(r_buf->my_T);
- }
-
- return 0;
- }
- inline static void final_response_handler( struct retr_buf* r_buf,
- struct cell* t)
- {
- int silent;
- #ifdef USE_DNS_FAILOVER
- /*int i;
- int added_branches;
- */
- int branch_ret;
- int prev_branch;
- ticks_t now;
- #endif
- # ifdef EXTRA_DEBUG
- if (t->flags & T_IN_AGONY)
- {
- LOG( L_ERR, "ERROR: transaction %p scheduled for deletion and"
- " called from FR timer (flags %x)\n", t, t->flags);
- abort();
- }
- # endif
- /* FR for local cancels.... */
- if (r_buf->activ_type==TYPE_LOCAL_CANCEL)
- {
- #ifdef TIMER_DEBUG
- DBG("DEBUG: final_response_handler: stop retr for Local Cancel\n");
- #endif
- return;
- }
- /* FR for replies (negative INVITE replies) */
- if (r_buf->activ_type>0) {
- # ifdef EXTRA_DEBUG
- if (t->uas.request->REQ_METHOD!=METHOD_INVITE
- || t->uas.status < 200 ) {
- LOG(L_CRIT, "BUG: final_response_handler: unknown type reply"
- " buffer\n");
- abort();
- }
- # endif
- put_on_wait( t );
- return;
- };
- /* lock reply processing to determine how to proceed reliably */
- LOCK_REPLIES( t );
- /* now it can be only a request retransmission buffer;
- try if you can simply discard the local transaction
- state without compellingly removing it from the
- world */
- silent=
- /* don't go silent if disallowed globally ... */
- cfg_get(tm, tm_cfg, noisy_ctimer)==0
- /* ... or for this particular transaction */
- && has_noisy_ctimer(t) == 0
- /* not for UACs */
- && !is_local(t)
- /* invites only */
- && is_invite(t)
- /* parallel forking does not allow silent state discarding */
- && t->nr_of_outgoings==1
- /* on_negativ reply handler not installed -- serial forking
- * could occur otherwise */
- && t->on_negative==0
- /* the same for FAILURE callbacks */
- && !has_tran_tmcbs( t, TMCB_ON_FAILURE_RO|TMCB_ON_FAILURE)
- /* something received -- we will not be silent on error */
- && t->uac[r_buf->branch].last_received==0;
-
- if (silent) {
- UNLOCK_REPLIES(t);
- #ifdef EXTRA_DEBUG
- DBG("DEBUG: final_response_handler: transaction silently dropped (%p)"
- ", branch %d, last_received %d\n",t, r_buf->branch,
- t->uac[r_buf->branch].last_received);
- #endif
- put_on_wait( t );
- return;
- }
- #ifdef EXTRA_DEBUG
- DBG("DEBUG: final_response_handler:stop retr. and send CANCEL (%p)\n", t);
- #endif
- if ((r_buf->branch < MAX_BRANCHES) && /* r_buf->branch is always >=0 */
- (t->uac[r_buf->branch].last_received==0) &&
- (t->uac[r_buf->branch].request.buffer!=NULL) /* not a blind UAC */
- ){
- /* no reply received */
- #ifdef USE_DST_BLACKLIST
- if (cfg_get(core, core_cfg, use_dst_blacklist)
- && r_buf->my_T
- && r_buf->my_T->uas.request
- && (r_buf->my_T->uas.request->REQ_METHOD & cfg_get(tm, tm_cfg, tm_blst_methods_add))
- )
- dst_blacklist_add( BLST_ERR_TIMEOUT, &r_buf->dst,
- r_buf->my_T->uas.request);
- #endif
- #ifdef USE_DNS_FAILOVER
- /* if this is an invite, the destination resolves to more ips, and
- * it still hasn't passed more than fr_inv_timeout since we
- * started, add another branch/uac */
- if (cfg_get(core, core_cfg, use_dns_failover)){
- now=get_ticks_raw();
- if ((s_ticks_t)(t->end_of_life-now)>0){
- branch_ret=add_uac_dns_fallback(t, t->uas.request,
- &t->uac[r_buf->branch], 0);
- prev_branch=-1;
- while((branch_ret>=0) &&(branch_ret!=prev_branch)){
- prev_branch=branch_ret;
- branch_ret=t_send_branch(t, branch_ret, t->uas.request ,
- 0, 0);
- }
- }
- }
- #endif
- }
- fake_reply(t, r_buf->branch, 408);
- }
- /* handles retransmissions and fr timers */
- /* the following assumption are made (to avoid deleting/re-adding the timer):
- * retr_buf->retr_interval < ( 1<<((sizeof(ticks_t)*8-1) )
- * if retr_buf->retr_interval==0 => timer disabled
- * ==(ticks_t) -1 => retr. disabled (fr working)
- * retr_buf->retr_interval & (1 <<(sizeof(ticks_t)*8-1) => retr. & fr reset
- * (we never reset only retr, it's either reset both of them or retr
- * disabled & reset fr). In this case the fr_origin will contain the
- * "time" of the reset and next retr should occur at
- * fr->origin+retr_interval (we also assume that we'll never reset retr
- * to a lower value then the current one)
- */
- ticks_t retr_buf_handler(ticks_t ticks, struct timer_ln* tl, void *p)
- {
- struct retr_buf* rbuf ;
- ticks_t fr_remainder;
- ticks_t retr_remainder;
- ticks_t retr_interval;
- ticks_t new_retr_interval;
- struct cell *t;
- rbuf=(struct retr_buf*)
- ((void*)tl-(void*)(&((struct retr_buf*)0)->timer));
- membar_depends(); /* to be on the safe side */
- t=rbuf->my_T;
-
- #ifdef TIMER_DEBUG
- DBG("tm: timer retr_buf_handler @%d (%p -> %p -> %p)\n",
- ticks, tl, rbuf, t);
- #endif
- if (unlikely(rbuf->flags & F_RB_DEL_TIMER)){
- /* timer marked for deletion */
- rbuf->t_active=0; /* mark it as removed */
- /* a membar is not really needed, in the very unlikely case that
- * another process will see old t_active's value and will try to
- * delete the timer again, but since timer_del it's safe in this cases
- * it will be a no-op */
- return 0;
- }
- /* overflow safe check (should work ok for fr_intervals < max ticks_t/2) */
- if ((s_ticks_t)(rbuf->fr_expire-ticks)<=0){
- /* final response */
- rbuf->t_active=0; /* mark the timer as removed
- (both timers disabled)
- a little race risk, but
- nothing bad would happen */
- rbuf->flags|=F_RB_TIMEOUT;
- /* WARNING: the next line depends on taking care not to start the
- * wait timer before finishing with t (if this is not
- * guaranteed then comment the timer_allow_del() line) */
- timer_allow_del(); /* [optional] allow timer_dels, since we're done
- and there is no race risk */
- final_response_handler(rbuf, t);
- return 0;
- }else{
- /* 4 possible states running (t1), t2, paused, disabled */
- if ((s_ticks_t)(rbuf->retr_expire-ticks)<=0){
- if (rbuf->flags & F_RB_RETR_DISABLED)
- goto disabled;
- /* retr_interval= min (2*ri, rt_t2) , *p==2*ri*/
- /* no branch version:
- #idef CC_SIGNED_RIGHT_SHIFT
- ri= rt_t2+((2*ri-rt_t2) &
- ((signed)(2*ri-rt_t2)>>(sizeof(ticks_t)*8-1));
- #else
- ri=rt_t2+((2*ri-rt_t2)& -(2*ri<rt_t2));
- #endif
- */
-
- /* get the current interval from timer param. */
- if ((rbuf->flags & F_RB_T2) ||
- (((ticks_t)(unsigned long)p)>RT_T2_TIMEOUT(rbuf))){
- retr_interval=RT_T2_TIMEOUT(rbuf);
- new_retr_interval=RT_T2_TIMEOUT(rbuf);
- }else{
- retr_interval=(ticks_t)(unsigned long)p;
- new_retr_interval=retr_interval<<1;
- }
- #ifdef TIMER_DEBUG
- DBG("tm: timer: retr: new interval %d (max %d)\n",
- retr_interval, RT_T2_TIMEOUT(rbuf));
- #endif
- /* we could race with the reply_received code, but the
- * worst thing that can happen is to delay a reset_to_t2
- * for crt_interval and send an extra retr.*/
- rbuf->retr_expire=ticks+retr_interval;
- /* set new interval to -1 on error, or retr_int. on success */
- retr_remainder=retransmission_handler(rbuf) | retr_interval;
- /* store the next retr. interval inside the timer struct,
- * in the data member */
- tl->data=(void*)(unsigned long)(new_retr_interval);
- }else{
- retr_remainder= rbuf->retr_expire-ticks;
- DBG("tm: timer: retr: nothing to do, expire in %d\n",
- retr_remainder);
- }
- }
- /* skip: */
- /* return minimum of the next retransmission handler and the
- * final response (side benefit: it properly cancels timer if ret==0 and
- * sleeps for fr_remainder if retr. is canceled [==(ticks_t)-1]) */
- fr_remainder=rbuf->fr_expire-ticks; /* to be more precise use
- get_ticks_raw() instead of ticks
- (but make sure that
- crt. ticks < fr_expire */
- #ifdef TIMER_DEBUG
- DBG("tm: timer retr_buf_handler @%d (%p ->%p->%p) exiting min (%d, %d)\n",
- ticks, tl, rbuf, t, retr_remainder, fr_remainder);
- #endif
- #ifdef EXTRA_DEBUG
- if (retr_remainder==0 || fr_remainder==0){
- BUG("tm: timer retr_buf_handler: 0 remainder => disabling timer!: "
- "retr_remainder=%d, fr_remainder=%d\n", retr_remainder,
- fr_remainder);
- }
- #endif
- if (retr_remainder<fr_remainder)
- return retr_remainder;
- else{
- /* hack to switch to the slow timer */
- #ifdef TM_FAST_RETR_TIMER
- tl->flags&=~F_TIMER_FAST;
- #endif
- return fr_remainder;
- }
- disabled:
- return rbuf->fr_expire-ticks;
- }
- ticks_t wait_handler(ticks_t ti, struct timer_ln *wait_tl, void* data)
- {
- struct cell *p_cell;
- ticks_t ret;
- p_cell=(struct cell*)data;
- #ifdef TIMER_DEBUG
- DBG("DEBUG: WAIT timer hit @%d for %p (timer_lm %p)\n",
- ti, p_cell, wait_tl);
- #endif
- #ifdef TM_DEL_UNREF
- /* stop cancel timers if any running */
- if ( is_invite(p_cell) ) cleanup_localcancel_timers( p_cell );
- /* remove the cell from the hash table */
- LOCK_HASH( p_cell->hash_index );
- remove_from_hash_table_unsafe( p_cell );
- UNLOCK_HASH( p_cell->hash_index );
- p_cell->flags |= T_IN_AGONY;
- UNREF_FREE(p_cell);
- ret=0;
- #else /* TM_DEL_UNREF */
- if (p_cell->flags & T_IN_AGONY){
- /* delayed delete */
- /* we call delete now without any locking on hash/ref_count;
- we can do that because delete_handler is only entered after
- the delete timer was installed from wait_handler, which
- removed transaction from hash table and did not destroy it
- because some processes were using it; that means that the
- processes currently using the transaction can unref and no
- new processes can ref -- we can wait until ref_count is
- zero safely without locking
- */
- ret=delete_cell( p_cell, 0 /* don't unlock on return */ );
- }else {
- /* stop cancel timers if any running */
- if ( is_invite(p_cell) ) cleanup_localcancel_timers( p_cell );
- /* remove the cell from the hash table */
- LOCK_HASH( p_cell->hash_index );
- remove_from_hash_table_unsafe( p_cell );
- p_cell->flags |= T_IN_AGONY;
- /* delete (returns with UNLOCK-ed_HASH) */
- ret=delete_cell( p_cell, 1 /* unlock on return */ );
- }
- #endif /* TM_DEL_UNREF */
- return ret;
- }
|