xref: /iperf/src/timer.c (revision ba7b91d2)
1 /*
2  * Copyright (c) 2009-2011, The Regents of the University of California,
3  * through Lawrence Berkeley National Laboratory (subject to receipt of any
4  * required approvals from the U.S. Dept. of Energy).  All rights reserved.
5  *
6  * This code is distributed under a BSD style license, see the LICENSE file
7  * for complete information.
8  *
9  * Based on timers.c by Jef Poskanzer. Used with permission.
10  */
11 
12 #include <sys/types.h>
13 #include <stdlib.h>
14 
15 #include "timer.h"
16 
17 
18 static Timer* timers = NULL;
19 static Timer* free_timers = NULL;
20 
21 TimerClientData JunkClientData;
22 
23 
24 
25 /* This is an efficiency tweak.  All the routines that need to know the
26 ** current time get passed a pointer to a struct timeval.  If it's non-NULL
27 ** it gets used, otherwise we do our own gettimeofday() to fill it in.
28 ** This lets the caller avoid extraneous gettimeofday()s when efficiency
29 ** is needed, and not bother with the extra code when efficiency doesn't
30 ** matter too much.
31 */
32 static void
33 getnow( struct timeval* nowP, struct timeval* nowP2 )
34 {
35     if ( nowP != NULL )
36 	*nowP2 = *nowP;
37     else
38 	(void) gettimeofday( nowP2, NULL );
39 }
40 
41 
42 static void
43 list_add( Timer* t )
44 {
45     Timer* t2;
46     Timer* t2prev;
47 
48     if ( timers == NULL ) {
49 	/* The list is empty. */
50 	timers = t;
51 	t->prev = t->next = NULL;
52     } else {
53 	if ( t->time.tv_sec < timers->time.tv_sec ||
54 	     ( t->time.tv_sec == timers->time.tv_sec &&
55 	       t->time.tv_usec < timers->time.tv_usec ) ) {
56 	    /* The new timer goes at the head of the list. */
57 	    t->prev = NULL;
58 	    t->next = timers;
59 	    timers->prev = t;
60 	    timers = t;
61 	} else {
62 	    /* Walk the list to find the insertion point. */
63 	    for ( t2prev = timers, t2 = timers->next; t2 != NULL;
64 		  t2prev = t2, t2 = t2->next ) {
65 		if ( t->time.tv_sec < t2->time.tv_sec ||
66 		     ( t->time.tv_sec == t2->time.tv_sec &&
67 		       t->time.tv_usec < t2->time.tv_usec ) ) {
68 		    /* Found it. */
69 		    t2prev->next = t;
70 		    t->prev = t2prev;
71 		    t->next = t2;
72 		    t2->prev = t;
73 		    return;
74 		}
75 	    }
76 	    /* Oops, got to the end of the list.  Add to tail. */
77 	    t2prev->next = t;
78 	    t->prev = t2prev;
79 	    t->next = NULL;
80 	}
81     }
82 }
83 
84 
85 static void
86 list_remove( Timer* t )
87 {
88     if ( t->prev == NULL )
89 	timers = t->next;
90     else
91 	t->prev->next = t->next;
92     if ( t->next != NULL )
93 	t->next->prev = t->prev;
94 }
95 
96 
97 static void
98 list_resort( Timer* t )
99 {
100     /* Remove the timer from the list. */
101     list_remove( t );
102     /* And add it back in, sorted correctly. */
103     list_add( t );
104 }
105 
106 
107 static void
108 add_usecs( struct timeval* t, int64_t usecs )
109 {
110     t->tv_sec += usecs / 1000000L;
111     t->tv_usec += usecs % 1000000L;
112     if ( t->tv_usec >= 1000000L ) {
113 	t->tv_sec += t->tv_usec / 1000000L;
114 	t->tv_usec %= 1000000L;
115     }
116 }
117 
118 
119 Timer*
120 tmr_create(
121     struct timeval* nowP, TimerProc* timer_proc, TimerClientData client_data,
122     int64_t usecs, int periodic )
123 {
124     struct timeval now;
125     Timer* t;
126 
127     getnow( nowP, &now );
128 
129     if ( free_timers != NULL ) {
130 	t = free_timers;
131 	free_timers = t->next;
132     } else {
133 	t = (Timer*) malloc( sizeof(Timer) );
134 	if ( t == NULL )
135 	    return NULL;
136     }
137 
138     t->timer_proc = timer_proc;
139     t->client_data = client_data;
140     t->usecs = usecs;
141     t->periodic = periodic;
142     t->time = now;
143     add_usecs( &t->time, usecs );
144     /* Add the new timer to the active list. */
145     list_add( t );
146 
147     return t;
148 }
149 
150 
151 struct timeval*
152 tmr_timeout( struct timeval* nowP )
153 {
154     struct timeval now;
155     int64_t usecs;
156     static struct timeval timeout;
157 
158     getnow( nowP, &now );
159     /* Since the list is sorted, we only need to look at the first timer. */
160     if ( timers == NULL )
161 	return NULL;
162     usecs = ( timers->time.tv_sec - now.tv_sec ) * 1000000LL +
163 	    ( timers->time.tv_usec - now.tv_usec );
164     if ( usecs <= 0 )
165 	usecs = 0;
166     timeout.tv_sec = usecs / 1000000LL;
167     timeout.tv_usec = usecs % 1000000LL;
168     return &timeout;
169 }
170 
171 
172 void
173 tmr_run( struct timeval* nowP )
174 {
175     struct timeval now;
176     Timer* t;
177     Timer* next;
178 
179     getnow( nowP, &now );
180     for ( t = timers; t != NULL; t = next ) {
181 	next = t->next;
182 	/* Since the list is sorted, as soon as we find a timer
183 	** that isn't ready yet, we are done.
184 	*/
185 	if ( t->time.tv_sec > now.tv_sec ||
186 	     ( t->time.tv_sec == now.tv_sec &&
187 	       t->time.tv_usec > now.tv_usec ) )
188 	    break;
189 	(t->timer_proc)( t->client_data, &now );
190 	if ( t->periodic ) {
191 	    /* Reschedule. */
192 	    add_usecs( &t->time, t->usecs );
193 	    list_resort( t );
194 	} else
195 	    tmr_cancel( t );
196     }
197 }
198 
199 
200 void
201 tmr_reset( struct timeval* nowP, Timer* t )
202 {
203     struct timeval now;
204 
205     getnow( nowP, &now );
206     t->time = now;
207     add_usecs( &t->time, t->usecs );
208     list_resort( t );
209 }
210 
211 
212 void
213 tmr_cancel( Timer* t )
214 {
215     /* Remove it from the active list. */
216     list_remove( t );
217     /* And put it on the free list. */
218     t->next = free_timers;
219     free_timers = t;
220     t->prev = NULL;
221 }
222 
223 
224 void
225 tmr_cleanup( void )
226 {
227     Timer* t;
228 
229     while ( free_timers != NULL ) {
230 	t = free_timers;
231 	free_timers = t->next;
232 	free( (void*) t );
233     }
234 }
235 
236 
237 void
238 tmr_destroy( void )
239 {
240     while ( timers != NULL )
241 	tmr_cancel( timers );
242     tmr_cleanup();
243 }
244