瀏覽代碼

- 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
 VERSION = 0
 PATCHLEVEL = 10
 PATCHLEVEL = 10
 SUBLEVEL =   99
 SUBLEVEL =   99
-EXTRAVERSION = -dev25-timers
+EXTRAVERSION = -dev26-tm-timers
 
 
 RELEASE=$(VERSION).$(PATCHLEVEL).$(SUBLEVEL)$(EXTRAVERSION)
 RELEASE=$(VERSION).$(PATCHLEVEL).$(SUBLEVEL)$(EXTRAVERSION)
 OS = $(shell uname -s | sed -e s/SunOS/solaris/ | tr "[A-Z]" "[a-z]")
 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)
                 hashing after an uri (to, from or request uri)
               - improved uri hashing (password is ignored, port is used only
               - improved uri hashing (password is ignored, port is used only
                 if != 5060 or 5061)
                 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:
 core:
+ - timer: - improved performance/precision, new api, see doc/timers.txt 
  - tcp: - improved  performance (io event handling), using OS specific
  - tcp: - improved  performance (io event handling), using OS specific
            optimizations
            optimizations
         - 1024 connections limit removed (see tcp_max_connections)
         - 1024 connections limit removed (see tcp_max_connections)
@@ -34,6 +51,9 @@ core:
 - default on reply route added: onreply_route {.. } will add a default 
 - default on reply route added: onreply_route {.. } will add a default 
     onreply route that will be executed for any reply (usefull to catch
     onreply route that will be executed for any reply (usefull to catch
     replies without using tm)
     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:
 new config variables:
    tos = number  - ip type of service (TOS) value
    tos = number  - ip type of service (TOS) value
    dns_try_ipv6 = yes/no - if yes and a dns lookup fails, it will retry it
    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
 /* FINAL_RESPONSE_TIMER ... tells how long should the transaction engine
    wait if no final response comes back*/
    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
 /* WAIT timer ... tells how long state should persist in memory after
    a transaction was finalized*/
    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
 /* 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 */
    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 */
 /* 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
 /* when first reply is sent, this additional space is allocated so that
    one does not have to reallocate share memory when the message is
    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>
 	</example>
     </section>
     </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>
 </section>

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

@@ -18,7 +18,7 @@
 	<title><varname>fr_timer</varname> (integer)</title>
 	<title><varname>fr_timer</varname> (integer)</title>
 	<para>
 	<para>
 	    Timer which hits if no final reply for a request or ACK for a
 	    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>
 	<para>
 	<para>
 	    Default value is 30 seconds.
 	    Default value is 30 seconds.
@@ -37,7 +37,7 @@ modparam("tm", "fr_timer", 10)
 	<title><varname>fr_inv_timer</varname> (integer)</title>
 	<title><varname>fr_inv_timer</varname> (integer)</title>
 	<para>
 	<para>
 	    Timer which hits if no final reply for an INVITE arrives after a
 	    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>
 	<para>
 	<para>
 	    Default value is 120 seconds.
 	    Default value is 120 seconds.
@@ -81,7 +81,7 @@ modparam("tm", "wt_timer", 10)
 	    process will be tried to be deleted again.
 	    process will be tried to be deleted again.
 	</para>
 	</para>
 	<para>
 	<para>
-	    Default value is 2 seconds.
+	    Default value is 200 milliseconds.
 	</para>
 	</para>
 	<example>
 	<example>
 	    <title>Set <varname>delete_timer</varname> parameter</title>
 	    <title>Set <varname>delete_timer</varname> parameter</title>
@@ -93,55 +93,19 @@ modparam("tm", "delete_timer", 5)
 	</example>
 	</example>
     </section>
     </section>
     
     
-    <section id="retr_timer1p1">
-	<title><varname>retr_timer1p1</varname> (integer)</title>
+    <section id="retr_timer1">
+	<title><varname>retr_timer1</varname> (integer)</title>
 	<para>
 	<para>
-	    Retransmission period.
+	    Initial retransmission period (in milliseconds).
 	</para>
 	</para>
 	<para>
 	<para>
-	    Default value is 1 second.
+	    Default value is 500 milliseconds.
 	</para>
 	</para>
 	<example>
 	<example>
-	    <title>Set <varname>retr_timer1p1</varname> parameter</title>
+	    <title>Set <varname>retr_timer1</varname> parameter</title>
 	    <programlisting>
 	    <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>
 	    </programlisting>
 	</example>
 	</example>
@@ -150,16 +114,19 @@ modparam("tm", "retr_timer1p3", 8)
     <section id="retr_timer2">
     <section id="retr_timer2">
 	<title><varname>retr_timer2</varname> (integer)</title>
 	<title><varname>retr_timer2</varname> (integer)</title>
 	<para>
 	<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>
 	<para>
 	<para>
-	    Default value is 4 seconds.
+	    Default value is 4000 milliseconds.
 	</para>
 	</para>
 	<example>
 	<example>
 	    <title>Set <varname>retr_timer2</varname> parameter</title>
 	    <title>Set <varname>retr_timer2</varname> parameter</title>
 	    <programlisting>
 	    <programlisting>
 ...
 ...
-modparam("tm", "retr_timer2", 8)
+modparam("tm", "retr_timer2", 2000)
 ...
 ...
 	    </programlisting>
 	    </programlisting>
 	</example>
 	</example>

+ 5 - 12
modules/tm/h_table.c

@@ -61,6 +61,7 @@
 #include "t_stats.h"
 #include "t_stats.h"
 #include "h_table.h"
 #include "h_table.h"
 #include "fix_lumps.h" /* free_via_clen_lump */
 #include "fix_lumps.h" /* free_via_clen_lump */
+#include "timer.h"
 
 
 static enum kill_reason kr;
 static enum kill_reason kr;
 
 
@@ -213,10 +214,7 @@ static void inline init_branches(struct cell *t)
 		uac=&t->uac[i];
 		uac=&t->uac[i];
 		uac->request.my_T = t;
 		uac->request.my_T = t;
 		uac->request.branch = i;
 		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;
 		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 ) );
 	memset( new_cell, 0, sizeof( struct cell ) );
 
 
 	/* UAS */
 	/* 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;
 	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 */
 	/* move the current avp list to transaction -bogdan */
 	old = set_avp_list(AVP_TRACK_FROM | AVP_CLASS_USER,  &new_cell->user_avps );
 	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->relayed_reply_branch   = -1;
 	/* new_cell->T_canceled = T_UNDEFINED; */
 	/* 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_synonym_id(new_cell);
 	init_cell_lock(  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
  * 2004-02-13  t->is_invite, t->local, t->noisy_ctimer replaced
  *             with flags (bogdan)
  *             with flags (bogdan)
  * 2004-08-23  avp support added - avp list linked in transaction (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"
 #include "defs.h"
@@ -50,6 +51,7 @@
 #include "../../types.h"
 #include "../../types.h"
 #include "../../md5utils.h"
 #include "../../md5utils.h"
 #include "../../usr_avp.h"
 #include "../../usr_avp.h"
+#include "../../timer.h"
 #include "config.h"
 #include "config.h"
 
 
 struct s_table;
 struct s_table;
@@ -63,7 +65,7 @@ struct retr_buf;
 #include "sip_msg.h"
 #include "sip_msg.h"
 #include "t_reply.h"
 #include "t_reply.h"
 #include "t_hooks.h"
 #include "t_hooks.h"
-#include "timer.h"
+#include "../../timer.h"
 
 
 #define LOCK_HASH(_h) lock_hash((_h))
 #define LOCK_HASH(_h) lock_hash((_h))
 #define UNLOCK_HASH(_h) unlock_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 };
 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
 typedef struct retr_buf
 {
 {
-	int activ_type;
+	short activ_type;
 	/* set to status code if the buffer is a reply,
 	/* set to status code if the buffer is a reply,
 	0 if request or -1 if local CANCEL */
 	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;
 	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*/
 	/*the cell that contains this retrans_buff*/
 	struct cell* my_T;
 	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;
 }retr_buf_type;
 
 
 
 
@@ -128,11 +133,11 @@ typedef struct ua_server
 	struct sip_msg   *request;
 	struct sip_msg   *request;
 	char             *end_request;
 	char             *end_request;
 	struct retr_buf  response;
 	struct retr_buf  response;
-	unsigned int     status;
 	/* keep to-tags for local 200 replies for INVITE -- 
 	/* keep to-tags for local 200 replies for INVITE -- 
 	 * we need them for dialog-wise matching of ACKs;
 	 * we need them for dialog-wise matching of ACKs;
 	 * the pointer shows to shmem-ed reply */
 	 * the pointer shows to shmem-ed reply */
 	str				 local_totag;
 	str				 local_totag;
+	unsigned int     status;
 }ua_server_type;
 }ua_server_type;
 
 
 
 
@@ -160,9 +165,9 @@ typedef struct ua_client
 
 
 
 
 struct totag_elem {
 struct totag_elem {
+	struct totag_elem *next;
 	str tag;
 	str tag;
 	short acked;
 	short acked;
-	struct totag_elem *next;
 };
 };
 
 
 
 
@@ -176,7 +181,8 @@ struct totag_elem {
    dropping when C timer hits */
    dropping when C timer hits */
 #define T_NOISY_CTIMER_FLAG  (1<<2)
 #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 */
 /* transaction context */
 
 
@@ -190,7 +196,9 @@ typedef struct cell
 	/* sequence number within hash collision slot */
 	/* sequence number within hash collision slot */
 	unsigned int  label;
 	unsigned int  label;
 	/* different information about the transaction */
 	/* 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 ;
 	/* how many processes are currently processing this transaction ;
 	   note that only processes working on a request/reply belonging
 	   note that only processes working on a request/reply belonging
@@ -218,44 +226,40 @@ typedef struct cell
 	struct tmcb_head_list tmcb_hl;
 	struct tmcb_head_list tmcb_hl;
 
 
 	/* bindings to wait and delete timer */
 	/* 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 */
 	/* UA Server */
 	struct ua_server  uas;
 	struct ua_server  uas;
 	/* UA Clients */
 	/* UA Clients */
 	struct ua_client  uac[ MAX_BRANCHES ];
 	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 */
 	/* protection against concurrent reply processing */
 	ser_lock_t   reply_mutex;
 	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 */
 	/* 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 */
 	/* 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];
 	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;
 }cell_type;
 
 
 
 

+ 0 - 7
modules/tm/lock.c

@@ -294,10 +294,3 @@ int release_timerlist_lock( struct timer *timerlist )
 	/* the same as above */
 	/* the same as above */
 	return 0;
 	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 "h_table.h"
-#include "timer.h" 
 
 
 /* Uni*x permissions for IPC */
 /* Uni*x permissions for IPC */
 #define IPC_PERMISSIONS 0666
 #define IPC_PERMISSIONS 0666
@@ -133,7 +132,6 @@ static inline void _unlock( ser_lock_t* s )
 #endif
 #endif
 }
 }
 
 
-int init_timerlist_lock(  enum lists timerlist_id);
 
 
 
 
 #endif
 #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");
 		LOG(L_ERR, "ERROR: attempt to build a CANCEL failed\n");
 		return;
 		return;
 	}
 	}
-
-	     /* install cancel now */
+	/* install cancel now */
 	crb->buffer = cancel;
 	crb->buffer = cancel;
 	crb->buffer_len = len;
 	crb->buffer_len = len;
 	crb->dst = irb->dst;
 	crb->dst = irb->dst;
 	crb->branch = branch;
 	crb->branch = branch;
-
 	/* label it as cancel so that FR timer can better now how to
 	/* label it as cancel so that FR timer can better now how to
 	   deal with it */
 	   deal with it */
 	crb->activ_type = TYPE_LOCAL_CANCEL;
 	crb->activ_type = TYPE_LOCAL_CANCEL;
 
 
 	DBG("DEBUG: cancel_branch: sending cancel...\n");
 	DBG("DEBUG: cancel_branch: sending cancel...\n");
 	SEND_BUFFER( crb );
 	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");
 	DBG("DEBUG: tm_shutdown : start\n");
-	unlink_timer_lists();
 
 
 	/* destroy the hash table */
 	/* destroy the hash table */
 	DBG("DEBUG: tm_shutdown : emptying hash table\n");
 	DBG("DEBUG: tm_shutdown : emptying hash table\n");
 	free_hash_table( );
 	free_hash_table( );
-	DBG("DEBUG: tm_shutdown : releasing timers\n");
-	free_timer_table();
 	DBG("DEBUG: tm_shutdown : removing semaphores\n");
 	DBG("DEBUG: tm_shutdown : removing semaphores\n");
 	lock_cleanup();
 	lock_cleanup();
 	DBG("DEBUG: tm_shutdown : destroying tmcb lists\n");
 	DBG("DEBUG: tm_shutdown : destroying tmcb lists\n");
@@ -118,9 +115,7 @@ int t_release_transaction( struct cell *trans )
 {
 {
 	set_kr(REQ_RLSD);
 	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 );
 	cleanup_uac_timers( trans );
 	
 	
 	put_on_wait( trans );
 	put_on_wait( trans );
@@ -158,7 +153,12 @@ void put_on_wait(  struct cell  *Trans  )
 		4.									WAIT timer executed,
 		4.									WAIT timer executed,
 											transaction deleted
 											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
   *  2003-03-13  now send_pr_buffer will be called w/ function/line info
   *               only when compiling w/ -DEXTRA_DEBUG (andrei)
   *               only when compiling w/ -DEXTRA_DEBUG (andrei)
   *  2003-03-31  200 for INVITE/UAS resent even for UDP (jiri) 
   *  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);
 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();
 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 put_on_wait(  struct cell  *Trans  );
 
 
-void cleanup_localcancel_timers( struct cell *t );
 
 
 int t_relay_to( struct sip_msg  *p_msg ,
 int t_relay_to( struct sip_msg  *p_msg ,
 	struct proxy_l *proxy, int proto, int replicate ) ;
 	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,
 	/* start FR timer -- protocol set by default to PROTO_NONE,
        which means retransmission timer will not be started
        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
 	/* we are on a timer -- don't need to put on wait on script
 	   clean-up	
 	   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, 
 	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);
 	send_sock=get_send_socket( request, &to , proto);
 	if (send_sock==0) {
 	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) {
 			if (SEND_BUFFER(&t_cancel->uac[i].request) == -1) {
 				LOG(L_ERR, "ERROR: e2e_cancel: send failed\n");
 				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 {
 		} else {
 			if (t_invite->uac[i].last_received < 100) {
 			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);
 				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;
 					lowest_error = -1;
 				}
 				}
 			}
 			}
@@ -553,7 +557,9 @@ int t_forward_nonack( struct cell *t, struct sip_msg* p_msg ,
 			} else {
 			} else {
 				success_branch++;
 				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) {
 	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)
  * 2004-10-10: use of mhomed disabled for replies (jiri)
  * 2005-02-01: use the incoming request interface for sending the replies
  * 2005-02-01: use the incoming request interface for sending the replies
  *             - changes in init_rb() (bogdan)
  *             - changes in init_rb() (bogdan)
+ *  2005-12-09  added t_set_fr()  (andrei)
  */
  */
 
 
 #include "defs.h"
 #include "defs.h"
@@ -91,6 +92,7 @@
 #include "../../parser/parse_from.h"
 #include "../../parser/parse_from.h"
 #include "../../ut.h"
 #include "../../ut.h"
 #include "../../timer.h"
 #include "../../timer.h"
+#include "../../timer_ticks.h"
 #include "../../hash_func.h"
 #include "../../hash_func.h"
 #include "../../globals.h"
 #include "../../globals.h"
 #include "../../forward.h"
 #include "../../forward.h"
@@ -920,9 +922,9 @@ int t_check( struct sip_msg* p_msg , int *param_branch )
 
 
 		}
 		}
 #ifdef EXTRA_DEBUG
 #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 "
 			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();
 			abort();
 		}
 		}
 #endif
 #endif
@@ -945,6 +947,7 @@ int init_rb( struct retr_buf *rb, struct sip_msg *msg)
 	int proto;
 	int proto;
 	int backup_mhomed;
 	int backup_mhomed;
 
 
+	/* rb. timers are init. init_t()/new_cell() */
 	via=msg->via1;
 	via=msg->via1;
 	if (!reply_to_via) {
 	if (!reply_to_via) {
 		update_sock_struct_from_ip( &rb->dst.to, msg );
 		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)
 static inline void init_new_t(struct cell *new_cell, struct sip_msg *p_msg)
 {
 {
 	struct sip_msg *shm_msg;
 	struct sip_msg *shm_msg;
+	unsigned int timeout; /* avp timeout gets stored here (in s) */
 
 
 	shm_msg=new_cell->uas.request;
 	shm_msg=new_cell->uas.request;
 	new_cell->from.s=shm_msg->from->name.s;
 	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;
 	if (p_msg->REQ_METHOD==METHOD_INVITE) new_cell->flags |= T_IS_INVITE_FLAG;
 	new_cell->on_negative=get_on_negative();
 	new_cell->on_negative=get_on_negative();
 	new_cell->on_reply=get_on_reply();
 	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();
 	new_cell->on_branch=get_on_branch();
 }
 }
 
 
@@ -1322,3 +1347,36 @@ int t_lookup_callid(struct cell ** trans, str callid, str cseq) {
 	return -1;
 	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/
  *  2003-02-24  s/T_NULL/T_NULL_CELL/ to avoid redefinition conflict w/
  *               nameser_compat.h (andrei)
  *               nameser_compat.h (andrei)
  *  2004-02-11  FIFO/CANCEL + alignments (hash=f(callid,cseq)) (uli+jiri)
  *  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 */
 /* lookup a transaction by callid and cseq */
 int t_lookup_callid(struct cell** trans, str callid, str 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
 #endif
 
 

+ 48 - 49
modules/tm/t_reply.c

@@ -66,6 +66,7 @@
  *              the request (bogdan)
  *              the request (bogdan)
  *  2005-09-01  reverted to the old way of checking response.dst.send_sock
  *  2005-09-01  reverted to the old way of checking response.dst.send_sock
  *               in t_retransmit_reply & reply_light (andrei)
  *               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 ) {
 	if ( !is_local(t) && t->uas.request->REQ_METHOD==METHOD_INVITE ) {
 		/* crank timers for negative replies */
 		/* crank timers for negative replies */
 		if (t->uas.status>=300) {
 		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;
 			return;
 		}
 		}
 		/* local UAS retransmits too */
 		/* 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
 			   even if TCP used, UDP could be used upstream and
 			   loose the 200, which is not retransmitted by proxies
 			   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;
 			return;
 		}
 		}
 	}
 	}
@@ -961,9 +966,8 @@ void cleanup_uac_timers( struct cell *t )
 	int i;
 	int i;
 
 
 	/* reset FR/retransmission timers */
 	/* 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");
 	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;
 	struct cell *t;
 	str next_hop;
 	str next_hop;
 	avp_list_t* backup_list;
 	avp_list_t* backup_list;
-	unsigned int timer;
 
 
 	/* make sure we know the associated transaction ... */
 	/* make sure we know the associated transaction ... */
 	if (t_check( p_msg  , &branch )==-1)
 	if (t_check( p_msg  , &branch )==-1)
@@ -1293,40 +1296,46 @@ int reply_received( struct sip_msg  *p_msg )
 		/* .. which is not e2e ? ... */
 		/* .. which is not e2e ? ... */
 		&& is_invite(t) ) {
 		&& is_invite(t) ) {
 			/* ... then just stop timers */
 			/* ... then just stop timers */
-			reset_timer( &uac->local_cancel.retr_timer);
 			if ( msg_status >= 200 )
 			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");
 			DBG("DEBUG: reply to local CANCEL processed\n");
 			goto done;
 			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
 		/* acknowledge negative INVITE replies (do it before detailed
 		 * on_reply processing, which may take very long, like if it
 		 * on_reply processing, which may take very long, like if it
 		 * is attempted to establish a TCP connection to a fail-over dst */
 		 * 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 */
 	/* processing of on_reply block */
 	if (t->on_reply) {
 	if (t->on_reply) {
@@ -1351,7 +1360,8 @@ int reply_received( struct sip_msg  *p_msg )
 			      */
 			      */
 			cleanup_uac_timers( t );
 			cleanup_uac_timers( t );
 			if (is_invite(t)) cancel_uacs( t, cancel_bitmap );
 			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);
 			put_on_wait(t);
 		}
 		}
 	} else {
 	} else {
@@ -1363,8 +1373,11 @@ int reply_received( struct sip_msg  *p_msg )
 			     */
 			     */
 			cleanup_uac_timers( t );
 			cleanup_uac_timers( t );
 			if (is_invite(t)) cancel_uacs( t, cancel_bitmap );
 			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)) )
 					((msg_status>=180) || (last_uac_status==0)) )
 			) ) { /* provisional now */
 			) ) { /* provisional now */
 		if (is_invite(t)) {
 		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 );
 			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 */
 	} /* provisional replies */
 
 

+ 211 - 656
modules/tm/timer.c

@@ -98,6 +98,7 @@
  *  2003-06-27  timers are not unlinked if timerlist is 0 (andrei)
  *  2003-06-27  timers are not unlinked if timerlist is 0 (andrei)
  *  2004-02-13  t->is_invite, t->local, t->noisy_ctimer replaced;
  *  2004-02-13  t->is_invite, t->local, t->noisy_ctimer replaced;
  *              timer_link.payload removed (bogdan)
  *              timer_link.payload removed (bogdan)
+ *  2005-10-03  almost completely rewritten to use the new timers (andrei)
  */
  */
 
 
 #include "defs.h"
 #include "defs.h"
@@ -116,125 +117,108 @@
 #include "../../config.h"
 #include "../../config.h"
 #include "../../parser/parser_f.h"
 #include "../../parser/parser_f.h"
 #include "../../ut.h"
 #include "../../ut.h"
+#include "../../timer_ticks.h"
 #include "t_funcs.h"
 #include "t_funcs.h"
 #include "t_reply.h"
 #include "t_reply.h"
 #include "t_cancel.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 ***************************/
 /******************** 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;
 	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
 	/* there may still be FR/RETR timers, which have been reset
 	   (i.e., time_out==TIMER_DELETED) but are stilled linked to
 	   (i.e., time_out==TIMER_DELETED) but are stilled linked to
 	   timer lists and must be removed from there before the
 	   timer lists and must be removed from there before the
 	   structures are released
 	   structures are released
 	*/
 	*/
 	unlink_timers( p_cell );
 	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 */
 	/* still in use ... don't delete */
 	if ( IS_REFFED_UNSAFE(p_cell) ) {
 	if ( IS_REFFED_UNSAFE(p_cell) ) {
 		if (unlock) UNLOCK_HASH(p_cell->hash_index);
 		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 {
 	} else {
 		if (unlock) UNLOCK_HASH(p_cell->hash_index);
 		if (unlock) UNLOCK_HASH(p_cell->hash_index);
+#ifdef EXTRA_DEBUG
 		DBG("DEBUG: delete transaction %p\n", p_cell );
 		DBG("DEBUG: delete transaction %p\n", p_cell );
+#endif
 		free_cell( p_cell );
 		free_cell( p_cell );
+		return 0;
 	}
 	}
 }
 }
 
 
+
+
 static void fake_reply(struct cell *t, int branch, int code )
 static void fake_reply(struct cell *t, int branch, int code )
 {
 {
 	branch_bm_t cancel_bitmap;
 	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
 #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"
 		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();
 		abort();
 	}	
 	}	
 #endif
 #endif
-
-	/*the transaction is already removed from RETRANSMISSION_LIST by timer*/
-	/* retransmission */
 	if ( r_buf->activ_type==TYPE_LOCAL_CANCEL 
 	if ( r_buf->activ_type==TYPE_LOCAL_CANCEL 
 		|| r_buf->activ_type==TYPE_REQUEST ) {
 		|| r_buf->activ_type==TYPE_REQUEST ) {
+#ifdef EXTRA_DEBUG
 			DBG("DEBUG: retransmission_handler : "
 			DBG("DEBUG: retransmission_handler : "
 				"request resending (t=%p, %.9s ... )\n", 
 				"request resending (t=%p, %.9s ... )\n", 
 				r_buf->my_T, r_buf->buffer);
 				r_buf->my_T, r_buf->buffer);
+#endif
 			if (SEND_BUFFER( r_buf )==-1) {
 			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 );
 				fake_reply(r_buf->my_T, r_buf->branch, 503 );
-				return;
+				return (ticks_t)-1;
 			}
 			}
 	} else {
 	} else {
+#ifdef EXTRA_DEBUG
 			DBG("DEBUG: retransmission_handler : "
 			DBG("DEBUG: retransmission_handler : "
 				"reply resending (t=%p, %.9s ... )\n", 
 				"reply resending (t=%p, %.9s ... )\n", 
 				r_buf->my_T, r_buf->buffer);
 				r_buf->my_T, r_buf->buffer);
+#endif
 			t_retransmit_reply(r_buf->my_T);
 			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
 #	ifdef EXTRA_DEBUG
-	if (t->damocles) 
+	if (t->flags & T_IN_AGONY) 
 	{
 	{
 		LOG( L_ERR, "ERROR: transaction %p scheduled for deletion and"
 		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();
 		abort();
 	}
 	}
 #	endif
 #	endif
-
-	reset_timer(  &(r_buf->retr_timer) );
-
-	/* the transaction is already removed from FR_LIST by the timer */
-
 	/* FR for local cancels.... */
 	/* FR for local cancels.... */
 	if (r_buf->activ_type==TYPE_LOCAL_CANCEL)
 	if (r_buf->activ_type==TYPE_LOCAL_CANCEL)
 	{
 	{
+#ifdef TIMER_DEBUG
 		DBG("DEBUG: final_response_handler: stop retr for Local Cancel\n");
 		DBG("DEBUG: final_response_handler: stop retr for Local Cancel\n");
+#endif
 		return;
 		return;
 	}
 	}
-
 	/* FR for replies (negative INVITE replies) */
 	/* FR for replies (negative INVITE replies) */
 	if (r_buf->activ_type>0) {
 	if (r_buf->activ_type>0) {
 #		ifdef EXTRA_DEBUG
 #		ifdef EXTRA_DEBUG
 		if (t->uas.request->REQ_METHOD!=METHOD_INVITE
 		if (t->uas.request->REQ_METHOD!=METHOD_INVITE
 			|| t->uas.status < 200 ) {
 			|| 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();
 			abort();
 		}
 		}
 #		endif
 #		endif
@@ -402,15 +366,17 @@ inline static void final_response_handler( struct timer_link *fr_tl )
 		&& has_noisy_ctimer(t) == 0;
 		&& has_noisy_ctimer(t) == 0;
 	if (silent) {
 	if (silent) {
 		UNLOCK_REPLIES(t);
 		UNLOCK_REPLIES(t);
+#ifdef EXTRA_DEBUG
 		DBG("DEBUG: final_response_handler: transaction silently dropped (%p)\n",t);
 		DBG("DEBUG: final_response_handler: transaction silently dropped (%p)\n",t);
+#endif
 		put_on_wait( t );
 		put_on_wait( t );
 		return;
 		return;
 	}
 	}
-
+#ifdef EXTRA_DEBUG
 	DBG("DEBUG: final_response_handler:stop retr. and send CANCEL (%p)\n", t);
 	DBG("DEBUG: final_response_handler:stop retr. and send CANCEL (%p)\n", t);
-
+#endif
 	if (is_invite(t) && 
 	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) {
 	    t->uac[r_buf->branch].last_received > 0) {
 		reply_code = 480; /* Request Terminated */
 		reply_code = 480; /* Request Terminated */
 	} else {
 	} else {
@@ -418,554 +384,143 @@ inline static void final_response_handler( struct timer_link *fr_tl )
 	}
 	}
 
 
 	fake_reply(t, r_buf->branch, reply_code );
 	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
 #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
 #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
 #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
 #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)
  *  2003-09-12  timer_link.tg exists only if EXTRA_DEBUG (andrei)
  *  2004-02-13  timer_link.payload removed (bogdan)
  *  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 "defs.h"
 
 
 #include "lock.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
 #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-11  FIFO/CANCEL + alignments (hash=f(callid,cseq)) (uli+jiri)
  *  2004-02-18  t_reply exported via FIFO - imported from VM (bogdan)
  *  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)
  *  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
  *  2005-12-09  fixup_hostport2proxy uses route_struct to access param #1
  *              when fixing param #2
  *              when fixing param #2
+ *  2005-12-09  added t_set_fr() (andrei)
  */
  */
 
 
 
 
@@ -110,6 +112,7 @@
 #include "callid.h"
 #include "callid.h"
 #include "t_cancel.h"
 #include "t_cancel.h"
 #include "t_fifo.h"
 #include "t_fifo.h"
+#include "timer.h"
 
 
 MODULE_VERSION
 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_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 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);
 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;
 static char *fr_timer_param = FR_TIMER_AVP;
@@ -236,6 +241,10 @@ static cmd_export_t cmds[]={
 			REQUEST_ROUTE | FAILURE_ROUTE },
 			REQUEST_ROUTE | FAILURE_ROUTE },
 	{"t_write_unix",      t_write_unix,             2, fixup_t_write,
 	{"t_write_unix",      t_write_unix,             2, fixup_t_write,
 	                REQUEST_ROUTE | FAILURE_ROUTE },
 	                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 */
 	/* not applicable from the script */
 	{"register_tmcb",      (cmd_function)register_tmcb,     NO_SCRIPT,   0, 0},
 	{"register_tmcb",      (cmd_function)register_tmcb,     NO_SCRIPT,   0, 0},
@@ -263,14 +272,12 @@ static cmd_export_t cmds[]={
 static param_export_t params[]={
 static param_export_t params[]={
 	{"ruri_matching",       INT_PARAM, &ruri_matching                        },
 	{"ruri_matching",       INT_PARAM, &ruri_matching                        },
 	{"via1_matching",       INT_PARAM, &via1_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                         },
 	{"noisy_ctimer",        INT_PARAM, &noisy_ctimer                         },
 	{"uac_from",            STR_PARAM, &uac_from                             },
 	{"uac_from",            STR_PARAM, &uac_from                             },
 	{"unix_tx_timeout",     INT_PARAM, &tm_unix_tx_timeout                   },
 	{"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)
 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));
 			(long)sizeof(struct cell), (long)sizeof(struct sip_msg));
 	/* checking if we have sufficient bitmap capacity for given
 	/* checking if we have sufficient bitmap capacity for given
 	   maximum number of  branches */
 	   maximum number of  branches */
@@ -435,13 +442,10 @@ static int mod_init(void)
 	/* init static hidden values */
 	/* init static hidden values */
 	init_t();
 	init_t();
 
 
-	if (!tm_init_timers()) {
+	if (tm_init_timers()==-1) {
 		LOG(L_ERR, "ERROR: mod_init: timer init failed\n");
 		LOG(L_ERR, "ERROR: mod_init: timer init failed\n");
 		return -1;
 		return -1;
 	}
 	}
-	/* register the timer function */
-	register_timer( timer_routine , 0 /* empty attr */, 1 );
-
 	/* init_tm_stats calls process_count, which should
 	/* init_tm_stats calls process_count, which should
 	 * NOT be called from mod_init, because one does not
 	 * NOT be called from mod_init, because one does not
 	 * now, if a timer is used and thus how many processes
 	 * 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);
 	LOG(L_CRIT, "ERROR: w_t_relay_to: unsupported mode: %d\n", rmode);
 	return 0;
 	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);
 			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;
 	return 1;
 
 
  error1:
  error1: