瀏覽代碼

- tm migrated to the new timers (tm timers completely re-written).
- tm structures re-ordered & cleaned up for better "packing" on 64 bit
machines => size reduction (~800 bytes less per transaction on a 64 bit
machine)
- params: - retr_timer1p1, retr_timer1p2, retr_timer1p3 removed and replaced
by retr_timer1 and retr_timer2
- all timer values are now expressed in milliseconds (they were in
seconds before). Affected params: fr_timer, fr_inv_timer,
wt_timer, delete_timer, retr_timer1, retr_timer2
- retr_timer1 (first retransmission) changed to 500 ms
- delete_timer changed to 200 ms
- functions: - new t_set_fr(timeout_fr_inv, timeout_fr) -- allows changing the
transaction timer from script, even if the transaction was already created
(see tm docs for more).
- fr_inv_timer/fr_timer can be changed now without any performance penalty
- more precise retransmissions timing
- overall improved tm speed an memory footprint (e.g. on a 64 bit 2*Xeon 2.8Ghz
+ HT, optimized ser, NO_DEBUG => ~ 6370 cps new version, ~6000 cps old
version + rel_0_9_0 tm inser_timer fix; memory usage @ 6000cps: 490Mb new
version, 616Mb old version).
- NEWS updated

Andrei Pelinescu-Onciul 20 年之前
父節點
當前提交
57857a94b6

+ 1 - 1
Makefile.defs

@@ -61,7 +61,7 @@ MAIN_NAME=ser
 VERSION = 0
 PATCHLEVEL = 10
 SUBLEVEL =   99
-EXTRAVERSION = -dev25-timers
+EXTRAVERSION = -dev26-tm-timers
 
 RELEASE=$(VERSION).$(PATCHLEVEL).$(SUBLEVEL)$(EXTRAVERSION)
 OS = $(shell uname -s | sed -e s/SunOS/solaris/ | tr "[A-Z]" "[a-z]")

+ 20 - 0
NEWS

@@ -16,8 +16,25 @@ modules:
                 hashing after an uri (to, from or request uri)
               - improved uri hashing (password is ignored, port is used only
                 if != 5060 or 5061)
+ - tm        - migrated to the new timers (tm timers completely rewritten)
+             - improved speed and less memory usage
+             - much more precise reptransmissions timing
+             - params: - retr_timer1p1, retr_timer1p2, retr_timer1p3 removed
+                         and replaced by retr_timer1 and retr_timer2
+                       - all timer values are now expressed in milliseconds
+                         (they were in seconds before).
+                         Affected params: fr_timer, fr_inv_timer, wt_timer,
+                         delete_timer, retr_timer1, retr_timer2
+                       - retr_timer1 (first retransmission) changed to 500 ms
+                       - delete_timer changed to 200 ms
+             - functions:
+                       - new t_set_fr(timeout_fr_inv, timeout_fr) -- allows
+                         changing the transaction timer from script, even if
+                         the transaction was already created (see tm docs for
+                         more).
  
 core:
+ - timer: - improved performance/precision, new api, see doc/timers.txt 
  - tcp: - improved  performance (io event handling), using OS specific
            optimizations
         - 1024 connections limit removed (see tcp_max_connections)
@@ -34,6 +51,9 @@ core:
 - default on reply route added: onreply_route {.. } will add a default 
     onreply route that will be executed for any reply (usefull to catch
     replies without using tm)
+- branch_routes added (tm triggered), only a very limited number of commands
+   are available (see tm docs)
+- avps directly accessible from script with %avp_name (variable style)
 new config variables:
    tos = number  - ip type of service (TOS) value
    dns_try_ipv6 = yes/no - if yes and a dns lookup fails, it will retry it

+ 6 - 6
modules/tm/config.h

@@ -46,20 +46,20 @@
 
 /* FINAL_RESPONSE_TIMER ... tells how long should the transaction engine
    wait if no final response comes back*/
-#define FR_TIME_OUT       30
-#define INV_FR_TIME_OUT   120
+#define FR_TIME_OUT        30000 /* ms */
+#define INV_FR_TIME_OUT   120000 /* ms */
 
 /* WAIT timer ... tells how long state should persist in memory after
    a transaction was finalized*/
-#define WT_TIME_OUT       5
+#define WT_TIME_OUT       5000 /* ms */
 
 /* DELETE timer ... tells how long should the transaction persist in memory
    after it was removed from the hash table and before it will be deleted */
-#define DEL_TIME_OUT      2
+#define DEL_TIME_OUT      200 /* ms */
  
 /* retransmission timers */
-#define RETR_T1           1
-#define RETR_T2           4
+#define RETR_T1           500 /* ms */
+#define RETR_T2          4000 /* ms */
 
 /* when first reply is sent, this additional space is allocated so that
    one does not have to reallocate share memory when the message is

+ 46 - 0
modules/tm/doc/functions.xml

@@ -366,4 +366,50 @@ t_forward_nonack("1.2.3.4", "5060");
 	</example>
     </section>
 
+	<section id="t_set_fr">
+	<title>
+	    <function>t_set_fr(fr_inv_timeout [, fr_timeout])</function>
+	</title>
+	<para>
+		Sets the fr_inv_timeout and optionally fr_timeout for the current
+		transaction. If the transaction is already created (e.g called after
+		 <function>t_relay()</function> or in an onreply_route) all the
+		 branches will have their final response timeout updated on-the-fly.
+		If one of the parameters is 0, it's value won't be changed.
+	</para>
+	<para>Meaning of the parameters is as follows:</para>
+	<itemizedlist>
+	    <listitem>
+		<para><emphasis>fr_inv_timeout</emphasis> - new final response timeout
+			(in milliseconds) for INVITEs. See also 
+			<varname>fr_inv_timer</varname>.
+		</para>
+		<para><emphasis>fr_timeout</emphasis> - new final response timeout
+		 	(in milliseconds) for non-INVITE transaction, or INVITEs which 
+			haven't received yet a provisional response. See also
+			<varname>fr_timer</varname>.
+		</para>
+	    </listitem>
+	</itemizedlist>
+	<example>
+	    <title><function>t_set_fr</function> usage</title>
+	    <programlisting>
+...
+route { 
+	t_set_fr(10000); # set only fr invite timeout to 10s
+	t_on_branch("1");
+	t_relay(); 
+} 
+
+branch_route[1] {
+	# if we are calling the pstn, extend the invite timeout to 50s
+	# for all the branches, and set the no-reply-received timeout to 2s
+	if (uri=~"sip:[0-9]+"){
+		t_set_fr(50000, 2000); 
+	}
+}
+	    </programlisting>
+	</example>
+	</section>
+
 </section>

+ 15 - 48
modules/tm/doc/params.xml

@@ -18,7 +18,7 @@
 	<title><varname>fr_timer</varname> (integer)</title>
 	<para>
 	    Timer which hits if no final reply for a request or ACK for a
-	    negative INVITE reply arrives (in seconds).
+	    negative INVITE reply arrives (in milliseconds).
 	</para>
 	<para>
 	    Default value is 30 seconds.
@@ -37,7 +37,7 @@ modparam("tm", "fr_timer", 10)
 	<title><varname>fr_inv_timer</varname> (integer)</title>
 	<para>
 	    Timer which hits if no final reply for an INVITE arrives after a
-	    provisional message was received (in seconds).
+	    provisional message was received (in milliseconds).
 	</para>
 	<para>
 	    Default value is 120 seconds.
@@ -81,7 +81,7 @@ modparam("tm", "wt_timer", 10)
 	    process will be tried to be deleted again.
 	</para>
 	<para>
-	    Default value is 2 seconds.
+	    Default value is 200 milliseconds.
 	</para>
 	<example>
 	    <title>Set <varname>delete_timer</varname> parameter</title>
@@ -93,55 +93,19 @@ modparam("tm", "delete_timer", 5)
 	</example>
     </section>
     
-    <section id="retr_timer1p1">
-	<title><varname>retr_timer1p1</varname> (integer)</title>
+    <section id="retr_timer1">
+	<title><varname>retr_timer1</varname> (integer)</title>
 	<para>
-	    Retransmission period.
+	    Initial retransmission period (in milliseconds).
 	</para>
 	<para>
-	    Default value is 1 second.
+	    Default value is 500 milliseconds.
 	</para>
 	<example>
-	    <title>Set <varname>retr_timer1p1</varname> parameter</title>
+	    <title>Set <varname>retr_timer1</varname> parameter</title>
 	    <programlisting>
 ...
-modparam("tm", "retr_timer1p1", 2)
-...
-	    </programlisting>
-	</example>
-    </section>
-
-    <section id="retr_timer1p2">
-	<title><varname>retr_timer1p2</varname> (integer)</title>
-	<para>
-	    Retransmission period.
-	</para>
-	<para>
-	    Default value is 2 * <varname>retr_timer1p1</varname> second.
-	</para>
-	<example>
-	    <title>Set <varname>retr_timer1p2</varname> parameter</title>
-	    <programlisting>
-...
-modparam("tm", "retr_timer1p2", 4)
-...
-	    </programlisting>
-	</example>
-    </section>
-
-    <section id="retr_timer1p3">
-	<title><varname>retr_timer1p3</varname> (integer)</title>
-	<para>
-	    Retransmission period.
-	</para>
-	<para>
-	    Default value is 4 * <varname>retr_timer1p1</varname> second.
-	</para>
-	<example>
-	    <title>Set <varname>retr_timer1p4</varname> parameter</title>
-	    <programlisting>
-...
-modparam("tm", "retr_timer1p3", 8)
+modparam("tm", "retr_timer1", 1000)
 ...
 	    </programlisting>
 	</example>
@@ -150,16 +114,19 @@ modparam("tm", "retr_timer1p3", 8)
     <section id="retr_timer2">
 	<title><varname>retr_timer2</varname> (integer)</title>
 	<para>
-	    Maximum retransmission period.
+	    Maximum retransmission period (in milliseconds). The retransmission
+		interval starts with <varname>retr_timer1</varname> and increases until
+		it reaches this value. After this it stays constant at 
+		<varname>retr_timer2</varname>.
 	</para>
 	<para>
-	    Default value is 4 seconds.
+	    Default value is 4000 milliseconds.
 	</para>
 	<example>
 	    <title>Set <varname>retr_timer2</varname> parameter</title>
 	    <programlisting>
 ...
-modparam("tm", "retr_timer2", 8)
+modparam("tm", "retr_timer2", 2000)
 ...
 	    </programlisting>
 	</example>

+ 5 - 12
modules/tm/h_table.c

@@ -61,6 +61,7 @@
 #include "t_stats.h"
 #include "h_table.h"
 #include "fix_lumps.h" /* free_via_clen_lump */
+#include "timer.h"
 
 static enum kill_reason kr;
 
@@ -213,10 +214,7 @@ static void inline init_branches(struct cell *t)
 		uac=&t->uac[i];
 		uac->request.my_T = t;
 		uac->request.branch = i;
-#ifdef EXTRA_DEBUG
-		uac->request.fr_timer.tg = TG_FR;
-		uac->request.retr_timer.tg = TG_RT;
-#endif
+		init_rb_timers(&uac->request);
 		uac->local_cancel=uac->request;
 	}
 }
@@ -239,11 +237,10 @@ struct cell*  build_cell( struct sip_msg* p_msg )
 	memset( new_cell, 0, sizeof( struct cell ) );
 
 	/* UAS */
-#ifdef EXTRA_DEBUG
-	new_cell->uas.response.retr_timer.tg=TG_RT;
-	new_cell->uas.response.fr_timer.tg=TG_FR;
-#endif
 	new_cell->uas.response.my_T=new_cell;
+	init_rb_timers(&new_cell->uas.response);
+	/* timers */
+	init_cell_timers(new_cell);
 
 	/* move the current avp list to transaction -bogdan */
 	old = set_avp_list(AVP_TRACK_FROM | AVP_CLASS_USER,  &new_cell->user_avps );
@@ -270,10 +267,6 @@ struct cell*  build_cell( struct sip_msg* p_msg )
 
 	new_cell->relayed_reply_branch   = -1;
 	/* new_cell->T_canceled = T_UNDEFINED; */
-#ifdef EXTRA_DEBUG
-	new_cell->wait_tl.tg=TG_WT;
-	new_cell->dele_tl.tg=TG_DEL;
-#endif
 
 	init_synonym_id(new_cell);
 	init_cell_lock(  new_cell );

+ 46 - 42
modules/tm/h_table.h

@@ -35,6 +35,7 @@
  * 2004-02-13  t->is_invite, t->local, t->noisy_ctimer replaced
  *             with flags (bogdan)
  * 2004-08-23  avp support added - avp list linked in transaction (bogdan)
+ * 2005-11-03  updated to the new timer interface (dropped tm timers) (andrei)
  */
 
 #include "defs.h"
@@ -50,6 +51,7 @@
 #include "../../types.h"
 #include "../../md5utils.h"
 #include "../../usr_avp.h"
+#include "../../timer.h"
 #include "config.h"
 
 struct s_table;
@@ -63,7 +65,7 @@ struct retr_buf;
 #include "sip_msg.h"
 #include "t_reply.h"
 #include "t_hooks.h"
-#include "timer.h"
+#include "../../timer.h"
 
 #define LOCK_HASH(_h) lock_hash((_h))
 #define UNLOCK_HASH(_h) unlock_hash((_h))
@@ -98,25 +100,28 @@ void unlock_hash(int i);
 enum kill_reason { REQ_FWDED=1, REQ_RPLD=2, REQ_RLSD=4, REQ_EXIST=8 };
 
 
+/* #define F_RB_T_ACTIVE		0x01  (obsolete) fr or retr active */
+#define F_RB_T2				0x02
+#define F_RB_RETR_DISABLED	0x04 /* retransmission disabled */
+#define F_RB_FR_INV	0x08 /* timer switched to FR_INV */
+
+
 typedef struct retr_buf
 {
-	int activ_type;
+	short activ_type;
 	/* set to status code if the buffer is a reply,
 	0 if request or -1 if local CANCEL */
-
+	volatile unsigned char flags; /* DISABLED, T2 */
+	volatile unsigned char t_active; /* timer active */
+	unsigned short branch; /* no more then 65k branches :-) */
+	short   buffer_len;
 	char *buffer;
-	int   buffer_len;
-	
-	struct dest_info dst;
-
-	/* a message can be linked just to retransmission and FR list */
-	struct timer_link retr_timer;
-	struct timer_link fr_timer;
-	enum lists retr_list;
-
 	/*the cell that contains this retrans_buff*/
 	struct cell* my_T;
-	unsigned int branch;
+	struct timer_ln timer;
+	struct dest_info dst;
+	ticks_t retr_expire;
+	ticks_t fr_expire; /* ticks value after which fr. will fire */
 }retr_buf_type;
 
 
@@ -128,11 +133,11 @@ typedef struct ua_server
 	struct sip_msg   *request;
 	char             *end_request;
 	struct retr_buf  response;
-	unsigned int     status;
 	/* keep to-tags for local 200 replies for INVITE -- 
 	 * we need them for dialog-wise matching of ACKs;
 	 * the pointer shows to shmem-ed reply */
 	str				 local_totag;
+	unsigned int     status;
 }ua_server_type;
 
 
@@ -160,9 +165,9 @@ typedef struct ua_client
 
 
 struct totag_elem {
+	struct totag_elem *next;
 	str tag;
 	short acked;
-	struct totag_elem *next;
 };
 
 
@@ -176,7 +181,8 @@ struct totag_elem {
    dropping when C timer hits */
 #define T_NOISY_CTIMER_FLAG  (1<<2)
 
-
+#define T_IN_AGONY (1<<3) /* set if waiting to die (delete timer)
+                             TODO: replace it with del on unref */
 
 /* transaction context */
 
@@ -190,7 +196,9 @@ typedef struct cell
 	/* sequence number within hash collision slot */
 	unsigned int  label;
 	/* different information about the transaction */
-	unsigned int flags;
+	unsigned short flags;
+	/* number of forks */
+	short nr_of_outgoings;
 
 	/* how many processes are currently processing this transaction ;
 	   note that only processes working on a request/reply belonging
@@ -218,44 +226,40 @@ typedef struct cell
 	struct tmcb_head_list tmcb_hl;
 
 	/* bindings to wait and delete timer */
-	struct timer_link wait_tl;
-	struct timer_link dele_tl;
+	struct timer_ln wait_timer; /* used also for delete */
 
-	/* number of forks */
-	int nr_of_outgoings;
-	/* nr of replied branch; 0..MAX_BRANCHES=branch value,
-	 * -1 no reply, -2 local reply */
-	int relayed_reply_branch;
 	/* UA Server */
 	struct ua_server  uas;
 	/* UA Clients */
 	struct ua_client  uac[ MAX_BRANCHES ];
-
+	
+	/* to-tags of 200/INVITEs which were received from downstream and 
+	 * forwarded or passed to UAC; note that there can be arbitrarily 
+	 * many due to downstream forking; */
+	struct totag_elem *fwded_totags;
+	/* list with user avp */
+	struct usr_avp *user_avps;
+	
 	/* protection against concurrent reply processing */
 	ser_lock_t   reply_mutex;
+	
+	ticks_t fr_timeout;     /* final response interval for retr_bufs */
+	ticks_t fr_inv_timeout; /* final inv. response interval for retr_bufs */
+
+	/* nr of replied branch; 0..MAX_BRANCHES=branch value,
+	 * -1 no reply, -2 local reply */
+	short relayed_reply_branch;
 
 	/* the route to take if no final positive reply arrived */
-	unsigned int on_negative;
+	unsigned short on_negative;
 	/* the onreply_route to be processed if registered to do so */
-	unsigned int on_reply;
-	     /* The route to take for each downstream branch separately */
-	unsigned int on_branch;
+	unsigned short on_reply;
+	 /* The route to take for each downstream branch separately */
+	unsigned short on_branch;
 
-	/* MD5checksum  (meaningful only if syn_branch=0 */
+	/* MD5checksum  (meaningful only if syn_branch=0) */
 	char md5[MD5_LEN];
 
-#ifdef	EXTRA_DEBUG
-	/* scheduled for deletion ? */
-	short damocles;
-#endif
-
-	/* to-tags of 200/INVITEs which were received from downstream and 
-	 * forwarded or passed to UAC; note that there can be arbitrarily 
-	 * many due to downstream forking; */
-	struct totag_elem *fwded_totags;
-
-	/* list with user avp */
-	struct usr_avp *user_avps;
 }cell_type;
 
 

+ 0 - 7
modules/tm/lock.c

@@ -294,10 +294,3 @@ int release_timerlist_lock( struct timer *timerlist )
 	/* the same as above */
 	return 0;
 }
-
-int init_timerlist_lock( enum lists timerlist_id)
-{
-	get_timertable()->timers[timerlist_id].mutex=
-		&(timer_group_lock[ timer_group[timerlist_id] ]);
-	return 0;
-}

+ 0 - 2
modules/tm/lock.h

@@ -68,7 +68,6 @@ enum timer_groups {
 
 
 #include "h_table.h"
-#include "timer.h" 
 
 /* Uni*x permissions for IPC */
 #define IPC_PERMISSIONS 0666
@@ -133,7 +132,6 @@ static inline void _unlock( ser_lock_t* s )
 #endif
 }
 
-int init_timerlist_lock(  enum lists timerlist_id);
 
 
 #endif

+ 5 - 6
modules/tm/t_cancel.c

@@ -108,22 +108,21 @@ void cancel_branch( struct cell *t, int branch )
 		LOG(L_ERR, "ERROR: attempt to build a CANCEL failed\n");
 		return;
 	}
-
-	     /* install cancel now */
+	/* install cancel now */
 	crb->buffer = cancel;
 	crb->buffer_len = len;
 	crb->dst = irb->dst;
 	crb->branch = branch;
-
 	/* label it as cancel so that FR timer can better now how to
 	   deal with it */
 	crb->activ_type = TYPE_LOCAL_CANCEL;
 
 	DBG("DEBUG: cancel_branch: sending cancel...\n");
 	SEND_BUFFER( crb );
-	
-	     /*sets and starts the FINAL RESPONSE timer */
-	start_retr( crb );
+	/*sets and starts the FINAL RESPONSE timer */
+	if (start_retr( crb )!=0)
+		LOG(L_CRIT, "BUG: cancel_branch: failed to start retransmission"
+					" for %p\n", crb);
 }
 
 

+ 7 - 7
modules/tm/t_funcs.c

@@ -96,13 +96,10 @@ void tm_shutdown()
 {
 
 	DBG("DEBUG: tm_shutdown : start\n");
-	unlink_timer_lists();
 
 	/* destroy the hash table */
 	DBG("DEBUG: tm_shutdown : emptying hash table\n");
 	free_hash_table( );
-	DBG("DEBUG: tm_shutdown : releasing timers\n");
-	free_timer_table();
 	DBG("DEBUG: tm_shutdown : removing semaphores\n");
 	lock_cleanup();
 	DBG("DEBUG: tm_shutdown : destroying tmcb lists\n");
@@ -118,9 +115,7 @@ int t_release_transaction( struct cell *trans )
 {
 	set_kr(REQ_RLSD);
 
-	reset_timer( & trans->uas.response.fr_timer );
-	reset_timer( & trans->uas.response.retr_timer );
-
+	stop_rb_timers(&trans->uas.response);
 	cleanup_uac_timers( trans );
 	
 	put_on_wait( trans );
@@ -158,7 +153,12 @@ void put_on_wait(  struct cell  *Trans  )
 		4.									WAIT timer executed,
 											transaction deleted
 	*/
-	set_1timer( &Trans->wait_tl, WT_TIMER_LIST, 0 );
+	if (timer_add(&Trans->wait_timer, wait_timeout)==0){
+		/* sucess */
+		t_stats_wait();
+	}else{
+		DBG("tm: put_on_wait: transaction %p already on wait\n", Trans);
+	}
 }
 
 

+ 17 - 27
modules/tm/t_funcs.h

@@ -32,6 +32,8 @@
   *  2003-03-13  now send_pr_buffer will be called w/ function/line info
   *               only when compiling w/ -DEXTRA_DEBUG (andrei)
   *  2003-03-31  200 for INVITE/UAS resent even for UDP (jiri) 
+  *  2005-11-09  added stop_rb_timers, updated to the new timer interface 
+  *               (andrei)
   */
 
 
@@ -124,37 +126,26 @@ int fr_avp2timer(unsigned int* timer);
 int fr_inv_avp2timer(unsigned int* timer);
 
 
+#ifdef TIMER_DEBUG
+#define start_retr(rb) \
+	_set_fr_retr((rb), \
+				((rb)->dst.proto==PROTO_UDP)?rt_t1_timeout:(ticks_t)(-1), \
+				__FILE__, __FUNCTION__, __LINE__)
 
-static void inline _set_fr_retr( struct retr_buf *rb, int retr )
-{
-	unsigned int timer;
+#define force_retr(rb) \
+	_set_fr_retr((rb), rt_t1_timeout, __FILE__, __FUNCTION__, __LINE__)
 
-	if (retr) {
-		rb->retr_list=RT_T1_TO_1;
-		set_timer( &rb->retr_timer, RT_T1_TO_1, 0 );
-	}
+#else
+#define start_retr(rb) \
+	_set_fr_retr((rb), \
+				((rb)->dst.proto==PROTO_UDP)?rt_t1_timeout:(ticks_t)(-1))
+
+#define force_retr(rb) \
+	_set_fr_retr((rb), rt_t1_timeout)
 
-	if (!fr_avp2timer(&timer)) {
-		DBG("_set_fr_retr: FR_TIMER = %d\n", timer);
-		set_timer(&rb->fr_timer, FR_TIMER_LIST, &timer);
-		     /* Automatically enable noisy_ctimer for the
-		      * transaction
-		      */
-		rb->my_T->flags |= T_NOISY_CTIMER_FLAG;
-	} else {
-		set_timer(&rb->fr_timer, FR_TIMER_LIST, 0);
-	}
-}
+#endif
 
-static void inline start_retr(struct retr_buf *rb)
-{
-	_set_fr_retr(rb, rb->dst.proto==PROTO_UDP);
-}
 
-static void inline force_retr(struct retr_buf *rb)
-{
-	_set_fr_retr(rb, 1);
-}
 
 
 void tm_shutdown();
@@ -177,7 +168,6 @@ int get_ip_and_port_from_uri( str* uri , unsigned int *param_ip,
 
 void put_on_wait(  struct cell  *Trans  );
 
-void cleanup_localcancel_timers( struct cell *t );
 
 int t_relay_to( struct sip_msg  *p_msg ,
 	struct proxy_l *proxy, int proto, int replicate ) ;

+ 18 - 12
modules/tm/t_fwd.c

@@ -202,7 +202,9 @@ int add_blind_uac( /*struct cell *t*/ )
 	/* start FR timer -- protocol set by default to PROTO_NONE,
        which means retransmission timer will not be started
     */
-	start_retr(&t->uac[branch].request);
+	if (start_retr(&t->uac[branch].request)!=0)
+		LOG(L_CRIT, "BUG: add_blind_uac: start retr failed for %p\n",
+				&t->uac[branch].request);
 	/* we are on a timer -- don't need to put on wait on script
 	   clean-up	
 	*/
@@ -263,7 +265,7 @@ int add_uac( struct cell *t, struct sip_msg *request, str *uri, str* next_hop,
 	}
 
 	hostent2su( &to, &proxy->host, proxy->addr_idx, 
-		proxy->port ? proxy->port:SIP_PORT);
+		proxy->port ? proxy->port:((proto==PROTO_TLS)?SIPS_PORT:SIP_PORT));
 
 	send_sock=get_send_socket( request, &to , proto);
 	if (send_sock==0) {
@@ -396,18 +398,20 @@ void e2e_cancel( struct sip_msg *cancel_msg,
 			if (SEND_BUFFER(&t_cancel->uac[i].request) == -1) {
 				LOG(L_ERR, "ERROR: e2e_cancel: send failed\n");
 			}
-			start_retr(&t_cancel->uac[i].request);
+			if (start_retr( &t_cancel->uac[i].request )!=0)
+				LOG(L_CRIT, "BUG: e2e_cancel: failed to start retr. for %p\n",
+							&t_cancel->uac[i].request);
 		} else {
 			if (t_invite->uac[i].last_received < 100) {
-				     /* No provisional response received, stop
-				      * retransmission timers
-				      */
-				reset_timer(&t_invite->uac[i].request.retr_timer);
-				reset_timer(&t_invite->uac[i].request.fr_timer);
-
-				     /* Generate faked reply */
+				/* No provisional response received, stop
+				 * retransmission timers */
+				stop_rb_retr(&t_invite->uac[i].request);
+				/* no need to stop fr, it will be stoped by relay_reply
+				 * put_on_wait -- andrei */
+				/* Generate faked reply */
 				LOCK_REPLIES(t_invite);
-				if (relay_reply(t_invite, FAKED_REPLY, i, 487, &tmp_bm) == RPS_ERROR) {
+				if (relay_reply(t_invite, FAKED_REPLY, i, 487, &tmp_bm) == 
+						RPS_ERROR) {
 					lowest_error = -1;
 				}
 			}
@@ -553,7 +557,9 @@ int t_forward_nonack( struct cell *t, struct sip_msg* p_msg ,
 			} else {
 				success_branch++;
 			}
-			start_retr( &t->uac[i].request );
+			if (start_retr( &t->uac[i].request )!=0)
+				LOG(L_CRIT, "BUG: t_forward_non_ack: "
+						"failed to start retr. for %p\n", &t->uac[i].request);
 		}
 	}
 	if (success_branch<=0) {

+ 60 - 2
modules/tm/t_lookup.c

@@ -79,6 +79,7 @@
  * 2004-10-10: use of mhomed disabled for replies (jiri)
  * 2005-02-01: use the incoming request interface for sending the replies
  *             - changes in init_rb() (bogdan)
+ *  2005-12-09  added t_set_fr()  (andrei)
  */
 
 #include "defs.h"
@@ -91,6 +92,7 @@
 #include "../../parser/parse_from.h"
 #include "../../ut.h"
 #include "../../timer.h"
+#include "../../timer_ticks.h"
 #include "../../hash_func.h"
 #include "../../globals.h"
 #include "../../forward.h"
@@ -920,9 +922,9 @@ int t_check( struct sip_msg* p_msg , int *param_branch )
 
 		}
 #ifdef EXTRA_DEBUG
-		if ( T && T!=T_UNDEFINED && T->damocles) {
+		if ( T && T!=T_UNDEFINED && T->flags & (T_IN_AGONY)) {
 			LOG( L_ERR, "ERROR: transaction %p scheduled for deletion "
-				"and called from t_check\n", T);
+				"and called from t_check (flags=%x)\n", T, T->flags);
 			abort();
 		}
 #endif
@@ -945,6 +947,7 @@ int init_rb( struct retr_buf *rb, struct sip_msg *msg)
 	int proto;
 	int backup_mhomed;
 
+	/* rb. timers are init. init_t()/new_cell() */
 	via=msg->via1;
 	if (!reply_to_via) {
 		update_sock_struct_from_ip( &rb->dst.to, msg );
@@ -983,6 +986,7 @@ int init_rb( struct retr_buf *rb, struct sip_msg *msg)
 static inline void init_new_t(struct cell *new_cell, struct sip_msg *p_msg)
 {
 	struct sip_msg *shm_msg;
+	unsigned int timeout; /* avp timeout gets stored here (in s) */
 
 	shm_msg=new_cell->uas.request;
 	new_cell->from.s=shm_msg->from->name.s;
@@ -1000,6 +1004,27 @@ static inline void init_new_t(struct cell *new_cell, struct sip_msg *p_msg)
 	if (p_msg->REQ_METHOD==METHOD_INVITE) new_cell->flags |= T_IS_INVITE_FLAG;
 	new_cell->on_negative=get_on_negative();
 	new_cell->on_reply=get_on_reply();
+	new_cell->fr_timeout=(ticks_t)get_msgid_val(user_fr_timeout,
+												p_msg->id, int);
+	new_cell->fr_inv_timeout=(ticks_t)get_msgid_val(user_fr_inv_timeout,
+												p_msg->id, int);
+	if (new_cell->fr_timeout==0){
+		if (!fr_avp2timer(&timeout)) {
+			DBG("init_new_t: FR__TIMER = %d s\n", timeout);
+			new_cell->fr_timeout=S_TO_TICKS((ticks_t)timeout);
+		}else{
+			new_cell->fr_timeout=fr_timeout;
+		}
+	}
+	if (new_cell->fr_inv_timeout==0){
+		if (!fr_inv_avp2timer(&timeout)) {
+			DBG("init_new_t: FR_INV_TIMER = %d s\n", timeout);
+			new_cell->fr_inv_timeout=S_TO_TICKS((ticks_t)timeout);
+			new_cell->flags |= T_NOISY_CTIMER_FLAG;
+		}else{
+			new_cell->fr_inv_timeout=fr_inv_timeout;
+		}
+	}
 	new_cell->on_branch=get_on_branch();
 }
 
@@ -1322,3 +1347,36 @@ int t_lookup_callid(struct cell ** trans, str callid, str cseq) {
 	return -1;
 }
 
+
+
+/* params: fr_inv & fr value in ms, 0 means "do not touch"
+ * ret: 1 on success, -1 on error (script safe)*/
+int t_set_fr(struct sip_msg* msg, unsigned int fr_inv_to, unsigned int fr_to)
+{
+	struct cell *t;
+	ticks_t fr_inv, fr;
+	
+	
+	fr_inv=MS_TO_TICKS((ticks_t)fr_inv_to);
+	if ((fr_inv==0) && (fr_inv_to!=0)){
+		ERR("t_set_fr_inv: fr_inv_timeout too small (%d)\n", fr_inv_to);
+		return -1;
+	}
+	fr=MS_TO_TICKS((ticks_t)fr_to);
+	if ((fr==0) && (fr_to!=0)){
+		ERR("t_set_fr_inv: fr_timeout too small (%d)\n", fr_to);
+		return -1;
+	}
+	
+	t=get_t();
+	/* in MODE_REPLY and MODE_ONFAILURE T will be set to current transaction;
+	 * in MODE_REQUEST T will be set only if the transaction was already
+	 * created; if not -> use the static variable */
+	if (!t || t==T_UNDEFINED ){
+		set_msgid_val(user_fr_inv_timeout, msg->id, int, (int)fr_inv);
+		set_msgid_val(user_fr_timeout, msg->id, int, (int)fr);
+	}else{
+		change_fr(t, fr_inv, fr); /* change running uac timers */
+	}
+	return 1;
+}

+ 3 - 0
modules/tm/t_lookup.h

@@ -30,6 +30,7 @@
  *  2003-02-24  s/T_NULL/T_NULL_CELL/ to avoid redefinition conflict w/
  *               nameser_compat.h (andrei)
  *  2004-02-11  FIFO/CANCEL + alignments (hash=f(callid,cseq)) (uli+jiri)
+ *  2005-12-09  added t_set_fr()  (andrei)
  */
 
 
@@ -94,5 +95,7 @@ int t_lookup_ident(struct cell** trans, unsigned int hash_index, unsigned int la
 /* lookup a transaction by callid and cseq */
 int t_lookup_callid(struct cell** trans, str callid, str cseq);
 
+int t_set_fr(struct sip_msg* msg, unsigned int fr_inv_to, unsigned int fr_to );
+
 #endif
 

+ 48 - 49
modules/tm/t_reply.c

@@ -66,6 +66,7 @@
  *              the request (bogdan)
  *  2005-09-01  reverted to the old way of checking response.dst.send_sock
  *               in t_retransmit_reply & reply_light (andrei)
+ *  2005-11-09  updated to the new timers interface (andrei)
  */
 
 
@@ -940,7 +941,9 @@ void set_final_timer( /* struct s_table *h_table, */ struct cell *t )
 	if ( !is_local(t) && t->uas.request->REQ_METHOD==METHOD_INVITE ) {
 		/* crank timers for negative replies */
 		if (t->uas.status>=300) {
-			start_retr(&t->uas.response);
+			if (start_retr(&t->uas.response)!=0)
+				LOG(L_CRIT, "BUG: set_final_timer: start retr failed for %p\n",
+						&t->uas.response);
 			return;
 		}
 		/* local UAS retransmits too */
@@ -949,7 +952,9 @@ void set_final_timer( /* struct s_table *h_table, */ struct cell *t )
 			   even if TCP used, UDP could be used upstream and
 			   loose the 200, which is not retransmitted by proxies
 			*/
-			force_retr( &t->uas.response );
+			if (force_retr( &t->uas.response )!=0)
+				LOG(L_CRIT, "BUG: set_final_timer: force retr failed for %p\n",
+						&t->uas.response);
 			return;
 		}
 	}
@@ -961,9 +966,8 @@ void cleanup_uac_timers( struct cell *t )
 	int i;
 
 	/* reset FR/retransmission timers */
-	for (i=0; i<t->nr_of_outgoings; i++ )  {
-		reset_timer( &t->uac[i].request.retr_timer );
-		reset_timer( &t->uac[i].request.fr_timer );
+	for (i=0; i<t->nr_of_outgoings; i++ ){
+		stop_rb_timers(&t->uac[i].request);
 	}
 	DBG("DEBUG: cleanup_uac_timers: RETR/FR timers reset\n");
 }
@@ -1268,7 +1272,6 @@ int reply_received( struct sip_msg  *p_msg )
 	struct cell *t;
 	str next_hop;
 	avp_list_t* backup_list;
-	unsigned int timer;
 
 	/* make sure we know the associated transaction ... */
 	if (t_check( p_msg  , &branch )==-1)
@@ -1293,40 +1296,46 @@ int reply_received( struct sip_msg  *p_msg )
 		/* .. which is not e2e ? ... */
 		&& is_invite(t) ) {
 			/* ... then just stop timers */
-			reset_timer( &uac->local_cancel.retr_timer);
 			if ( msg_status >= 200 )
-				reset_timer( &uac->local_cancel.fr_timer);
+				stop_rb_timers(&uac->local_cancel); /* stop retr & fr */
+			else
+				stop_rb_retr(&uac->local_cancel);  /* stop only retr */
 			DBG("DEBUG: reply to local CANCEL processed\n");
 			goto done;
 	}
 
 
-	/* *** stop timers *** */
-	/* stop retransmission */
-	reset_timer( &uac->request.retr_timer);
-	/* stop final response timer only if I got a final response */
-	if ( msg_status >= 200 )
-		reset_timer( &uac->request.fr_timer);
+	if ( msg_status >= 200 ){
+		/* stop final response timer  & retr. only if I got a final response */
+		stop_rb_timers(&uac->request); 
 		/* acknowledge negative INVITE replies (do it before detailed
 		 * on_reply processing, which may take very long, like if it
 		 * is attempted to establish a TCP connection to a fail-over dst */
-
-	if (is_invite(t)) {
-		if (msg_status >= 300) {
-			ack = build_ack(p_msg, t, branch, &ack_len);
-			if (ack) {
-				SEND_PR_BUFFER(&uac->request, ack, ack_len);
-				shm_free(ack);
-			}
-		} else if (is_local(t) && msg_status >= 200) {
-			ack = build_local_ack(p_msg, t, branch, &ack_len, &next_hop);
-			if (ack) {
-				if (send_local_ack(p_msg, &next_hop, ack, ack_len) < 0) {
-					LOG(L_ERR, "Error while sending local ACK\n");
+		if (is_invite(t)) {
+			if (msg_status >= 300) {
+				ack = build_ack(p_msg, t, branch, &ack_len);
+				if (ack) {
+					SEND_PR_BUFFER(&uac->request, ack, ack_len);
+					shm_free(ack);
+				}
+			} else if (is_local(t) /*&& msg_status >= 200*/) {
+				ack = build_local_ack(p_msg, t, branch, &ack_len, &next_hop);
+				if (ack) {
+					if (send_local_ack(p_msg, &next_hop, ack, ack_len) < 0) {
+						LOG(L_ERR, "Error while sending local ACK\n");
+					}
+					shm_free(ack);
 				}
-				shm_free(ack);
 			}
 		}
+	}else{
+		if (is_invite(t)){
+			/* stop only retr. (and not fr) */
+			stop_rb_retr(&uac->request);
+		}else{
+			/* non-invite: increase retransmissions interval (slow now) */
+			switch_rb_retr_to_t2(&uac->request);
+		}
 	}
 	/* processing of on_reply block */
 	if (t->on_reply) {
@@ -1351,7 +1360,8 @@ int reply_received( struct sip_msg  *p_msg )
 			      */
 			cleanup_uac_timers( t );
 			if (is_invite(t)) cancel_uacs( t, cancel_bitmap );
-			     /* FR for negative INVITES, WAIT anything else */
+			/* There is no need to call set_final_timer because we know
+>--->--->--- * that the transaction is local */
 			put_on_wait(t);
 		}
 	} else {
@@ -1363,8 +1373,11 @@ int reply_received( struct sip_msg  *p_msg )
 			     */
 			cleanup_uac_timers( t );
 			if (is_invite(t)) cancel_uacs( t, cancel_bitmap );
-			     /* FR for negative INVITES, WAIT anything else */
-			     /* set_final_timer(t) */
+			/* FR for negative INVITES, WAIT anything else */
+			/* Call to set_final_timer is embedded in relay_reply to avoid
+			 * race conditions when reply is sent out and an ACK to stop
+			 * retransmissions comes before retransmission timer is set.*/
+			/* set_final_timer(t) */
 		}
 
 	}
@@ -1378,25 +1391,11 @@ int reply_received( struct sip_msg  *p_msg )
 					((msg_status>=180) || (last_uac_status==0)) )
 			) ) { /* provisional now */
 		if (is_invite(t)) {
-			/* invite: change FR to longer FR_INV, do not
-			   attempt to restart retransmission any more
-			*/
-
-			backup_list = set_avp_list(AVP_TRACK_FROM | AVP_CLASS_USER,  &t->user_avps );
-			if (!fr_inv_avp2timer(&timer)) {
-				DBG("reply_received: FR_INV_TIMER = %d\n", timer);
-				set_timer( & uac->request.fr_timer,
-					   FR_INV_TIMER_LIST, &timer );
-				t->flags |= T_NOISY_CTIMER_FLAG;
-			} else {
-				set_timer( & uac->request.fr_timer,
-					   FR_INV_TIMER_LIST, 0 );
-			}
+			backup_list = set_avp_list(AVP_TRACK_FROM | AVP_CLASS_USER, 
+										&t->user_avps );
+			restart_rb_fr(& uac->request, t->fr_inv_timeout);
+			uac->request.flags|=F_RB_FR_INV; /* mark fr_inv */
 			set_avp_list(AVP_TRACK_FROM | AVP_CLASS_USER,  backup_list );
-		} else {
-			     /* non-invite: restart retransmissions (slow now) */
-			uac->request.retr_list=RT_T2;
-			set_timer(  & uac->request.retr_timer, RT_T2, 0 );
 		}
 	} /* provisional replies */
 

+ 211 - 656
modules/tm/timer.c

@@ -98,6 +98,7 @@
  *  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)
  */
 
 #include "defs.h"
@@ -116,125 +117,108 @@
 #include "../../config.h"
 #include "../../parser/parser_f.h"
 #include "../../ut.h"
+#include "../../timer_ticks.h"
 #include "t_funcs.h"
 #include "t_reply.h"
 #include "t_cancel.h"
 
 
-static struct timer_table *timertable=0;
-static struct timer detached_timer; /* just to have a value to compare with*/
 
-#define DETACHED_LIST (&detached_timer)
+int noisy_ctimer=0;
 
-#define is_in_timer_list2(_tl) ( (_tl)->timer_list &&  \
-									((_tl)->timer_list!=DETACHED_LIST) )
+struct msgid_var user_fr_timeout;
+struct msgid_var user_fr_inv_timeout;
 
-int noisy_ctimer=0;
+/* default values of timeouts for all the timer list */
 
+ticks_t fr_timeout		=	FR_TIME_OUT;
+ticks_t fr_inv_timeout	=	INV_FR_TIME_OUT;
+ticks_t wait_timeout	=	WT_TIME_OUT;
+ticks_t delete_timeout	=	DEL_TIME_OUT;
+ticks_t rt_t1_timeout	=	RETR_T1;
+ticks_t rt_t2_timeout	=	RETR_T2;
 
-int timer_group[NR_OF_TIMER_LISTS] = 
+/* fix timer values to ticks */
+int tm_init_timers()
 {
-	TG_FR, TG_FR,
-	TG_WT,
-	TG_DEL,
-	TG_RT, TG_RT, TG_RT, TG_RT
-};
-
-/* default values of timeouts for all the timer list
-   (see timer.h for enumeration of timer lists)
-*/
-unsigned int timer_id2timeout[NR_OF_TIMER_LISTS] = {
-	FR_TIME_OUT, 		/* FR_TIMER_LIST */
-	INV_FR_TIME_OUT, 	/* FR_INV_TIMER_LIST */
-	WT_TIME_OUT, 		/* WT_TIMER_LIST */
-	DEL_TIME_OUT,		/* DELETE_LIST */
-	RETR_T1, 			/* RT_T1_TO_1 */
-	RETR_T1 << 1, 		/* RT_T1_TO_2 */
-	RETR_T1 << 2, 		/* RT_T1_TO_3 */
-	RETR_T2 			/* RT_T2 */
-						/* NR_OF_TIMER_LISTS */
-};
+	fr_timeout=MS_TO_TICKS(fr_timeout); 
+	fr_inv_timeout=MS_TO_TICKS(fr_inv_timeout);
+	wait_timeout=MS_TO_TICKS(wait_timeout);
+	delete_timeout=MS_TO_TICKS(delete_timeout);
+	rt_t1_timeout=MS_TO_TICKS(rt_t1_timeout);
+	rt_t2_timeout=MS_TO_TICKS(rt_t2_timeout);
+	/* fix 0 values to 1 tick (minimum possible wait time ) */
+	if (fr_timeout==0) fr_timeout=1;
+	if (fr_inv_timeout==0) fr_inv_timeout=1;
+	if (wait_timeout==0) wait_timeout=1;
+	if (delete_timeout==0) delete_timeout=1;
+	if (rt_t2_timeout==0) rt_t2_timeout=1;
+	if (rt_t1_timeout==0) rt_t1_timeout=1;
+	
+	memset(&user_fr_timeout, 0, sizeof(user_fr_timeout));
+	memset(&user_fr_inv_timeout, 0, sizeof(user_fr_inv_timeout));
+	
+	DBG("tm: tm_init_timers: fr=%d fr_inv=%d wait=%d delete=%d t1=%d t2=%d\n",
+			fr_timeout, fr_inv_timeout, wait_timeout, delete_timeout,
+			rt_t1_timeout, rt_t2_timeout);
+	return 0;
+}
 
 /******************** handlers ***************************/
 
 
-static void unlink_timers( struct cell *t );
 
-static void delete_cell( struct cell *p_cell, int unlock )
+inline static void cleanup_localcancel_timers( struct cell *t )
 {
+	int i;
+	for (i=0; i<t->nr_of_outgoings; i++ )
+		stop_rb_timers(&t->uac[i].local_cancel);
+}
 
-#ifdef EXTRA_DEBUG
+
+
+inline static void unlink_timers( struct cell *t )
+{
 	int i;
-#endif
 
+	stop_rb_timers(&t->uas.response);
+	for (i=0; i<t->nr_of_outgoings; i++)
+		stop_rb_timers(&t->uac[i].request);
+	cleanup_localcancel_timers(t);
+}
+
+
+
+/* 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 );
-
-#ifdef EXTRA_DEBUG
-
-	if (is_in_timer_list2(& p_cell->wait_tl )) {
-		LOG( L_ERR, "ERROR: transaction %p scheduled for deletion and"
-			" still on WAIT, timeout=%d\n", p_cell, p_cell->wait_tl.time_out);
-		abort();
-	}
-	if (is_in_timer_list2(& p_cell->uas.response.retr_timer )) {
-		LOG( L_ERR, "ERROR: transaction %p scheduled for deletion and"
-			" still on RETR (rep), timeout=%d\n",
-			p_cell, p_cell->uas.response.retr_timer.time_out);
-		abort();
-	}
-	if (is_in_timer_list2(& p_cell->uas.response.fr_timer )) {
-		LOG( L_ERR, "ERROR: transaction %p scheduled for deletion and"
-			" still on FR (rep), timeout=%d\n", p_cell,
-			p_cell->uas.response.fr_timer.time_out);
-		abort();
-	}
-	for (i=0; i<p_cell->nr_of_outgoings; i++) {
-		if (is_in_timer_list2(& p_cell->uac[i].request.retr_timer)) {
-			LOG( L_ERR, "ERROR: transaction %p scheduled for deletion and"
-				" still on RETR (req %d), timeout %d\n", p_cell, i,
-				p_cell->uac[i].request.retr_timer.time_out);
-			abort();
-		}
-		if (is_in_timer_list2(& p_cell->uac[i].request.fr_timer)) {
-			LOG( L_ERR, "ERROR: transaction %p scheduled for deletion and"
-				" still on FR (req %d), timeout %d\n", p_cell, i,
-				p_cell->uac[i].request.fr_timer.time_out);
-			abort();
-		}
-		if (is_in_timer_list2(& p_cell->uac[i].local_cancel.retr_timer)) {
-			LOG( L_ERR, "ERROR: transaction %p scheduled for deletion and"
-				" still on RETR/cancel (req %d), timeout %d\n", p_cell, i,
-				p_cell->uac[i].request.retr_timer.time_out);
-			abort();
-		}
-		if (is_in_timer_list2(& p_cell->uac[i].local_cancel.fr_timer)) {
-			LOG( L_ERR, "ERROR: transaction %p scheduled for deletion and"
-				" still on FR/cancel (req %d), timeout %d\n", p_cell, i,
-				p_cell->uac[i].request.fr_timer.time_out);
-			abort();
-		}
-	}
-	/* reset_retr_timers( hash__XX_table, p_cell ); */
-#endif
 	/* 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\n",
-			p_cell);
-		/* it's added to del list for future del */
-		set_timer( &(p_cell->dele_tl), DELETE_LIST, 0 );
+		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 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;
 	}
 }
 
+
+
 static void fake_reply(struct cell *t, int branch, int code )
 {
 	branch_bm_t cancel_bitmap;
@@ -283,92 +267,72 @@ static void fake_reply(struct cell *t, int branch, int code )
 
 
 
-
-inline static void retransmission_handler( struct timer_link *retr_tl )
+/* return (ticks_t)-1 on error/disable and 0 on success */
+inline static ticks_t retransmission_handler( struct retr_buf *r_buf )
 {
-	struct retr_buf* r_buf ;
-	enum lists id;
-
-	r_buf = get_retr_timer_payload(retr_tl);
 #ifdef EXTRA_DEBUG
-	if (r_buf->my_T->damocles) {
+	if (r_buf->my_T->flags & T_IN_AGONY) {
 		LOG( L_ERR, "ERROR: transaction %p scheduled for deletion and"
-			" called from RETR timer\n",r_buf->my_T);
+			" called from RETR timer (flags %x)\n",
+			r_buf->my_T, r_buf->my_T->flags );
 		abort();
 	}	
 #endif
-
-	/*the transaction is already removed from RETRANSMISSION_LIST by timer*/
-	/* retransmission */
 	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) {
-				reset_timer( &r_buf->fr_timer );
+				/* disable retr. timers => return -1 */
 				fake_reply(r_buf->my_T, r_buf->branch, 503 );
-				return;
+				return (ticks_t)-1;
 			}
 	} 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);
 	}
-
-	id = r_buf->retr_list;
-	r_buf->retr_list = id < RT_T2 ? id + 1 : RT_T2;
 	
-	retr_tl->timer_list= NULL; /* set to NULL so that set_timer will work */
-	set_timer( retr_tl, id < RT_T2 ? id + 1 : RT_T2, 0 );
-
-	DBG("DEBUG: retransmission_handler : done\n");
+	return 0;
 }
 
 
 
-
-inline static void final_response_handler( struct timer_link *fr_tl )
+inline static void final_response_handler(	struct retr_buf* r_buf,
+											struct cell* t)
 {
-	int silent, reply_code;
-	struct retr_buf* r_buf;
-	struct cell *t;
-
-	if (fr_tl==0){
-		/* or BUG?, ignoring it for now */
-		LOG(L_CRIT, "ERROR: final_response_handler(0) called\n");
-		return;
-	}
-	r_buf = get_fr_timer_payload(fr_tl);
-	t=r_buf->my_T;
+	int silent;
+	int reply_code;
 
 #	ifdef EXTRA_DEBUG
-	if (t->damocles) 
+	if (t->flags & T_IN_AGONY) 
 	{
 		LOG( L_ERR, "ERROR: transaction %p scheduled for deletion and"
-			" called from FR timer\n",r_buf->my_T);
+			" called from FR timer (flags %x)\n", t, t->flags);
 		abort();
 	}
 #	endif
-
-	reset_timer(  &(r_buf->retr_timer) );
-
-	/* the transaction is already removed from FR_LIST by the timer */
-
 	/* 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_ERR, "ERROR: final_response_handler: unknown type reply buffer\n");
+			LOG(L_CRIT, "BUG: final_response_handler: unknown type reply"
+					" buffer\n");
 			abort();
 		}
 #		endif
@@ -402,15 +366,17 @@ inline static void final_response_handler( struct timer_link *fr_tl )
 		&& has_noisy_ctimer(t) == 0;
 	if (silent) {
 		UNLOCK_REPLIES(t);
+#ifdef EXTRA_DEBUG
 		DBG("DEBUG: final_response_handler: transaction silently dropped (%p)\n",t);
+#endif
 		put_on_wait( t );
 		return;
 	}
-
+#ifdef EXTRA_DEBUG
 	DBG("DEBUG: final_response_handler:stop retr. and send CANCEL (%p)\n", t);
-
+#endif
 	if (is_invite(t) && 
-	    r_buf->branch < MAX_BRANCHES && r_buf->branch >= 0 &&
+	    r_buf->branch < MAX_BRANCHES && /* r_buf->branch is always >=0 */
 	    t->uac[r_buf->branch].last_received > 0) {
 		reply_code = 480; /* Request Terminated */
 	} else {
@@ -418,554 +384,143 @@ inline static void final_response_handler( struct timer_link *fr_tl )
 	}
 
 	fake_reply(t, r_buf->branch, reply_code );
-
-	DBG("DEBUG: final_response_handler : done\n");
-}
-
-
-
-void cleanup_localcancel_timers( struct cell *t )
-{
-	int i;
-	for (i=0; i<t->nr_of_outgoings; i++ )  {
-		reset_timer(  &t->uac[i].local_cancel.retr_timer );
-		reset_timer(  &t->uac[i].local_cancel.fr_timer );
-	}
-}
-
-
-inline static void wait_handler( struct timer_link *wait_tl )
-{
-	struct cell *p_cell;
-
-	p_cell = get_wait_timer_payload( wait_tl );
-#ifdef EXTRA_DEBUG
-	if (p_cell->damocles) {
-		LOG( L_ERR, "ERROR: transaction %p scheduled for deletion and"
-			" called from WAIT timer\n",p_cell);
-		abort();
-	}	
-	DBG("DEBUG: WAIT timer hit\n");
-#endif
-
-	/* stop cancel timers if any running */
-	if ( is_invite(p_cell) ) cleanup_localcancel_timers( p_cell );
-
-	/* the transaction is already removed from WT_LIST by the timer */
-	/* remove the cell from the hash table */
-	DBG("DEBUG: wait_handler : removing %p from table \n", p_cell );
-	LOCK_HASH( p_cell->hash_index );
-	remove_from_hash_table_unsafe(  p_cell );
-	/* jku: no more here -- we do it when we put a transaction on wait */
-#ifdef EXTRA_DEBUG
-	p_cell->damocles = 1;
-#endif
-	/* delete (returns with UNLOCK-ed_HASH) */
-	delete_cell( p_cell, 1 /* unlock on return */ );
-	DBG("DEBUG: wait_handler : done\n");
-}
-
-
-
-inline static void delete_handler( struct timer_link *dele_tl )
-{
-	struct cell *p_cell;
-
-	p_cell = get_dele_timer_payload( dele_tl );
-	DBG("DEBUG: delete_handler : removing %p \n", p_cell );
-#ifdef EXTRA_DEBUG
-	if (p_cell->damocles==0) {
-		LOG( L_ERR, "ERROR: transaction %p not scheduled for deletion"
-			" and called from DELETE timer\n",p_cell);
-		abort();
-	}	
-#endif
-
-	/* 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
-	*/
-	delete_cell( p_cell, 0 /* don't unlock on return */ );
-    DBG("DEBUG: delete_handler : done\n");
 }
 
 
-/***********************************************************/
 
-struct timer_table *get_timertable()
+/* 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)
 {
-	return timertable;
-}
-
+	struct retr_buf* rbuf ;
+	ticks_t fr_remainder;
+	ticks_t retr_remainder;
+	ticks_t retr_interval;
+	struct cell *t;
 
-void unlink_timer_lists()
-{
-	struct timer_link  *tl, *end, *tmp;
-	enum lists i;
-
-	if (timertable==0) return; /* nothing to do */
-	/* remember the DELETE LIST */
-	tl = timertable->timers[DELETE_LIST].first_tl.next_tl;
-	end = & timertable->timers[DELETE_LIST].last_tl;
-	/* unlink the timer lists */
-	for( i=0; i<NR_OF_TIMER_LISTS ; i++ )
-		reset_timer_list( i );
-	DBG("DEBUG: unlink_timer_lists : emptying DELETE list\n");
-	/* deletes all cells from DELETE_LIST list 
-	   (they are no more accessible from entrys) */
-	while (tl!=end) {
-		tmp=tl->next_tl;
-		free_cell( get_dele_timer_payload(tl) );
-		tl=tmp;
-	}
+	rbuf=(struct  retr_buf*)
+			((void*)tl-(void*)(&((struct retr_buf*)0)->timer));
+	t=rbuf->my_T;
 	
-}
-
-struct timer_table *tm_init_timers()
-{
-	enum lists i;
-
-	timertable=(struct timer_table *) shm_malloc(sizeof(struct timer_table));
-	if (!timertable) {
-		LOG(L_ERR, "ERROR: tm_init_timers: no shmem for timer_Table\n");
-		goto error0;
-	}
-	memset(timertable, 0, sizeof (struct timer_table));
-		
-
-	/* inits the timers*/
-	for(  i=0 ; i<NR_OF_TIMER_LISTS ; i++ )
-        init_timer_list( i );
-    
-    /* init. timer lists */
-	timertable->timers[RT_T1_TO_1].id = RT_T1_TO_1;
-	timertable->timers[RT_T1_TO_2].id = RT_T1_TO_2;
-	timertable->timers[RT_T1_TO_3].id = RT_T1_TO_3;
-	timertable->timers[RT_T2].id      = RT_T2;
-	timertable->timers[FR_TIMER_LIST].id     = FR_TIMER_LIST; 
-	timertable->timers[FR_INV_TIMER_LIST].id = FR_INV_TIMER_LIST;
-	timertable->timers[WT_TIMER_LIST].id     = WT_TIMER_LIST;
-	timertable->timers[DELETE_LIST].id       = DELETE_LIST;
-
-	return timertable;
-
-error0:
-	return 0;
-}
-
-void free_timer_table()
-{
-	enum lists i;
-
-	if (timertable) {
-		/* the mutexs for sync the lists are released*/
-		for ( i=0 ; i<NR_OF_TIMER_LISTS ; i++ )
-			release_timerlist_lock( &timertable->timers[i] );
-		shm_free(timertable);
-	}
-		
-}
-
-void reset_timer_list( enum lists list_id)
-{
-	timertable->timers[list_id].first_tl.next_tl =
-		&(timertable->timers[list_id].last_tl );
-	timertable->timers[list_id].last_tl.prev_tl =
-		&(timertable->timers[list_id].first_tl );
-	timertable->timers[list_id].first_tl.prev_tl =
-		timertable->timers[list_id].last_tl.next_tl = NULL;
-	timertable->timers[list_id].last_tl.time_out = -1;
-}
-
-
-
-
-void init_timer_list( /* struct s_table* ht, */ enum lists list_id)
-{
-	reset_timer_list( /* ht, */ list_id );
-	init_timerlist_lock( /* ht, */ list_id );
-}
-
-
-
-
-void print_timer_list( enum lists list_id)
-{
-	struct timer* timer_list=&(timertable->timers[ list_id ]);
-	struct timer_link *tl ;
-
-	tl = timer_list->first_tl.next_tl;
-	while (tl!=& timer_list->last_tl)
-	{
-		DBG("DEBUG: print_timer_list[%d]: %p, next=%p \n",
-			list_id, tl, tl->next_tl);
-		tl = tl->next_tl;
-	}
-}
-
-
-
-static void remove_timer_unsafe(  struct timer_link* tl )
-{
-#ifdef EXTRA_DEBUG
-	if (tl && is_in_timer_list2(tl) &&
-		tl->timer_list->last_tl.prev_tl==0) {
-		LOG( L_CRIT,
-		"CRITICAL : Oh no, zero link in trailing timer element\n");
-		abort();
-	};
-#endif
-	if (is_in_timer_list2( tl )) {
-#ifdef EXTRA_DEBUG
-		DBG("DEBUG: unlinking timer: tl=%p, timeout=%d, group=%d\n", 
-			tl, tl->time_out, tl->tg);
-#endif
-		tl->prev_tl->next_tl = tl->next_tl;
-		tl->next_tl->prev_tl = tl->prev_tl;
-		tl->next_tl = 0;
-		tl->prev_tl = 0;
-		tl->timer_list = NULL;
-	}
-}
-
-
-/* put a new cell into a list nr. list_id */
-static void insert_timer_unsafe( struct timer *timer_list, struct timer_link *tl,
-	unsigned int time_out )
-{
-	struct timer_link* ptr;
-
-	tl->time_out = time_out;
-	tl->timer_list = timer_list;
-
-	for(ptr = timer_list->last_tl.prev_tl; 
-	    ptr != &timer_list->first_tl; 
-	    ptr = ptr->prev_tl) {
-		if ((ptr->time_out != TIMER_DELETED) && (ptr->time_out <= time_out)) break;
-	}
-
-	tl->prev_tl = ptr;
-	tl->next_tl = ptr->next_tl;
-	tl->prev_tl->next_tl = tl;
-	tl->next_tl->prev_tl = tl;
-
-	DBG("DEBUG: add_to_tail_of_timer[%d]: %p\n",timer_list->id,tl);
-}
-
-
-
-#if 0  /* not used anymore */
-/* put a new cell into a list nr. list_id */
-static void add_timer_unsafe( struct timer *timer_list, struct timer_link *tl,
-	unsigned int time_out )
-{
-#ifdef EXTRA_DEBUG
-	if (timer_list->last_tl.prev_tl==0) {
-	LOG( L_CRIT,
-		"CRITICAL : Oh no, zero link in trailing timer element\n");
-		abort();
-	};
+#ifdef TIMER_DEBUG
+	DBG("tm: timer retr_buf_handler @%d (%p -> %p -> %p)\n",
+			ticks, tl, rbuf, t);
 #endif
-
-	tl->time_out = time_out;
-	tl->prev_tl = timer_list->last_tl.prev_tl;
-	tl->next_tl = & timer_list->last_tl;
-	timer_list->last_tl.prev_tl = tl;
-	tl->prev_tl->next_tl = tl;
-	tl->timer_list = timer_list;
-#ifdef EXTRA_DEBUG
-	if ( tl->tg != timer_group[ timer_list->id ] ) {
-		LOG( L_CRIT, "CRITICAL error: changing timer group\n");
-		abort();
-	}
+	/* overflow safe check (should work ok for fr_intervals < max ticks_t/2) */
+	if ((s_ticks_t)(rbuf->fr_expire-ticks)<=0){
+		/* final response */
+		final_response_handler(rbuf, t);
+		rbuf->t_active=0; /* mark the timer as removed 
+							 (both timers disabled)
+							  a little race risk, but
+							  nothing bad would happen */
+		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) */
+				/* 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<<1)>rt_t2_timeout))
+					retr_interval=rt_t2_timeout;
+				else
+					retr_interval=(ticks_t)(unsigned long)p<<1;
+#ifdef TIMER_DEBUG
+				DBG("tm: timer: retr: new interval %d (max %d)\n", 
+						retr_interval, rt_t2_timeout);
 #endif
-	DBG("DEBUG: add_timer_unsafe[%d]: %p\n",timer_list->id,tl);
-}
-#endif
-
-
-
-/* detach items passed by the time from timer list */
-static struct timer_link  *check_and_split_time_list( struct timer *timer_list,
-	int time )
-{
-	struct timer_link *tl , *end, *ret;
-
-
-	/* quick check whether it is worth entering the lock */
-	if (timer_list->first_tl.next_tl==&timer_list->last_tl 
-			|| ( /* timer_list->first_tl.next_tl
-				&& */ timer_list->first_tl.next_tl->time_out > time) )
-		return NULL;
-
-	/* the entire timer list is locked now -- noone else can manipulate it */
-	lock(timer_list->mutex);
-
-	end = &timer_list->last_tl;
-	tl = timer_list->first_tl.next_tl;
-	while( tl!=end && tl->time_out <= time) {
-		tl->timer_list = DETACHED_LIST;
-		tl=tl->next_tl;
-	}
-
-	/* nothing to delete found */
-	if (tl->prev_tl==&(timer_list->first_tl)) {
-		ret = NULL;
-	} else { /* we did find timers to be fired! */
-		/* the detached list begins with current beginning */
-		ret = timer_list->first_tl.next_tl;
-		/* and we mark the end of the split list */
-		tl->prev_tl->next_tl = NULL;
-		/* the shortened list starts from where we suspended */
-		timer_list->first_tl.next_tl = tl;	
-		tl->prev_tl = & timer_list->first_tl;
+				/* 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;
+				retr_remainder=retr_interval;
+				/* store the crt. retr. interval inside the timer struct,
+				 * in the data member */
+				tl->data=(void*)(unsigned long)retr_interval;
+			}else{
+				retr_remainder= rbuf->retr_expire-ticks;
+				DBG("tm: timer: retr: nothing to do, expire in %d\n", 
+						retr_remainder);
+			}
 	}
-#ifdef EXTRA_DEBUG
-	if (timer_list->last_tl.prev_tl==0) {
-		LOG( L_CRIT,
-		"CRITICAL : Oh no, zero link in trailing timer element\n");
-		abort();
-	};
-#endif
-	/* give the list lock away */
-	unlock(timer_list->mutex);
-
-	return ret;
-}
-
-
-
-/* stop timer
- * WARNING: a reset'ed timer will be lost forever
- *  (successive set_timer won't work unless you're lucky
- *   an catch the race condition, the idea here is there is no
- *   guarantee you can do anything after a timer_reset)*/
-void reset_timer( struct timer_link* tl )
-{
-	/* disqualify this timer from execution by setting its time_out
-	   to zero; it will stay in timer-list until the timer process
-	   starts removing outdated elements; then it will remove it
-	   but not execute; there is a race condition, though -- see
-	   timer.c for more details
-	*/
-	tl->time_out = TIMER_DELETED;
-#ifdef EXTRA_DEBUG
-	DBG("DEBUG: reset_timer (group %d, tl=%p)\n", tl->tg, tl );
+/* 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
+	if (retr_remainder<fr_remainder)
+		return retr_remainder;
+	else
+		return fr_remainder;
+disabled:
+	return rbuf->fr_expire-ticks;
 }
 
 
 
-
-/* determine timer length and put on a correct timer list
- * WARNING: - don't try to use it to "move" a timer from one list
- *            to another, you'll run into races
- *          - reset_timer; set_timer might not work, a reset'ed timer
- *             has no set_timer guarantee, it might be lost;
- *             same for an expired timer: only it's handler can
- *             set it again, an external set_timer has no guarantee
- */
-void set_timer( struct timer_link *new_tl, enum lists list_id, unsigned int* ext_timeout )
+ticks_t wait_handler(ticks_t ti, struct timer_ln *wait_tl, void* data)
 {
-	unsigned int timeout;
-	struct timer* list;
-
-
-	if (list_id<FR_TIMER_LIST || list_id>=NR_OF_TIMER_LISTS) {
-		LOG(L_CRIT, "ERROR: set_timer: unknown list: %d\n", list_id);
-#ifdef EXTRA_DEBUG
-		abort();
-#endif
-		return;
-	}
-
-	if (!ext_timeout) {
-		timeout = timer_id2timeout[ list_id ];
-	} else {
-		timeout = *ext_timeout;
-	}
-
-	list= &(timertable->timers[ list_id ]);
-
-	lock(list->mutex);
-	/* check first if we are on the "detached" timer_routine list,
-	 * if so do nothing, the timer is not valid anymore
-	 * (sideffect: reset_timer ; set_timer is not safe, a reseted timer
-	 *  might be lost, depending on this race condition ) */
-	if (new_tl->timer_list==DETACHED_LIST){
-		LOG(L_CRIT, "WARNING: set_timer called on a \"detached\" timer"
-				" -- ignoring: %p\n", new_tl);
-		goto end;
-	}
-	/* make sure I'm not already on a list */
-	remove_timer_unsafe( new_tl );
-	     /*
-	       add_timer_unsafe( list, new_tl, get_ticks()+timeout);
-	     */
-	insert_timer_unsafe( list, new_tl, get_ticks()+timeout);
-end:
-	unlock(list->mutex);
-}
-
-/* similar to set_timer, except it allows only one-time
-   timer setting and all later attempts are ignored */
-void set_1timer( struct timer_link *new_tl, enum lists list_id, unsigned int* ext_timeout )
-{
-	unsigned int timeout;
-	struct timer* list;
-
+	struct cell *p_cell;
+	ticks_t ret;
 
-	if (list_id<FR_TIMER_LIST || list_id>=NR_OF_TIMER_LISTS) {
-		LOG(L_CRIT, "ERROR: set_timer: unknown list: %d\n", list_id);
-#ifdef EXTRA_DEBUG
-		abort();
+	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
-		return;
-	}
-
-	if (!ext_timeout) {
-		timeout = timer_id2timeout[ list_id ];
-	} else {
-		timeout = *ext_timeout;
-	}
-
-	list= &(timertable->timers[ list_id ]);
-
-	lock(list->mutex);
-	if (!(new_tl->time_out>TIMER_DELETED)) {
-		/* make sure I'm not already on a list */
-		/* remove_timer_unsafe( new_tl ); */
-		/*
-		add_timer_unsafe( list, new_tl, get_ticks()+timeout);
-		*/
-		insert_timer_unsafe( list, new_tl, get_ticks()+timeout);
-
-		/* set_1timer is used only by WAIT -- that's why we can
-		   afford updating wait statistics; I admit its not nice
-		   but it greatly utilizes existing lock 
-		*/
-	}
-	unlock(list->mutex);
-	t_stats_wait();
-}
-
-
-
-/* should be called only from timer process context,
- * else it's unsafe */
-static void unlink_timers( struct cell *t )
-{
-	int i;
-	int remove_fr, remove_retr;
-
-	remove_fr=0; remove_retr=0;
-
-	/* first look if we need to remove timers and play with
-	   costly locks at all
 
-	    note that is_in_timer_list2 is unsafe but it does not
-	    hurt -- transaction is already dead (wait state) so that
-	    noone else will install a FR/RETR timer and it can only
-	    be removed from timer process itself -> it is safe to
-	    use it without any protection
-	*/
-	if (is_in_timer_list2(&t->uas.response.fr_timer)) remove_fr=1; 
-	else for (i=0; i<t->nr_of_outgoings; i++)
-		if (is_in_timer_list2(&t->uac[i].request.fr_timer)
-			|| is_in_timer_list2(&t->uac[i].local_cancel.fr_timer)) {
-				remove_fr=1;
-				break;
-		}
-	if (is_in_timer_list2(&t->uas.response.retr_timer)) remove_retr=1; 
-	else for (i=0; i<t->nr_of_outgoings; i++)
-		if (is_in_timer_list2(&t->uac[i].request.retr_timer)
-			|| is_in_timer_list2(&t->uac[i].local_cancel.retr_timer)) {
-				remove_retr=1;
-				break;
-		}
-
-	/* do what we have to do....*/
-	if (remove_retr) {
-		/* RT_T1 lock is shared by all other RT timer
-		   lists -- we can safely lock just one
+	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
 		*/
-		lock(timertable->timers[RT_T1_TO_1].mutex);
-		remove_timer_unsafe(&t->uas.response.retr_timer);
-		for (i=0; i<t->nr_of_outgoings; i++) {
-			remove_timer_unsafe(&t->uac[i].request.retr_timer);
-			remove_timer_unsafe(&t->uac[i].local_cancel.retr_timer);
-		}
-		unlock(timertable->timers[RT_T1_TO_1].mutex);
-	}
-	if (remove_fr) {
-		/* FR lock is shared by all other FR timer
-		   lists -- we can safely lock just one
-		*/
-		lock(timertable->timers[FR_TIMER_LIST].mutex);
-		remove_timer_unsafe(&t->uas.response.fr_timer);
-		for (i=0; i<t->nr_of_outgoings; i++) {
-			remove_timer_unsafe(&t->uac[i].request.fr_timer);
-			remove_timer_unsafe(&t->uac[i].local_cancel.fr_timer);
-		}
-		unlock(timertable->timers[FR_TIMER_LIST].mutex);
-	}
-}
-
-
-
-
-#define run_handler_for_each( _tl , _handler ) \
-	while ((_tl))\
-	{\
-		/* reset the timer list linkage */\
-		tmp_tl = (_tl)->next_tl;\
-		(_tl)->next_tl = (_tl)->prev_tl = 0;\
-		DBG("DEBUG: timer routine:%d,tl=%p next=%p\n",\
-			id,(_tl),tmp_tl);\
-		if ((_tl)->time_out>TIMER_DELETED) \
-			(_handler)( _tl );\
-		(_tl) = tmp_tl;\
-	}
-
-
-
-
-void timer_routine(unsigned int ticks , void * attr)
-{
-	/* struct timer_table *tt= (struct timer_table*)attr; */
-	struct timer_link *tl, *tmp_tl;
-	int                id;
-
-	for( id=0 ; id<NR_OF_TIMER_LISTS ; id++ )
-	{
-		/* to waste as little time in lock as possible, detach list
-		   with expired items and process them after leaving the lock */
-		tl=check_and_split_time_list( &timertable->timers[ id ], ticks);
-		/* process items now */
-		switch (id)
-		{
-			case FR_TIMER_LIST:
-			case FR_INV_TIMER_LIST:
-				run_handler_for_each(tl,final_response_handler);
-				break;
-			case RT_T1_TO_1:
-			case RT_T1_TO_2:
-			case RT_T1_TO_3:
-			case RT_T2:
-				run_handler_for_each(tl,retransmission_handler);
-				break;
-			case WT_TIMER_LIST:
-				run_handler_for_each(tl,wait_handler);
-				break;
-			case DELETE_LIST:
-				run_handler_for_each(tl,delete_handler);
-				break;
-		}
+		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 */ );
 	}
+	return ret;
 }
 

+ 107 - 65
modules/tm/timer.h

@@ -29,95 +29,137 @@
  * --------
  *  2003-09-12  timer_link.tg exists only if EXTRA_DEBUG (andrei)
  *  2004-02-13  timer_link.payload removed (bogdan)
+ *  2005-11-03  rewritten to use the new timers (andrei)
  */
 
 
-#ifndef _TIMER_H
-#define _TIMER_H
+#ifndef _TM_TIMER_H
+#define _TM_TIMER_H
 
 #include "defs.h"
 
 #include "lock.h"
 
-/* timer timestamp value indicating a timer has been 
-   deactivated and shall not be executed
-*/
-#define TIMER_DELETED	1
+#include "../../timer.h"
+#include "h_table.h"
 
+extern struct msgid_var user_fr_timeout;
+extern struct msgid_var user_fr_inv_timeout;
 
+extern ticks_t fr_timeout;
+extern ticks_t fr_inv_timeout;
+extern ticks_t wait_timeout;
+extern ticks_t delete_timeout;
+extern ticks_t rt_t1_timeout;
+extern ticks_t rt_t2_timeout;
 
-/* identifiers of timer lists;*/
-/* fixed-timer retransmission lists (benefit: fixed timer$
-   length allows for appending new items to the list as$
-   opposed to inserting them which is costly */
-enum lists
-{
-	FR_TIMER_LIST, FR_INV_TIMER_LIST,
-	WT_TIMER_LIST,
-	DELETE_LIST,
-	RT_T1_TO_1, RT_T1_TO_2, RT_T1_TO_3, RT_T2,
-	NR_OF_TIMER_LISTS
-};
-
-/* all you need to put a cell in a timer list
-   links to neighbors and timer value */
-typedef struct timer_link
-{
-	struct timer_link     *next_tl;
-	struct timer_link     *prev_tl;
-	volatile unsigned int  time_out;
-	struct timer          *timer_list;
-#ifdef EXTRA_DEBUG
-	enum timer_groups  tg;
-#endif
-}timer_link_type ;
+extern int tm_init_timers();
 
+ticks_t wait_handler(ticks_t t, struct timer_ln *tl, void* data);
+ticks_t retr_buf_handler(ticks_t t, struct timer_ln *tl, void* data);
 
-/* timer list: includes head, tail and protection semaphore */
-typedef struct  timer
-{
-	struct timer_link  first_tl;
-	struct timer_link  last_tl;
-	ser_lock_t*        mutex;
-	enum lists         id;
-} timer_type;
-
-/* transaction table */
-struct timer_table
+#define init_cell_timers(c) \
+	timer_init(&(c)->wait_timer, wait_handler, (c), 0) /* slow? */
+
+#define init_rb_timers(rb) \
+	timer_init(&(rb)->timer, retr_buf_handler, \
+				(void*)(unsigned long)rt_t1_timeout, 0)
+
+/* set fr & retr timer
+ * rb  -  pointer to struct retr_buf
+ * retr - initial retr. in ticks (use (ticks_t)(-1) to disable)
+ * returns: -1 on error, 0 on success
+ */
+#ifdef TIMER_DEBUG
+inline static int _set_fr_retr(struct retr_buf* rb, ticks_t retr,
+								const char* file, const char* func,
+								unsigned line)
+#else
+inline static int _set_fr_retr(struct retr_buf* rb, ticks_t retr)
+#endif
 {
-    /* table of timer lists */
-    struct timer   timers[ NR_OF_TIMER_LISTS ];
-};
+	ticks_t timeout;
+	ticks_t ticks;
+	int ret;
+	
+	ticks=get_ticks_raw();
+	timeout=rb->my_T->fr_timeout;
+	rb->timer.data=(void*)(unsigned long)retr; /* hack */
+	rb->retr_expire=ticks+retr;
+	if (rb->t_active){
+		/* we could have set_fr_retr called in the same time (acceptable 
+		 * race), we rely on timer_add adding it only once */
+#ifdef TIMER_DEBUG
+		LOG(L_WARN, "WARNING: _set_fr_timer called from: %s(%s):%d\n", 
+						file, func, line);
+#endif
+		LOG(L_CRIT, "WARNING: -_set_fr_timer- already added: %p , tl=%p!!!\n",
+					rb, &rb->timer);
+	}
+	/* set active & if retr==-1 set disabled */
+	rb->flags|= (F_RB_RETR_DISABLED & -(retr==-1)); 
+	rb->fr_expire=ticks+timeout;
+#ifdef TIMER_DEBUG
+	ret=timer_add_safe(&(rb)->timer, (timeout<retr)?timeout:retr,
+							file, func, line);
+#else
+	ret=timer_add(&(rb)->timer, (timeout<retr)?timeout:retr);
+#endif
+	if (ret==0) rb->t_active=1;
+	return ret;
+}
 
 
 
+/* stop the timers assoc. with a retr. buf. */
+#define stop_rb_timers(rb) \
+do{ \
+	if ((rb)->t_active){ \
+		(rb)->t_active=0; \
+		timer_del(&(rb)->timer); \
+	}\
+}while(0)
 
+/* one shot, once disabled it cannot be re-enabled */
+#define stop_rb_retr(rb) \
+	((rb)->flags|=F_RB_RETR_DISABLED)
 
-extern int timer_group[NR_OF_TIMER_LISTS];
-extern unsigned int timer_id2timeout[NR_OF_TIMER_LISTS];
+/* reset retr. interval to t2 and restart retr. timer */
+#define switch_rb_retr_to_t2(rb) \
+	do{ \
+		(rb)->retr_expire=get_ticks_raw()+rt_t2_timeout; \
+		(rb)->flags|=F_RB_T2; \
+	}while(0)
 
 
+/* restart fr */
+#define restart_rb_fr(rb, new_val) \
+	((rb)->fr_expire=get_ticks_raw()+(new_val))
 
-struct timer_table * tm_init_timers();
-void unlink_timer_lists();
-void free_timer_table();
-void init_timer_list( enum lists list_id);
-void reset_timer_list( enum lists list_id);
-/*void remove_timer_unsafe(  struct timer_link* tl ) ;
-void add_timer_unsafe( struct timer*, struct timer_link*, unsigned int);
-struct timer_link  *check_and_split_time_list( struct timer*, int);
-*/
 
-void reset_timer( struct timer_link* tl );
-/* determine timer length and put on a correct timer list */
-void set_timer( struct timer_link *new_tl, enum lists list_id, unsigned int* ext_timeout );
-/* similar to set_timer, except it allows only one-time
-   timer setting and all later attempts are ignored */
-void set_1timer( struct timer_link *new_tl, enum lists list_id, unsigned int* ext_timeout );
-/*void unlink_timers( struct cell *t );*/
-void timer_routine(unsigned int, void*);
 
+/* change default & uac fr timers on-the-fly (if they are still running)
+ *  if timer value==0 => leave it unchanged
+ */
+inline static void change_fr(struct cell* t, ticks_t fr_inv, ticks_t fr)
+{
+	int i;
+	ticks_t fr_inv_expire, fr_expire;
+	
+	fr_expire=get_ticks_raw();
+	fr_inv_expire=fr_expire+fr_inv;
+	fr_expire+=fr;
+	if (fr_inv) t->fr_inv_timeout=fr_inv;
+	if (fr) t->fr_timeout=fr;
+	for (i=0; i<t->nr_of_outgoings; i++){
+		if (t->uac[i].request.t_active){ 
+				if ((t->uac[i].request.flags & F_RB_FR_INV) && fr_inv)
+					t->uac[i].request.fr_expire=fr_inv_expire;
+				else if (fr)
+					t->uac[i].request.fr_expire=fr_expire;
+		}
+	}
+}
 
-struct timer_table *get_timertable();
 
 #endif

+ 31 - 13
modules/tm/tm.c

@@ -72,8 +72,10 @@
  *  2004-02-11  FIFO/CANCEL + alignments (hash=f(callid,cseq)) (uli+jiri)
  *  2004-02-18  t_reply exported via FIFO - imported from VM (bogdan)
  *  2004-10-01  added a new param.: restart_fr_on_each_reply (andrei)
+ *  2005-11-14  new timer support, changed timer related module params (andrei)
  *  2005-12-09  fixup_hostport2proxy uses route_struct to access param #1
  *              when fixing param #2
+ *  2005-12-09  added t_set_fr() (andrei)
  */
 
 
@@ -110,6 +112,7 @@
 #include "callid.h"
 #include "t_cancel.h"
 #include "t_fifo.h"
+#include "timer.h"
 
 MODULE_VERSION
 
@@ -169,6 +172,8 @@ inline static int w_t_on_negative(struct sip_msg* msg, char *go_to, char *foo);
 inline static int w_t_on_branch(struct sip_msg* msg, char *go_to, char *foo);
 inline static int w_t_on_reply(struct sip_msg* msg, char *go_to, char *foo );
 inline static int t_check_status(struct sip_msg* msg, char *regexp, char *foo);
+static int t_set_fr_inv(struct sip_msg* msg, char* fr_inv, char* foo);
+static int t_set_fr_all(struct sip_msg* msg, char* fr_inv, char* fr);
 
 
 static char *fr_timer_param = FR_TIMER_AVP;
@@ -236,6 +241,10 @@ static cmd_export_t cmds[]={
 			REQUEST_ROUTE | FAILURE_ROUTE },
 	{"t_write_unix",      t_write_unix,             2, fixup_t_write,
 	                REQUEST_ROUTE | FAILURE_ROUTE },
+	{"t_set_fr",          t_set_fr_inv,             1, fixup_int_1,
+			REQUEST_ROUTE|ONREPLY_ROUTE|FAILURE_ROUTE|BRANCH_ROUTE },
+	{"t_set_fr",          t_set_fr_all,             2, fixup_int_12,
+			REQUEST_ROUTE|ONREPLY_ROUTE|FAILURE_ROUTE|BRANCH_ROUTE },
 
 	/* not applicable from the script */
 	{"register_tmcb",      (cmd_function)register_tmcb,     NO_SCRIPT,   0, 0},
@@ -263,14 +272,12 @@ static cmd_export_t cmds[]={
 static param_export_t params[]={
 	{"ruri_matching",       INT_PARAM, &ruri_matching                        },
 	{"via1_matching",       INT_PARAM, &via1_matching                        },
-	{"fr_timer",            INT_PARAM, &(timer_id2timeout[FR_TIMER_LIST])    },
-	{"fr_inv_timer",        INT_PARAM, &(timer_id2timeout[FR_INV_TIMER_LIST])},
-	{"wt_timer",            INT_PARAM, &(timer_id2timeout[WT_TIMER_LIST])    },
-	{"delete_timer",        INT_PARAM, &(timer_id2timeout[DELETE_LIST])      },
-	{"retr_timer1p1",       INT_PARAM, &(timer_id2timeout[RT_T1_TO_1])       },
-	{"retr_timer1p2",       INT_PARAM, &(timer_id2timeout[RT_T1_TO_2])       },
-	{"retr_timer1p3",       INT_PARAM, &(timer_id2timeout[RT_T1_TO_3])       },
-	{"retr_timer2",         INT_PARAM, &(timer_id2timeout[RT_T2])            },
+	{"fr_timer",            INT_PARAM, &fr_timeout                           },
+	{"fr_inv_timer",        INT_PARAM, &fr_inv_timeout                       },
+	{"wt_timer",            INT_PARAM, &wait_timeout                         },
+	{"delete_timer",        INT_PARAM, &delete_timeout                       },
+	{"retr_timer1",         INT_PARAM, &rt_t1_timeout                        },
+	{"retr_timer2"  ,       INT_PARAM, &rt_t2_timeout                        },
 	{"noisy_ctimer",        INT_PARAM, &noisy_ctimer                         },
 	{"uac_from",            STR_PARAM, &uac_from                             },
 	{"unix_tx_timeout",     INT_PARAM, &tm_unix_tx_timeout                   },
@@ -371,7 +378,7 @@ static int script_init( struct sip_msg *foo, void *bar)
 
 static int mod_init(void)
 {
-	DBG( "TM - (size of cell=%ld, sip_msg=%ld) initializing...\n", 
+	DBG( "TM - (sizeof cell=%ld, sip_msg=%ld) initializing...\n", 
 			(long)sizeof(struct cell), (long)sizeof(struct sip_msg));
 	/* checking if we have sufficient bitmap capacity for given
 	   maximum number of  branches */
@@ -435,13 +442,10 @@ static int mod_init(void)
 	/* init static hidden values */
 	init_t();
 
-	if (!tm_init_timers()) {
+	if (tm_init_timers()==-1) {
 		LOG(L_ERR, "ERROR: mod_init: timer init failed\n");
 		return -1;
 	}
-	/* register the timer function */
-	register_timer( timer_routine , 0 /* empty attr */, 1 );
-
 	/* init_tm_stats calls process_count, which should
 	 * NOT be called from mod_init, because one does not
 	 * now, if a timer is used and thus how many processes
@@ -833,3 +837,17 @@ inline static int w_t_relay( struct sip_msg  *p_msg ,
 	LOG(L_CRIT, "ERROR: w_t_relay_to: unsupported mode: %d\n", rmode);
 	return 0;
 }
+
+
+/* set fr_inv_timeout & or fr_timeout; 0 means: use the default value */
+static int t_set_fr_all(struct sip_msg* msg, char* fr_inv, char* fr)
+{
+	
+	return t_set_fr(msg, (unsigned int)(long)fr_inv, (unsigned int)(long)fr);
+}
+
+static int t_set_fr_inv(struct sip_msg* msg, char* fr_inv, char* foo)
+{
+	return t_set_fr_all(msg, fr_inv, (char*)0);
+}
+

+ 2 - 1
modules/tm/uac.c

@@ -254,7 +254,8 @@ int t_uac(str* method, str* headers, str* body, dlg_t* dialog,
 			dialog->hooks.next_hop->s);
 	}
 	
-	start_retr(request);
+	if (start_retr(request)!=0)
+		LOG(L_CRIT, "BUG: t_uac: failed to start retr. for %p\n", request);
 	return 1;
 
  error1: