4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License (the "License").
6 * You may not use this file except in compliance with the License.
8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9 * or http://www.opensolaris.org/os/licensing.
10 * See the License for the specific language governing permissions
11 * and limitations under the License.
13 * When distributing Covered Code, include this CDDL HEADER in each
14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15 * If applicable, add the following below this CDDL HEADER, with the
16 * fields enclosed by brackets "[]" replaced with your own identifying
17 * information: Portions Copyright [yyyy] [name of copyright owner]
23 * Copyright 2007 Sun Microsystems, Inc. All rights reserved.
24 * Use is subject to license terms.
27 #pragma ident "%Z%%M% %I% %E% SMI"
30 * Simple implementation of timeout functionality. The granuality is a sec
35 uint_t
sip_timeout(void *arg
, void (*callback_func
)(void *),
36 struct timeval
*timeout_time
);
37 boolean_t
sip_untimeout(uint_t
);
39 typedef struct timeout
{
40 struct timeout
*sip_timeout_next
;
41 hrtime_t sip_timeout_val
;
42 void (*sip_timeout_callback_func
)(void *);
43 void *sip_timeout_callback_func_arg
;
47 static pthread_mutex_t timeout_mutex
= PTHREAD_MUTEX_INITIALIZER
;
48 static pthread_cond_t timeout_cond_var
= PTHREAD_COND_INITIALIZER
;
49 static sip_timeout_t
*timeout_list
;
50 static sip_timeout_t
*timeout_current_start
;
51 static sip_timeout_t
*timeout_current_end
;
54 * LONG_SLEEP_TIME = (24 * 60 * 60 * NANOSEC)
56 #define LONG_SLEEP_TIME (0x15180LL * 0x3B9ACA00LL)
61 * Invoke the callback function
65 sip_run_to_functions(void *arg
)
67 sip_timeout_t
*timeout
= NULL
;
69 (void) pthread_mutex_lock(&timeout_mutex
);
70 while (timeout_current_start
!= NULL
) {
71 timeout
= timeout_current_start
;
72 if (timeout_current_end
== timeout_current_start
)
73 timeout_current_start
= timeout_current_end
= NULL
;
75 timeout_current_start
= timeout
->sip_timeout_next
;
76 (void) pthread_mutex_unlock(&timeout_mutex
);
77 timeout
->sip_timeout_callback_func(
78 timeout
->sip_timeout_callback_func_arg
);
80 (void) pthread_mutex_lock(&timeout_mutex
);
82 (void) pthread_mutex_unlock(&timeout_mutex
);
88 * In the very very unlikely case timer id wraps around and we have two timers
89 * with the same id. If that happens timer with the least amount of time left
90 * will be deleted. In case both timers have same time left than the one that
91 * was scheduled first will be deleted as it will be in the front of the list.
94 sip_untimeout(uint_t id
)
96 boolean_t ret
= B_FALSE
;
97 sip_timeout_t
*current
, *last
;
100 (void) pthread_mutex_lock(&timeout_mutex
);
103 * Check if this is in the to-be run list
105 if (timeout_current_start
!= NULL
) {
106 current
= timeout_current_start
;
107 while (current
!= NULL
) {
108 if (current
->sip_timeout_id
== id
) {
109 if (current
== timeout_current_start
) {
110 timeout_current_start
=
111 current
->sip_timeout_next
;
113 last
->sip_timeout_next
=
114 current
->sip_timeout_next
;
116 if (current
== timeout_current_end
)
117 timeout_current_end
= last
;
118 if (current
->sip_timeout_callback_func_arg
!=
121 sip_timeout_callback_func_arg
);
122 current
->sip_timeout_callback_func_arg
=
130 current
= current
->sip_timeout_next
;
135 * Check if this is in the to-be scheduled list
137 if (!ret
&& timeout_list
!= NULL
) {
139 current
= timeout_list
;
140 while (current
!= NULL
) {
141 if (current
->sip_timeout_id
== id
) {
142 if (current
== timeout_list
) {
144 current
->sip_timeout_next
;
146 last
->sip_timeout_next
=
147 current
->sip_timeout_next
;
149 if (current
->sip_timeout_callback_func_arg
!=
152 sip_timeout_callback_func_arg
);
153 current
->sip_timeout_callback_func_arg
=
161 current
= current
->sip_timeout_next
;
164 (void) pthread_mutex_unlock(&timeout_mutex
);
172 sip_timeout(void *arg
, void (*callback_func
)(void *),
173 struct timeval
*timeout_time
)
175 sip_timeout_t
*new_timeout
;
176 sip_timeout_t
*current
;
178 hrtime_t future_time
;
181 struct timespec tspec
;
185 new_timeout
= malloc(sizeof (sip_timeout_t
));
186 if (new_timeout
== NULL
)
190 if (clock_gettime(CLOCK_REALTIME
, &tspec
) != 0)
192 now
= (hrtime_t
)tspec
.tv_sec
* (hrtime_t
)NANOSEC
+ tspec
.tv_nsec
;
193 future_time
= (hrtime_t
)timeout_time
->tv_sec
* (hrtime_t
)NANOSEC
+
194 (hrtime_t
)(timeout_time
->tv_usec
* MILLISEC
) + now
;
196 future_time
= (hrtime_t
)timeout_time
->tv_sec
* (hrtime_t
)NANOSEC
+
197 (hrtime_t
)(timeout_time
->tv_usec
* MILLISEC
) + gethrtime();
199 if (future_time
<= 0L) {
204 new_timeout
->sip_timeout_next
= NULL
;
205 new_timeout
->sip_timeout_val
= future_time
;
206 new_timeout
->sip_timeout_callback_func
= callback_func
;
207 new_timeout
->sip_timeout_callback_func_arg
= arg
;
208 (void) pthread_mutex_lock(&timeout_mutex
);
213 new_timeout
->sip_timeout_id
= tid
;
214 last
= current
= timeout_list
;
215 while (current
!= NULL
) {
216 if (current
->sip_timeout_val
<= new_timeout
->sip_timeout_val
) {
218 current
= current
->sip_timeout_next
;
224 if (current
== timeout_list
) {
225 new_timeout
->sip_timeout_next
= timeout_list
;
226 timeout_list
= new_timeout
;
228 new_timeout
->sip_timeout_next
= current
,
229 last
->sip_timeout_next
= new_timeout
;
231 (void) pthread_cond_signal(&timeout_cond_var
);
232 (void) pthread_mutex_unlock(&timeout_mutex
);
237 * Schedule the next timeout
240 sip_schedule_to_functions()
242 sip_timeout_t
*timeout
= NULL
;
243 sip_timeout_t
*last
= NULL
;
244 boolean_t create_thread
= B_FALSE
;
245 hrtime_t current_time
;
247 struct timespec tspec
;
251 * Thread is holding the mutex.
254 if (clock_gettime(CLOCK_REALTIME
, &tspec
) != 0)
255 return ((hrtime_t
)LONG_SLEEP_TIME
+ current_time
);
256 current_time
= (hrtime_t
)tspec
.tv_sec
* (hrtime_t
)NANOSEC
+
259 current_time
= gethrtime();
261 if (timeout_list
== NULL
)
262 return ((hrtime_t
)LONG_SLEEP_TIME
+ current_time
);
263 timeout
= timeout_list
;
266 * Get all the timeouts that have fired.
268 while (timeout
!= NULL
&& timeout
->sip_timeout_val
<= current_time
) {
270 timeout
= timeout
->sip_timeout_next
;
274 if (timeout
!= NULL
) {
275 if (timeout_current_end
!= NULL
) {
276 timeout_current_end
->sip_timeout_next
= timeout_list
;
277 timeout_current_end
= timeout
;
279 timeout_current_start
= timeout_list
;
280 timeout_current_end
= timeout
;
281 create_thread
= B_TRUE
;
283 timeout_list
= timeout
->sip_timeout_next
;
284 timeout
->sip_timeout_next
= NULL
;
288 (void) pthread_create(&thr
, NULL
, sip_run_to_functions
,
290 (void) pthread_detach(thr
);
293 if (timeout_list
!= NULL
)
294 return (timeout_list
->sip_timeout_val
);
296 return ((hrtime_t
)LONG_SLEEP_TIME
+ current_time
);
304 sip_timer_thr(void *arg
)
307 hrtime_t current_time
;
308 hrtime_t next_timeout
;
312 struct timespec tspec
;
314 delta
= (hrtime_t
)5 * NANOSEC
;
315 (void) pthread_mutex_lock(&timeout_mutex
);
317 (void) gettimeofday(&tim
, NULL
);
318 to
.tv_sec
= tim
.tv_sec
+ (delta
/ NANOSEC
);
319 to
.tv_nsec
= (hrtime_t
)(tim
.tv_usec
* MILLISEC
) +
321 if (to
.tv_nsec
> NANOSEC
) {
322 to
.tv_sec
+= (to
.tv_nsec
/ NANOSEC
);
323 to
.tv_nsec
%= NANOSEC
;
325 (void) pthread_cond_timedwait(&timeout_cond_var
,
326 &timeout_mutex
, &to
);
328 * We return from timedwait because we either timed out
329 * or a new element was added and we need to reset the time
332 next_timeout
= sip_schedule_to_functions();
334 if (clock_gettime(CLOCK_REALTIME
, &tspec
) != 0)
335 goto again
; /* ??? */
336 current_time
= (hrtime_t
)tspec
.tv_sec
* (hrtime_t
)NANOSEC
+
339 current_time
= gethrtime();
341 delta
= next_timeout
- current_time
;
350 * The init routine, starts the timer thread
355 static boolean_t timout_init
= B_FALSE
;
358 (void) pthread_mutex_lock(&timeout_mutex
);
359 if (timout_init
== B_FALSE
) {
360 timout_init
= B_TRUE
;
361 (void) pthread_mutex_unlock(&timeout_mutex
);
363 (void) pthread_mutex_unlock(&timeout_mutex
);
366 (void) pthread_create(&thread1
, NULL
, sip_timer_thr
, NULL
);