GPLization banner introduced to *.[hc] files
[sip-router] / modules / tm / timer.c
1 /*
2  * $Id$
3  *
4  *
5  * Copyright (C) 2001-2003 Fhg Fokus
6  *
7  * This file is part of ser, a free SIP server.
8  *
9  * ser is free software; you can redistribute it and/or modify
10  * it under the terms of the GNU General Public License as published by
11  * the Free Software Foundation; either version 2 of the License, or
12  * (at your option) any later version
13  *
14  * For a license to use the ser software under conditions
15  * other than those described here, or to purchase support for this
16  * software, please contact iptel.org by e-mail at the following addresses:
17  *    info@iptel.org
18  *
19  * ser is distributed in the hope that it will be useful,
20  * but WITHOUT ANY WARRANTY; without even the implied warranty of
21  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
22  * GNU General Public License for more details.
23  *
24  * You should have received a copy of the GNU General Public License 
25  * along with this program; if not, write to the Free Software 
26  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
27  */
28
29
30 /* 
31   timer.c is where we implement TM timers. It has been designed
32   for high performance using some techniques of which timer users
33   need to be aware.
34
35         One technique is "fixed-timer-length". We maintain separate 
36         timer lists, all of them include elements of the same time
37         to fire. That allows *appending* new events to the list as
38         opposed to inserting them by time, which is costly due to
39         searching time spent in a mutex. The performance benefit is
40         noticeable. The limitation is you need a new timer list for
41         each new timer length.
42
43         Another technique is the timer process slices off expired elements
44         from the list in a mutex, but executes the timer after the mutex
45         is left. That saves time greatly as whichever process wants to
46         add/remove a timer, it does not have to wait until the current
47         list is processed. However, be aware the timers may hit in a delayed
48         manner; you have no guarantee in your process that after resetting a timer, 
49         it will no more hit. It might have been removed by timer process,
50     and is waiting to be executed.  The following example shows it:
51
52                         PROCESS1                                TIMER PROCESS
53
54         0.                                                              timer hits, it is removed from queue and
55                                                                         about to be executed
56         1.      process1 decides to
57                 reset the timer 
58         2.                                                              timer is executed now
59         3.      if the process1 naively
60                 thinks the timer could not 
61                 have been executed after 
62                 resetting the timer, it is
63                 WRONG -- it was (step 2.)
64
65         So be careful when writing the timer handlers. Currently defined timers 
66         don't hurt if they hit delayed, I hope at least. Retransmission timer 
67         may results in a useless retransmission -- not too bad. FR timer not too
68         bad either as timer processing uses a REPLY mutex making it safe to other
69         processing affecting transaction state. Wait timer not bad either -- processes
70         putting a transaction on wait don't do anything with it anymore.
71
72                 Example when it does not hurt:
73
74                         P1                                              TIMER
75         0.                                                              RETR timer removed from list and
76                                                                         scheduled for execution
77         1. 200/BYE received->
78            reset RETR, put_on_wait
79         2.                                                              RETR timer executed -- too late but it does
80                                                                         not hurt
81         3.                                                              WAIT handler executed
82
83         The rule of thumb is don't touch data you put under a timer. Create data,
84     put them under a timer, and let them live until they are safely destroyed from
85     wait/delete timer.  The only safe place to manipulate the data is 
86     from timer process in which delayed timers cannot hit (all timers are
87     processed sequentially).
88
89         A "bad example" -- rewriting content of retransmission buffer
90         in an unprotected way is bad because a delayed retransmission timer might 
91         hit. Thats why our reply retransmission procedure is enclosed in 
92         a REPLY_LOCK.
93
94 */
95
96
97 #include "config.h"
98 #include "h_table.h"
99 #include "timer.h"
100 #include "../../dprint.h"
101 #include "lock.h"
102 #include "t_stats.h"
103
104 #include "../../hash_func.h"
105 #include "../../dprint.h"
106 #include "../../config.h"
107 #include "../../parser/parser_f.h"
108 #include "../../ut.h"
109 #include "t_funcs.h"
110 #include "t_reply.h"
111 #include "t_cancel.h"
112
113
114 static struct timer_table *timertable;
115
116 int noisy_ctimer=0;
117
118
119 int timer_group[NR_OF_TIMER_LISTS] = 
120 {
121         TG_FR, TG_FR,
122         TG_WT,
123         TG_DEL,
124         TG_RT, TG_RT, TG_RT, TG_RT
125 };
126
127 /* default values of timeouts for all the timer list
128    (see timer.h for enumeration of timer lists)
129 */
130 unsigned int timer_id2timeout[NR_OF_TIMER_LISTS] = {
131         FR_TIME_OUT,            /* FR_TIMER_LIST */
132         INV_FR_TIME_OUT,        /* FR_INV_TIMER_LIST */
133         WT_TIME_OUT,            /* WT_TIMER_LIST */
134         DEL_TIME_OUT,           /* DELETE_LIST */
135         RETR_T1,                        /* RT_T1_TO_1 */
136         RETR_T1 << 1,           /* RT_T1_TO_2 */
137         RETR_T1 << 2,           /* RT_T1_TO_3 */
138         RETR_T2                         /* RT_T2 */
139                                                 /* NR_OF_TIMER_LISTS */
140 };
141
142 /******************** handlers ***************************/
143
144
145
146 static void delete_cell( struct cell *p_cell, int unlock )
147 {
148
149 #ifdef EXTRA_DEBUG
150         int i;
151 #endif
152
153         /* there may still be FR/RETR timers, which have been reset
154            (i.e., time_out==TIMER_DELETED) but are stilled linked to
155            timer lists and must be removed from there before the
156            structures are released
157         */
158         unlink_timers( p_cell );
159
160 #ifdef EXTRA_DEBUG
161
162         if (is_in_timer_list2(& p_cell->wait_tl )) {
163                 LOG( L_ERR, "ERROR: transaction %p scheduled for deletion and"
164                         " still on WAIT, timeout=%d\n", p_cell, p_cell->wait_tl.time_out);
165                 abort();
166         }
167         if (is_in_timer_list2(& p_cell->uas.response.retr_timer )) {
168                 LOG( L_ERR, "ERROR: transaction %p scheduled for deletion and"
169                         " still on RETR (rep), timeout=%d\n",
170                         p_cell, p_cell->uas.response.retr_timer.time_out);
171                 abort();
172         }
173         if (is_in_timer_list2(& p_cell->uas.response.fr_timer )) {
174                 LOG( L_ERR, "ERROR: transaction %p scheduled for deletion and"
175                         " still on FR (rep), timeout=%d\n", p_cell,
176                         p_cell->uas.response.fr_timer.time_out);
177                 abort();
178         }
179         for (i=0; i<p_cell->nr_of_outgoings; i++) {
180                 if (is_in_timer_list2(& p_cell->uac[i].request.retr_timer)) {
181                         LOG( L_ERR, "ERROR: transaction %p scheduled for deletion and"
182                                 " still on RETR (req %d), timeout %d\n", p_cell, i,
183                                 p_cell->uac[i].request.retr_timer.time_out);
184                         abort();
185                 }
186                 if (is_in_timer_list2(& p_cell->uac[i].request.fr_timer)) {
187                         LOG( L_ERR, "ERROR: transaction %p scheduled for deletion and"
188                                 " still on FR (req %d), timeout %d\n", p_cell, i,
189                                 p_cell->uac[i].request.fr_timer.time_out);
190                         abort();
191                 }
192                 if (is_in_timer_list2(& p_cell->uac[i].local_cancel.retr_timer)) {
193                         LOG( L_ERR, "ERROR: transaction %p scheduled for deletion and"
194                                 " still on RETR/cancel (req %d), timeout %d\n", p_cell, i,
195                                 p_cell->uac[i].request.retr_timer.time_out);
196                         abort();
197                 }
198                 if (is_in_timer_list2(& p_cell->uac[i].local_cancel.fr_timer)) {
199                         LOG( L_ERR, "ERROR: transaction %p scheduled for deletion and"
200                                 " still on FR/cancel (req %d), timeout %d\n", p_cell, i,
201                                 p_cell->uac[i].request.fr_timer.time_out);
202                         abort();
203                 }
204         }
205         /* reset_retr_timers( hash__XX_table, p_cell ); */
206 #endif
207         /* still in use ... don't delete */
208         if ( IS_REFFED_UNSAFE(p_cell) ) {
209                 if (unlock) UNLOCK_HASH(p_cell->hash_index);
210                 DBG("DEBUG: delete_cell %p: can't delete -- still reffed\n",
211                         p_cell);
212                 /* it's added to del list for future del */
213                 set_timer( &(p_cell->dele_tl), DELETE_LIST );
214         } else {
215                 if (unlock) UNLOCK_HASH(p_cell->hash_index);
216                 DBG("DEBUG: delete transaction %p\n", p_cell );
217                 free_cell( p_cell );
218         }
219 }
220
221 static void fake_reply(struct cell *t, int branch, int code )
222 {
223         branch_bm_t cancel_bitmap;
224         short do_cancel_branch;
225         enum rps reply_status;
226
227         do_cancel_branch=t->is_invite && should_cancel_branch(t, branch);
228
229         cancel_bitmap=do_cancel_branch ? 1<<branch : 0;
230         if (t->local) {
231                 reply_status=local_reply( t, FAKED_REPLY, branch, 
232                         code, &cancel_bitmap );
233         } else {
234                 reply_status=relay_reply( t, FAKED_REPLY, branch, code,
235                         &cancel_bitmap );
236         }
237         /* now when out-of-lock do the cancel I/O */
238         if (do_cancel_branch) cancel_branch(t, branch );
239         /* it's cleaned up on error; if no error occured and transaction
240            completed regularly, I have to clean-up myself
241         */
242         if (reply_status==RPS_COMPLETED) {
243                 /* don't need to cleanup uac_timers -- they were cleaned
244                    branch by branch and this last branch's timers are
245                    reset now too
246                 */
247                 /* don't need to issue cancels -- local cancels have been
248                    issued branch by branch and this last branch was
249                    cancelled now too
250                 */
251                 /* then the only thing to do now is to put the transaction
252                    on FR/wait state 
253                 */
254                 set_final_timer(  t );
255         }
256 }
257
258
259 inline static void retransmission_handler( void *attr)
260 {
261         struct retr_buf* r_buf ;
262         enum lists id;
263
264         r_buf = (struct retr_buf*)attr;
265 #ifdef EXTRA_DEBUG
266         if (r_buf->my_T->damocles) {
267                 LOG( L_ERR, "ERROR: transaction %p scheduled for deletion and"
268                         " called from RETR timer\n",r_buf->my_T);
269                 abort();
270         }       
271 #endif
272
273         /*the transaction is already removed from RETRANSMISSION_LIST by timer*/
274         /* retransmision */
275         if ( r_buf->activ_type==TYPE_LOCAL_CANCEL 
276                 || r_buf->activ_type==0 ) {
277                         DBG("DEBUG: retransmission_handler : "
278                                 "request resending (t=%p, %.9s ... )\n", 
279                                 r_buf->my_T, r_buf->buffer);
280                         if (SEND_BUFFER( r_buf )<=0) {
281                                 reset_timer( &r_buf->fr_timer );
282                                 fake_reply(r_buf->my_T, r_buf->branch, 503 );
283                                 return;
284                         }
285         } else {
286                         DBG("DEBUG: retransmission_handler : "
287                                 "reply resending (t=%p, %.9s ... )\n", 
288                                 r_buf->my_T, r_buf->buffer);
289                         t_retransmit_reply(r_buf->my_T);
290         }
291
292         id = r_buf->retr_list;
293         r_buf->retr_list = id < RT_T2 ? id + 1 : RT_T2;
294
295         set_timer(&(r_buf->retr_timer),id < RT_T2 ? id + 1 : RT_T2 );
296
297         DBG("DEBUG: retransmission_handler : done\n");
298 }
299
300
301
302
303 inline static void final_response_handler( void *attr)
304 {
305         int silent;
306         struct retr_buf* r_buf;
307         struct cell *t;
308
309         r_buf = (struct retr_buf*)attr;
310         t=r_buf->my_T;
311
312 #       ifdef EXTRA_DEBUG
313         if (t->damocles) 
314         {
315                 LOG( L_ERR, "ERROR: transaction %p scheduled for deletion and"
316                         " called from FR timer\n",r_buf->my_T);
317                 abort();
318         }
319 #       endif
320
321         reset_timer(  &(r_buf->retr_timer) );
322
323         /* the transaction is already removed from FR_LIST by the timer */
324
325         /* FR for local cancels.... */
326         if (r_buf->activ_type==TYPE_LOCAL_CANCEL)
327         {
328                 DBG("DEBUG: FR_handler: stop retr for Local Cancel\n");
329                 return;
330         }
331
332         /* FR for replies (negative INVITE replies) */
333         if (r_buf->activ_type>0) {
334 #               ifdef EXTRA_DEBUG
335                 if (t->uas.request->REQ_METHOD!=METHOD_INVITE
336                         || t->uas.status < 300 ) {
337                         LOG(L_ERR, "ERROR: FR timer: uknown type reply buffer\n");
338                         abort();
339                 }
340 #               endif
341                 put_on_wait( t );
342                 return;
343         };
344
345         /* lock reply processing to determine how to proceed reliably */
346         LOCK_REPLIES( t );
347         /* now it can be only a request retransmission buffer;
348            try if you can simply discard the local transaction 
349            state without compellingly removing it from the
350            world */
351         silent=
352                 /* not for UACs */
353                 !t->local
354                 /* invites only */
355                 && t->is_invite
356                 /* parallel forking does not allow silent state discarding */
357                 && t->nr_of_outgoings==1
358                 /* on_no_reply handler not installed -- serial forking could occur 
359                    otherwise */
360                 && t->on_negative==0
361                 /* something received -- we will not be silent on error */
362                 && t->uac[r_buf->branch].last_received>0
363                 /* don't go silent if disallowed globally ... */
364                 && noisy_ctimer==0
365                 /* ... or for this particular transaction */
366                 && t->noisy_ctimer==0;
367         if (silent) {
368                 UNLOCK_REPLIES(t);
369                 DBG("DEBUG: FR_handler: transaction silently dropped (%p)\n",t);
370                 put_on_wait( t );
371                 return;
372         }
373
374         DBG("DEBUG: FR_handler:stop retr. and send CANCEL (%p)\n", t);
375         fake_reply(t, r_buf->branch, 408 );
376
377         DBG("DEBUG: final_response_handler : done\n");
378 }
379
380 void cleanup_localcancel_timers( struct cell *t )
381 {
382         int i;
383         for (i=0; i<t->nr_of_outgoings; i++ )  {
384                 reset_timer(  &t->uac[i].local_cancel.retr_timer );
385                 reset_timer(  &t->uac[i].local_cancel.fr_timer );
386         }
387 }
388
389
390 inline static void wait_handler( void *attr)
391 {
392         struct cell *p_cell = (struct cell*)attr;
393
394 #ifdef EXTRA_DEBUG
395         if (p_cell->damocles) {
396                 LOG( L_ERR, "ERROR: transaction %p scheduled for deletion and"
397                         " called from WAIT timer\n",p_cell);
398                 abort();
399         }       
400         DBG("DEBUG: ---------- WAIT timer hit ------- \n");
401 #endif
402
403         /* stop cancel timers if any running */
404         if (p_cell->is_invite) cleanup_localcancel_timers( p_cell );
405
406         /* the transaction is already removed from WT_LIST by the timer */
407         /* remove the cell from the hash table */
408         DBG("DEBUG: wait_handler : removing %p from table \n", p_cell );
409         LOCK_HASH( p_cell->hash_index );
410         remove_from_hash_table_unsafe(  p_cell );
411         /* jku: no more here -- we do it when we put a transaction on wait */
412 #ifdef EXTRA_DEBUG
413         p_cell->damocles = 1;
414 #endif
415         /* delete (returns with UNLOCK-ed_HASH) */
416         delete_cell( p_cell, 1 /* unlock on return */ );
417         DBG("DEBUG: wait_handler : done\n");
418 }
419
420
421
422
423 inline static void delete_handler( void *attr)
424 {
425         struct cell *p_cell = (struct cell*)attr;
426
427         DBG("DEBUG: delete_handler : removing %p \n", p_cell );
428 #ifdef EXTRA_DEBUG
429         if (p_cell->damocles==0) {
430                 LOG( L_ERR, "ERROR: transaction %p not scheduled for deletion"
431                         " and called from DELETE timer\n",p_cell);
432                 abort();
433         }       
434 #endif
435
436         /* we call delete now without any locking on hash/ref_count;
437            we can do that because delete_handler is only entered after
438            the delete timer was installed from wait_handler, which
439            removed transaction from hash table and did not destroy it
440            because some processes were using it; that means that the
441            processes currently using the transaction can unref and no
442            new processes can ref -- we can wait until ref_count is
443            zero safely without locking
444         */
445         delete_cell( p_cell, 0 /* don't unlock on return */ );
446     DBG("DEBUG: delete_handler : done\n");
447 }
448
449
450 /***********************************************************/
451
452 struct timer_table *get_timertable()
453 {
454         return timertable;
455 }
456
457
458 void unlink_timer_lists()
459 {
460         struct timer_link  *tl, *end, *tmp;
461         enum lists i;
462
463         /* remember the DELETE LIST */
464         tl = timertable->timers[DELETE_LIST].first_tl.next_tl;
465         end = & timertable->timers[DELETE_LIST].last_tl;
466         /* unlink the timer lists */
467         for( i=0; i<NR_OF_TIMER_LISTS ; i++ )
468                 reset_timer_list( i );
469         DBG("DEBUG: tm_shutdown : empting DELETE list\n");
470         /* deletes all cells from DELETE_LIST list 
471            (they are no more accessible from enrys) */
472         while (tl!=end) {
473                 tmp=tl->next_tl;
474                 free_cell((struct cell*)tl->payload);
475                 tl=tmp;
476         }
477         
478 }
479
480 struct timer_table *tm_init_timers()
481 {
482         enum lists i;
483
484         timertable=(struct timer_table *) shm_malloc(sizeof(struct timer_table));
485         if (!timertable) {
486                 LOG(L_ERR, "ERROR: tm_init_timers: no shmem for timer_Table\n");
487                 goto error0;
488         }
489         memset(timertable, 0, sizeof (struct timer_table));
490                 
491
492         /* inits the timers*/
493         for(  i=0 ; i<NR_OF_TIMER_LISTS ; i++ )
494         init_timer_list( i );
495     
496     /* init. timer lists */
497         timertable->timers[RT_T1_TO_1].id = RT_T1_TO_1;
498         timertable->timers[RT_T1_TO_2].id = RT_T1_TO_2;
499         timertable->timers[RT_T1_TO_3].id = RT_T1_TO_3;
500         timertable->timers[RT_T2].id      = RT_T2;
501         timertable->timers[FR_TIMER_LIST].id     = FR_TIMER_LIST; 
502         timertable->timers[FR_INV_TIMER_LIST].id = FR_INV_TIMER_LIST;
503         timertable->timers[WT_TIMER_LIST].id     = WT_TIMER_LIST;
504         timertable->timers[DELETE_LIST].id       = DELETE_LIST;
505
506         return timertable;
507
508 error0:
509         return 0;
510 }
511
512 void free_timer_table()
513 {
514         enum lists i;
515
516         if (timertable) {
517                 /* the mutexs for sync the lists are released*/
518                 for ( i=0 ; i<NR_OF_TIMER_LISTS ; i++ )
519                         release_timerlist_lock( &timertable->timers[i] );
520                 shm_free(timertable);
521         }
522                 
523 }
524
525 void reset_timer_list( enum lists list_id)
526 {
527         timertable->timers[list_id].first_tl.next_tl =
528                 &(timertable->timers[list_id].last_tl );
529         timertable->timers[list_id].last_tl.prev_tl =
530                 &(timertable->timers[list_id].first_tl );
531         timertable->timers[list_id].first_tl.prev_tl =
532                 timertable->timers[list_id].last_tl.next_tl = NULL;
533         timertable->timers[list_id].last_tl.time_out = -1;
534 }
535
536
537
538
539 void init_timer_list( /* struct s_table* ht, */ enum lists list_id)
540 {
541         reset_timer_list( /* ht, */ list_id );
542         init_timerlist_lock( /* ht, */ list_id );
543 }
544
545
546
547
548 void print_timer_list( enum lists list_id)
549 {
550         struct timer* timer_list=&(timertable->timers[ list_id ]);
551         struct timer_link *tl ;
552
553         tl = timer_list->first_tl.next_tl;
554         while (tl!=& timer_list->last_tl)
555         {
556                 DBG("DEBUG: print_timer_list[%d]: %p, next=%p \n",
557                         list_id, tl, tl->next_tl);
558                 tl = tl->next_tl;
559         }
560 }
561
562
563
564
565 void remove_timer_unsafe(  struct timer_link* tl )
566 {
567 #ifdef EXTRA_DEBUG
568         if (tl && tl->timer_list &&
569                 tl->timer_list->last_tl.prev_tl==0) {
570                 LOG( L_CRIT,
571                 "CRITICAL : Oh no, zero link in trailing timer element\n");
572                 abort();
573         };
574 #endif
575         if (is_in_timer_list2( tl )) {
576 #ifdef EXTRA_DEBUG
577                 DBG("DEBUG: unlinking timer: tl=%p, timeout=%d, group=%d\n", 
578                         tl, tl->time_out, tl->tg);
579 #endif
580                 tl->prev_tl->next_tl = tl->next_tl;
581                 tl->next_tl->prev_tl = tl->prev_tl;
582                 tl->next_tl = 0;
583                 tl->prev_tl = 0;
584                 tl->timer_list = NULL;
585         }
586 }
587
588
589
590
591 /* put a new cell into a list nr. list_id */
592 void add_timer_unsafe( struct timer *timer_list, struct timer_link *tl,
593         unsigned int time_out )
594 {
595 #ifdef EXTRA_DEBUG
596         if (timer_list->last_tl.prev_tl==0) {
597         LOG( L_CRIT,
598                 "CRITICAL : Oh no, zero link in trailing timer element\n");
599                 abort();
600         };
601 #endif
602
603         tl->time_out = time_out;
604         tl->prev_tl = timer_list->last_tl.prev_tl;
605         tl->next_tl = & timer_list->last_tl;
606         timer_list->last_tl.prev_tl = tl;
607         tl->prev_tl->next_tl = tl;
608         tl->timer_list = timer_list;
609 #ifdef EXTRA_DEBUG
610         if ( tl->tg != timer_group[ timer_list->id ] ) {
611                 LOG( L_CRIT, "CRITICAL error: changing timer group\n");
612                 abort();
613         }
614 #endif
615         DBG("DEBUG: add_to_tail_of_timer[%d]: %p\n",timer_list->id,tl);
616 }
617
618
619
620
621 /* detach items passed by the time from timer list */
622 struct timer_link  *check_and_split_time_list( struct timer *timer_list,
623         int time )
624 {
625         struct timer_link *tl , *end, *ret;
626
627
628         /* quick check whether it is worth entering the lock */
629         if (timer_list->first_tl.next_tl==&timer_list->last_tl 
630                         || ( /* timer_list->first_tl.next_tl
631                                 && */ timer_list->first_tl.next_tl->time_out > time) )
632                 return NULL;
633
634         /* the entire timer list is locked now -- noone else can manipulate it */
635         lock(timer_list->mutex);
636
637         end = &timer_list->last_tl;
638         tl = timer_list->first_tl.next_tl;
639         while( tl!=end && tl->time_out <= time) {
640                 tl->timer_list = NULL;
641                 tl=tl->next_tl;
642         }
643
644         /* nothing to delete found */
645         if (tl->prev_tl==&(timer_list->first_tl)) {
646                 ret = NULL;
647         } else { /* we did find timers to be fired! */
648                 /* the detached list begins with current beginning */
649                 ret = timer_list->first_tl.next_tl;
650                 /* and we mark the end of the split list */
651                 tl->prev_tl->next_tl = NULL;
652                 /* the shortened list starts from where we suspended */
653                 timer_list->first_tl.next_tl = tl;      
654                 tl->prev_tl = & timer_list->first_tl;
655         }
656 #ifdef EXTRA_DEBUG
657         if (timer_list->last_tl.prev_tl==0) {
658                 LOG( L_CRIT,
659                 "CRITICAL : Oh no, zero link in trailing timer element\n");
660                 abort();
661         };
662 #endif
663         /* give the list lock away */
664         unlock(timer_list->mutex);
665
666         return ret;
667 }
668
669
670
671 /* stop timer */
672 void reset_timer( struct timer_link* tl )
673 {
674         /* disqualify this timer from execution by setting its time_out
675            to zero; it will stay in timer-list until the timer process
676            starts removing outdated elements; then it will remove it
677            but not execute; there is a race condition, though -- see
678            timer.c for more details
679         */
680         tl->time_out = TIMER_DELETED;
681 #ifdef EXTRA_DEBUG
682         DBG("DEBUG: reset_timer (group %d, tl=%p)\n", tl->tg, tl );
683 #endif
684 #ifdef _OBSOLETED
685         /* lock(timer_group_lock[ tl->tg ]); */
686         /* hack to work arround this timer group thing*/
687         lock(hash__XX_table->timers[timer_group[tl->tg]].mutex);
688         remove_timer_unsafe( tl );
689         unlock(hash_XX_table->timers[timer_group[tl->tg]].mutex);
690         /*unlock(timer_group_lock[ tl->tg ]);*/
691 #endif
692 }
693
694
695
696
697 /* determine timer length and put on a correct timer list */
698 void set_timer( struct timer_link *new_tl, enum lists list_id )
699 {
700         unsigned int timeout;
701         struct timer* list;
702
703
704         if (list_id<FR_TIMER_LIST || list_id>=NR_OF_TIMER_LISTS) {
705                 LOG(L_CRIT, "ERROR: set_timer: unkown list: %d\n", list_id);
706 #ifdef EXTRA_DEBUG
707                 abort();
708 #endif
709                 return;
710         }
711         timeout = timer_id2timeout[ list_id ];
712         list= &(timertable->timers[ list_id ]);
713
714         lock(list->mutex);
715         /* make sure I'm not already on a list */
716         remove_timer_unsafe( new_tl );
717         add_timer_unsafe( list, new_tl, get_ticks()+timeout);
718         unlock(list->mutex);
719 }
720
721 /* similar to set_timer, except it allows only one-time
722    timer setting and all later attempts are ignored */
723 void set_1timer( struct timer_link *new_tl, enum lists list_id )
724 {
725         unsigned int timeout;
726         struct timer* list;
727
728
729         if (list_id<FR_TIMER_LIST || list_id>=NR_OF_TIMER_LISTS) {
730                 LOG(L_CRIT, "ERROR: set_timer: unkown list: %d\n", list_id);
731 #ifdef EXTRA_DEBUG
732                 abort();
733 #endif
734                 return;
735         }
736         timeout = timer_id2timeout[ list_id ];
737         list= &(timertable->timers[ list_id ]);
738
739         lock(list->mutex);
740         if (!(new_tl->time_out>TIMER_DELETED)) {
741                 /* make sure I'm not already on a list */
742                 /* remove_timer_unsafe( new_tl ); */
743                 add_timer_unsafe( list, new_tl, get_ticks()+timeout);
744
745                 /* set_1timer is used only by WAIT -- that's why we can
746                    afford updating wait statistics; I admit its not nice
747                    but it greatly utilizes existing lock 
748                 */
749                 cur_stats->waiting++;acc_stats->waiting++;
750         }
751         unlock(list->mutex);
752 }
753
754
755 void unlink_timers( struct cell *t )
756 {
757         int i;
758         int remove_fr, remove_retr;
759
760         remove_fr=0; remove_retr=0;
761
762         /* first look if we need to remove timers and play with
763            costly locks at all
764
765             note that is_in_timer_list2 is unsafe but it does not
766             hurt -- transaction is already dead (wait state) so that
767             noone else will install a FR/RETR timer and it can only
768             be removed from timer process itself -> it is safe to
769             use it without any protection
770         */
771         if (is_in_timer_list2(&t->uas.response.fr_timer)) remove_fr=1; 
772         else for (i=0; i<t->nr_of_outgoings; i++)
773                 if (is_in_timer_list2(&t->uac[i].request.fr_timer)
774                         || is_in_timer_list2(&t->uac[i].local_cancel.fr_timer)) {
775                                 remove_fr=1;
776                                 break;
777                 }
778         if (is_in_timer_list2(&t->uas.response.retr_timer)) remove_retr=1; 
779         else for (i=0; i<t->nr_of_outgoings; i++)
780                 if (is_in_timer_list2(&t->uac[i].request.retr_timer)
781                         || is_in_timer_list2(&t->uac[i].local_cancel.retr_timer)) {
782                                 remove_retr=1;
783                                 break;
784                 }
785
786         /* do what we have to do....*/
787         if (remove_retr) {
788                 /* RT_T1 lock is shared by all other RT timer
789                    lists -- we can safely lock just one
790                 */
791                 lock(timertable->timers[RT_T1_TO_1].mutex);
792                 remove_timer_unsafe(&t->uas.response.retr_timer);
793                 for (i=0; i<t->nr_of_outgoings; i++) {
794                         remove_timer_unsafe(&t->uac[i].request.retr_timer);
795                         remove_timer_unsafe(&t->uac[i].local_cancel.retr_timer);
796                 }
797                 unlock(timertable->timers[RT_T1_TO_1].mutex);
798         }
799         if (remove_fr) {
800                 /* FR lock is shared by all other FR timer
801                    lists -- we can safely lock just one
802                 */
803                 lock(timertable->timers[FR_TIMER_LIST].mutex);
804                 remove_timer_unsafe(&t->uas.response.fr_timer);
805                 for (i=0; i<t->nr_of_outgoings; i++) {
806                         remove_timer_unsafe(&t->uac[i].request.fr_timer);
807                         remove_timer_unsafe(&t->uac[i].local_cancel.fr_timer);
808                 }
809                 unlock(timertable->timers[FR_TIMER_LIST].mutex);
810         }
811 }
812
813
814
815
816 #define run_handler_for_each( _tl , _handler ) \
817         while ((_tl))\
818         {\
819                 /* reset the timer list linkage */\
820                 tmp_tl = (_tl)->next_tl;\
821                 (_tl)->next_tl = (_tl)->prev_tl = 0;\
822                 DBG("DEBUG: timer routine:%d,tl=%p next=%p\n",\
823                         id,(_tl),tmp_tl);\
824                 if ((_tl)->time_out>TIMER_DELETED) \
825                         (_handler)( (_tl)->payload );\
826                 (_tl) = tmp_tl;\
827         }
828
829
830
831
832 void timer_routine(unsigned int ticks , void * attr)
833 {
834         /* struct timer_table *tt= (struct timer_table*)attr; */
835         struct timer_link *tl, *tmp_tl;
836         int                id;
837
838 #ifdef BOGDAN_TRIFLE
839         DBG(" %d \n",ticks);
840 #endif
841
842         for( id=0 ; id<NR_OF_TIMER_LISTS ; id++ )
843         {
844                 /* to waste as little time in lock as possible, detach list
845                    with expired items and process them after leaving the lock */
846                 tl=check_and_split_time_list( &timertable->timers[ id ], ticks);
847                 /* process items now */
848                 switch (id)
849                 {
850                         case FR_TIMER_LIST:
851                         case FR_INV_TIMER_LIST:
852                                 run_handler_for_each(tl,final_response_handler);
853                                 break;
854                         case RT_T1_TO_1:
855                         case RT_T1_TO_2:
856                         case RT_T1_TO_3:
857                         case RT_T2:
858                                 run_handler_for_each(tl,retransmission_handler);
859                                 break;
860                         case WT_TIMER_LIST:
861                                 run_handler_for_each(tl,wait_handler);
862                                 break;
863                         case DELETE_LIST:
864                                 run_handler_for_each(tl,delete_handler);
865                                 break;
866                 }
867         }
868 }
869