Print this page
4823 don't open-code NSEC2MSEC and MSEC2NSEC
Split |
Close |
Expand all |
Collapse all |
--- old/usr/src/lib/libinetutil/common/tq.c
+++ new/usr/src/lib/libinetutil/common/tq.c
1 1 /*
2 2 * CDDL HEADER START
3 3 *
4 4 * The contents of this file are subject to the terms of the
5 5 * Common Development and Distribution License (the "License").
6 6 * You may not use this file except in compliance with the License.
7 7 *
8 8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9 9 * or http://www.opensolaris.org/os/licensing.
10 10 * See the License for the specific language governing permissions
11 11 * and limitations under the License.
12 12 *
13 13 * When distributing Covered Code, include this CDDL HEADER in each
14 14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15 15 * If applicable, add the following below this CDDL HEADER, with the
16 16 * fields enclosed by brackets "[]" replaced with your own identifying
17 17 * information: Portions Copyright [yyyy] [name of copyright owner]
18 18 *
19 19 * CDDL HEADER END
20 20 */
21 21 /*
22 22 * Copyright 2009 Sun Microsystems, Inc. All rights reserved.
23 23 * Use is subject to license terms.
24 24 */
25 25
26 26 #include <stdlib.h>
27 27 #include <limits.h>
28 28 #include <sys/time.h>
29 29 #include <sys/types.h>
30 30 #include <sys/sysmacros.h>
31 31 #include <sys/stropts.h> /* INFTIM */
32 32
33 33 #include <libinetutil.h>
34 34 #include "libinetutil_impl.h"
35 35
36 36 static iu_timer_node_t *pending_delete_chain = NULL;
37 37
38 38 static void destroy_timer(iu_tq_t *, iu_timer_node_t *);
39 39 static iu_timer_id_t get_timer_id(iu_tq_t *);
40 40 static void release_timer_id(iu_tq_t *, iu_timer_id_t);
41 41
42 42 /*
43 43 * iu_tq_create(): creates, initializes and returns a timer queue for use
44 44 *
45 45 * input: void
46 46 * output: iu_tq_t *: the new timer queue
47 47 */
48 48
49 49 iu_tq_t *
50 50 iu_tq_create(void)
51 51 {
52 52 return (calloc(1, sizeof (iu_tq_t)));
53 53 }
54 54
55 55 /*
56 56 * iu_tq_destroy(): destroys an existing timer queue
57 57 *
58 58 * input: iu_tq_t *: the timer queue to destroy
59 59 * output: void
60 60 */
61 61
62 62 void
63 63 iu_tq_destroy(iu_tq_t *tq)
64 64 {
65 65 iu_timer_node_t *node, *next_node;
66 66
67 67 for (node = tq->iutq_head; node != NULL; node = next_node) {
68 68 next_node = node->iutn_next;
69 69 destroy_timer(tq, node);
70 70 }
71 71
72 72 free(tq);
73 73 }
74 74
75 75 /*
76 76 * insert_timer(): inserts a timer node into a tq's timer list
77 77 *
78 78 * input: iu_tq_t *: the timer queue
79 79 * iu_timer_node_t *: the timer node to insert into the list
80 80 * uint64_t: the number of milliseconds before this timer fires
81 81 * output: void
82 82 */
83 83
84 84 static void
85 85 insert_timer(iu_tq_t *tq, iu_timer_node_t *node, uint64_t msec)
86 86 {
87 87 iu_timer_node_t *after = NULL;
↓ open down ↓ |
87 lines elided |
↑ open up ↑ |
88 88
89 89 /*
90 90 * find the node to insert this new node "after". we do this
91 91 * instead of the more intuitive "insert before" because with
92 92 * the insert before approach, a null `before' node pointer
93 93 * is overloaded in meaning (it could be null because there
94 94 * are no items in the list, or it could be null because this
95 95 * is the last item on the list, which are very different cases).
96 96 */
97 97
98 - node->iutn_abs_timeout = gethrtime() + (msec * (NANOSEC / MILLISEC));
98 + node->iutn_abs_timeout = gethrtime() + MSEC2NSEC(msec);
99 99
100 100 if (tq->iutq_head != NULL &&
101 101 tq->iutq_head->iutn_abs_timeout < node->iutn_abs_timeout)
102 102 for (after = tq->iutq_head; after->iutn_next != NULL;
103 103 after = after->iutn_next)
104 104 if (after->iutn_next->iutn_abs_timeout >
105 105 node->iutn_abs_timeout)
106 106 break;
107 107
108 108 node->iutn_next = after ? after->iutn_next : tq->iutq_head;
109 109 node->iutn_prev = after;
110 110 if (after == NULL)
111 111 tq->iutq_head = node;
112 112 else
113 113 after->iutn_next = node;
114 114
115 115 if (node->iutn_next != NULL)
116 116 node->iutn_next->iutn_prev = node;
117 117 }
118 118
119 119 /*
120 120 * remove_timer(): removes a timer node from the tq's timer list
121 121 *
122 122 * input: iu_tq_t *: the timer queue
123 123 * iu_timer_node_t *: the timer node to remove from the list
124 124 * output: void
125 125 */
126 126
127 127 static void
128 128 remove_timer(iu_tq_t *tq, iu_timer_node_t *node)
129 129 {
130 130 if (node->iutn_next != NULL)
131 131 node->iutn_next->iutn_prev = node->iutn_prev;
132 132 if (node->iutn_prev != NULL)
133 133 node->iutn_prev->iutn_next = node->iutn_next;
134 134 else
135 135 tq->iutq_head = node->iutn_next;
136 136 }
137 137
138 138 /*
139 139 * destroy_timer(): destroy a timer node
140 140 *
141 141 * input: iu_tq_t *: the timer queue the timer node is associated with
142 142 * iu_timer_node_t *: the node to free
143 143 * output: void
144 144 */
145 145
146 146 static void
147 147 destroy_timer(iu_tq_t *tq, iu_timer_node_t *node)
148 148 {
149 149 release_timer_id(tq, node->iutn_timer_id);
150 150
151 151 /*
152 152 * if we're in expire, don't delete the node yet, since it may
153 153 * still be referencing it (through the expire_next pointers)
154 154 */
155 155
156 156 if (tq->iutq_in_expire) {
157 157 node->iutn_pending_delete++;
158 158 node->iutn_next = pending_delete_chain;
159 159 pending_delete_chain = node;
160 160 } else
161 161 free(node);
162 162
163 163 }
164 164
165 165 /*
166 166 * iu_schedule_timer(): creates and inserts a timer in the tq's timer list
167 167 *
168 168 * input: iu_tq_t *: the timer queue
169 169 * uint32_t: the number of seconds before this timer fires
170 170 * iu_tq_callback_t *: the function to call when the timer fires
171 171 * void *: an argument to pass to the called back function
172 172 * output: iu_timer_id_t: the new timer's timer id on success, -1 on failure
173 173 */
174 174
175 175 iu_timer_id_t
176 176 iu_schedule_timer(iu_tq_t *tq, uint32_t sec, iu_tq_callback_t *callback,
177 177 void *arg)
178 178 {
179 179 return (iu_schedule_timer_ms(tq, sec * MILLISEC, callback, arg));
180 180 }
181 181
182 182 /*
183 183 * iu_schedule_ms_timer(): creates and inserts a timer in the tq's timer list,
184 184 * using millisecond granularity
185 185 *
186 186 * input: iu_tq_t *: the timer queue
187 187 * uint64_t: the number of milliseconds before this timer fires
188 188 * iu_tq_callback_t *: the function to call when the timer fires
189 189 * void *: an argument to pass to the called back function
190 190 * output: iu_timer_id_t: the new timer's timer id on success, -1 on failure
191 191 */
192 192 iu_timer_id_t
193 193 iu_schedule_timer_ms(iu_tq_t *tq, uint64_t ms, iu_tq_callback_t *callback,
194 194 void *arg)
195 195 {
196 196 iu_timer_node_t *node = calloc(1, sizeof (iu_timer_node_t));
197 197
198 198 if (node == NULL)
199 199 return (-1);
200 200
201 201 node->iutn_callback = callback;
202 202 node->iutn_arg = arg;
203 203 node->iutn_timer_id = get_timer_id(tq);
204 204 if (node->iutn_timer_id == -1) {
205 205 free(node);
206 206 return (-1);
207 207 }
208 208
209 209 insert_timer(tq, node, ms);
210 210
211 211 return (node->iutn_timer_id);
212 212 }
213 213
214 214 /*
215 215 * iu_cancel_timer(): cancels a pending timer from a timer queue's timer list
216 216 *
217 217 * input: iu_tq_t *: the timer queue
218 218 * iu_timer_id_t: the timer id returned from iu_schedule_timer
219 219 * void **: if non-NULL, a place to return the argument passed to
220 220 * iu_schedule_timer
221 221 * output: int: 1 on success, 0 on failure
222 222 */
223 223
224 224 int
225 225 iu_cancel_timer(iu_tq_t *tq, iu_timer_id_t timer_id, void **arg)
226 226 {
227 227 iu_timer_node_t *node;
228 228
229 229 if (timer_id == -1)
230 230 return (0);
231 231
232 232 for (node = tq->iutq_head; node != NULL; node = node->iutn_next) {
233 233 if (node->iutn_timer_id == timer_id) {
234 234 if (arg != NULL)
235 235 *arg = node->iutn_arg;
236 236 remove_timer(tq, node);
237 237 destroy_timer(tq, node);
238 238 return (1);
239 239 }
240 240 }
241 241 return (0);
242 242 }
243 243
244 244 /*
245 245 * iu_adjust_timer(): adjusts the fire time of a timer in the tq's timer list
246 246 *
247 247 * input: iu_tq_t *: the timer queue
248 248 * iu_timer_id_t: the timer id returned from iu_schedule_timer
249 249 * uint32_t: the number of seconds before this timer fires
250 250 * output: int: 1 on success, 0 on failure
251 251 */
252 252
253 253 int
254 254 iu_adjust_timer(iu_tq_t *tq, iu_timer_id_t timer_id, uint32_t sec)
255 255 {
256 256 iu_timer_node_t *node;
257 257
258 258 if (timer_id == -1)
259 259 return (0);
260 260
261 261 for (node = tq->iutq_head; node != NULL; node = node->iutn_next) {
262 262 if (node->iutn_timer_id == timer_id) {
263 263 remove_timer(tq, node);
264 264 insert_timer(tq, node, sec * MILLISEC);
265 265 return (1);
266 266 }
267 267 }
268 268 return (0);
269 269 }
270 270
271 271 /*
272 272 * iu_earliest_timer(): returns the time until the next timer fires on a tq
273 273 *
274 274 * input: iu_tq_t *: the timer queue
275 275 * output: int: the number of milliseconds until the next timer (up to
276 276 * a maximum value of INT_MAX), or INFTIM if no timers are pending.
277 277 */
278 278
279 279 int
280 280 iu_earliest_timer(iu_tq_t *tq)
281 281 {
282 282 unsigned long long timeout_interval;
283 283 hrtime_t current_time = gethrtime();
284 284
285 285 if (tq->iutq_head == NULL)
286 286 return (INFTIM);
287 287
288 288 /*
289 289 * event might've already happened if we haven't gotten a chance to
290 290 * run in a while; return zero and pretend it just expired.
291 291 */
292 292
293 293 if (tq->iutq_head->iutn_abs_timeout <= current_time)
294 294 return (0);
295 295
296 296 /*
297 297 * since the timers are ordered in absolute time-to-fire, just
298 298 * subtract from the head of the list.
299 299 */
300 300
301 301 timeout_interval =
302 302 (tq->iutq_head->iutn_abs_timeout - current_time) / 1000000;
303 303
304 304 return (MIN(timeout_interval, INT_MAX));
305 305 }
306 306
307 307 /*
308 308 * iu_expire_timers(): expires all pending timers on a given timer queue
309 309 *
310 310 * input: iu_tq_t *: the timer queue
311 311 * output: int: the number of timers expired
312 312 */
313 313
314 314 int
315 315 iu_expire_timers(iu_tq_t *tq)
316 316 {
317 317 iu_timer_node_t *node, *next_node;
318 318 int n_expired = 0;
319 319 hrtime_t current_time = gethrtime();
320 320
321 321 /*
322 322 * in_expire is in the iu_tq_t instead of being passed through as
323 323 * an argument to remove_timer() below since the callback
324 324 * function may call iu_cancel_timer() itself as well.
325 325 */
326 326
327 327 tq->iutq_in_expire++;
328 328
329 329 /*
330 330 * this function builds another linked list of timer nodes
331 331 * through `expire_next' because the normal linked list
332 332 * may be changed as a result of callbacks canceling and
333 333 * scheduling timeouts, and thus can't be trusted.
334 334 */
335 335
336 336 for (node = tq->iutq_head; node != NULL; node = node->iutn_next)
337 337 node->iutn_expire_next = node->iutn_next;
338 338
339 339 for (node = tq->iutq_head; node != NULL;
340 340 node = node->iutn_expire_next) {
341 341
342 342 /*
343 343 * If the timeout is within 1 millisec of current time,
344 344 * consider it as expired already. We do this because
345 345 * iu_earliest_timer() only has millisec granularity.
346 346 * So we should also use millisec grandularity in
347 347 * comparing timeout values.
348 348 */
349 349 if (node->iutn_abs_timeout - current_time > 1000000)
350 350 break;
351 351
352 352 /*
353 353 * fringe condition: two timers fire at the "same
354 354 * time" (i.e., they're both scheduled called back in
355 355 * this loop) and one cancels the other. in this
356 356 * case, the timer which has already been "cancelled"
357 357 * should not be called back.
358 358 */
359 359
360 360 if (node->iutn_pending_delete)
361 361 continue;
362 362
363 363 /*
364 364 * we remove the timer before calling back the callback
365 365 * so that a callback which accidentally tries to cancel
366 366 * itself (through whatever means) doesn't succeed.
367 367 */
368 368
369 369 n_expired++;
370 370 remove_timer(tq, node);
371 371 destroy_timer(tq, node);
372 372 node->iutn_callback(tq, node->iutn_arg);
373 373 }
374 374
375 375 tq->iutq_in_expire--;
376 376
377 377 /*
378 378 * any cancels that took place whilst we were expiring timeouts
379 379 * ended up on the `pending_delete_chain'. delete them now
380 380 * that it's safe.
381 381 */
382 382
383 383 for (node = pending_delete_chain; node != NULL; node = next_node) {
384 384 next_node = node->iutn_next;
385 385 free(node);
386 386 }
387 387 pending_delete_chain = NULL;
388 388
389 389 return (n_expired);
390 390 }
391 391
392 392 /*
393 393 * get_timer_id(): allocates a timer id from the pool
394 394 *
395 395 * input: iu_tq_t *: the timer queue
396 396 * output: iu_timer_id_t: the allocated timer id, or -1 if none available
397 397 */
398 398
399 399 static iu_timer_id_t
400 400 get_timer_id(iu_tq_t *tq)
401 401 {
402 402 unsigned int map_index;
403 403 unsigned char map_bit;
404 404 boolean_t have_wrapped = B_FALSE;
405 405
406 406 for (; ; tq->iutq_next_timer_id++) {
407 407
408 408 if (tq->iutq_next_timer_id >= IU_TIMER_ID_MAX) {
409 409 if (have_wrapped)
410 410 return (-1);
411 411
412 412 have_wrapped = B_TRUE;
413 413 tq->iutq_next_timer_id = 0;
414 414 }
415 415
416 416 map_index = tq->iutq_next_timer_id / CHAR_BIT;
417 417 map_bit = tq->iutq_next_timer_id % CHAR_BIT;
418 418
419 419 if ((tq->iutq_timer_id_map[map_index] & (1 << map_bit)) == 0)
420 420 break;
421 421 }
422 422
423 423 tq->iutq_timer_id_map[map_index] |= (1 << map_bit);
424 424 return (tq->iutq_next_timer_id++);
425 425 }
426 426
427 427 /*
428 428 * release_timer_id(): releases a timer id back into the pool
429 429 *
430 430 * input: iu_tq_t *: the timer queue
431 431 * iu_timer_id_t: the timer id to release
432 432 * output: void
433 433 */
434 434
435 435 static void
436 436 release_timer_id(iu_tq_t *tq, iu_timer_id_t timer_id)
437 437 {
438 438 unsigned int map_index = timer_id / CHAR_BIT;
439 439 unsigned char map_bit = timer_id % CHAR_BIT;
440 440
441 441 tq->iutq_timer_id_map[map_index] &= ~(1 << map_bit);
442 442 }
↓ open down ↓ |
334 lines elided |
↑ open up ↑ |
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX