One bit of an ASCII character can make a big difference.
[people/andreif/gpxe.git] / src / net / retry.c
1 /*
2  * Copyright (C) 2006 Michael Brown <mbrown@fensystems.co.uk>.
3  *
4  * This program is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU General Public License as
6  * published by the Free Software Foundation; either version 2 of the
7  * License, or any later version.
8  *
9  * This program is distributed in the hope that it will be useful, but
10  * WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12  * General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License
15  * along with this program; if not, write to the Free Software
16  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
17  */
18
19 #include <stddef.h>
20 #include <latch.h>
21 #include <gpxe/list.h>
22 #include <gpxe/process.h>
23 #include <gpxe/init.h>
24 #include <gpxe/retry.h>
25
26 /** @file
27  *
28  * Retry timers
29  *
30  * A retry timer is a truncated binary exponential backoff timer.  It
31  * can be used to build automatic retransmission into network
32  * protocols.
33  */
34
35 /** List of running timers */
36 static LIST_HEAD ( timers );
37
38 /**
39  * Reload timer
40  *
41  * @v timer             Retry timer
42  *
43  * This reloads the timer with a new expiry time.  The expiry time
44  * will be the timer's base timeout value, shifted left by the number
45  * of retries (i.e. the number of timer expiries since the last timer
46  * reset).
47  */
48 static void reload_timer ( struct retry_timer *timer ) {
49         unsigned int exp;
50
51         exp = timer->retries;
52         if ( exp > BACKOFF_LIMIT )
53                 exp = BACKOFF_LIMIT;
54         timer->expiry = currticks() + ( timer->base << exp );
55 }
56
57 /**
58  * Reset timer
59  *
60  * @v timer             Retry timer
61  *
62  * This resets the timer, i.e. clears its retry count and starts it
63  * running with its base timeout value.
64  *
65  * Note that it is explicitly permitted to call reset_timer() on an
66  * inactive timer.
67  */
68 void reset_timer ( struct retry_timer *timer ) {
69         timer->retries = 0;
70         reload_timer ( timer );
71 }
72
73 /**
74  * Start timer
75  *
76  * @v timer             Retry timer
77  *
78  * This resets the timer and starts it running (i.e. adds it to the
79  * list of running timers).  The retry_timer::base and
80  * retry_timer::callback fields must have been filled in.
81  */
82 void start_timer ( struct retry_timer *timer ) {
83         list_add ( &timer->list, &timers );
84         reset_timer ( timer );
85 }
86
87 /**
88  * Stop timer
89  *
90  * @v timer             Retry timer
91  *
92  * This stops the timer (i.e. removes it from the list of running
93  * timers).
94  */
95 void stop_timer ( struct retry_timer *timer ) {
96         list_del ( &timer->list );
97 }
98
99 /**
100  * Single-step the retry timer list
101  *
102  * @v process           Retry timer process
103  */
104 static void retry_step ( struct process *process ) {
105         struct retry_timer *timer;
106         struct retry_timer *tmp;
107         unsigned long now = currticks();
108
109         list_for_each_entry_safe ( timer, tmp, &timers, list ) {
110                 if ( timer->expiry <= now ) {
111                         timer->retries++;
112                         reload_timer ( timer );
113                         timer->expired ( timer );
114                 }
115         }
116
117         schedule ( process );
118 }
119
120 /** Retry timer process */
121 static struct process retry_process = {
122         .step = retry_step,
123 };
124
125 /** Initialise the retry timer module */
126 static void init_retry ( void ) {
127         schedule ( &retry_process );
128 }
129
130 INIT_FN ( INIT_PROCESS, init_retry, NULL, NULL );