- tm migrated to the new timers (tm timers completely re-written).
authorAndrei Pelinescu-Onciul <andrei@iptel.org>
Sat, 10 Dec 2005 16:04:53 +0000 (16:04 +0000)
committerAndrei Pelinescu-Onciul <andrei@iptel.org>
Sat, 10 Dec 2005 16:04:53 +0000 (16:04 +0000)
- 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

20 files changed:
Makefile.defs
NEWS
modules/tm/config.h
modules/tm/doc/functions.xml
modules/tm/doc/params.xml
modules/tm/h_table.c
modules/tm/h_table.h
modules/tm/lock.c
modules/tm/lock.h
modules/tm/t_cancel.c
modules/tm/t_funcs.c
modules/tm/t_funcs.h
modules/tm/t_fwd.c
modules/tm/t_lookup.c
modules/tm/t_lookup.h
modules/tm/t_reply.c
modules/tm/timer.c
modules/tm/timer.h
modules/tm/tm.c
modules/tm/uac.c

index f37728f..dd175fc 100644 (file)
@@ -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]")
diff --git a/NEWS b/NEWS
index 134baca..d5edc54 100644 (file)
--- a/NEWS
+++ b/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
index b6918f1..b609740 100644 (file)
 
 /* 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
index 84d0e88..ab4ac33 100644 (file)
@@ -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>
index 40237f4..d0490a6 100644 (file)
@@ -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>
index 832e9f9..869729c 100644 (file)
@@ -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 );
index 8290379..9e6d7eb 100644 (file)
@@ -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;
 
 
index 689f890..90ab32e 100644 (file)
@@ -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;
-}
index 50d3a6a..2daa625 100644 (file)
@@ -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
index 1e469de..b000df1 100644 (file)
@@ -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);
 }
 
 
index f43a887..ea044a6 100644 (file)
@@ -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);
+       }
 }
 
 
index 1a3b3f2..0495b58 100644 (file)
@@ -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 ) ;
index 1921e0c..c444b71 100644 (file)
@@ -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) {
index 48fa464..af7d87e 100644 (file)
@@ -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;
+}
index e54215a..757c5d3 100644 (file)
@@ -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
 
index bb7bb5c..ecbbf32 100644 (file)
@@ -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 */
 
index 5de145c..7575c67 100644 (file)
@@ -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"
 #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;
 }
 
index dfd2783..9d608bb 100644 (file)
  * --------
  *  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
index 7a43676..18a0ab8 100644 (file)
  *  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)
  */
 
 
 #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);
+}
+
index e9e6595..3d152bf 100644 (file)
@@ -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: