1*2882Svi117747 /*
2*2882Svi117747  * CDDL HEADER START
3*2882Svi117747  *
4*2882Svi117747  * The contents of this file are subject to the terms of the
5*2882Svi117747  * Common Development and Distribution License (the "License").
6*2882Svi117747  * You may not use this file except in compliance with the License.
7*2882Svi117747  *
8*2882Svi117747  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9*2882Svi117747  * or http://www.opensolaris.org/os/licensing.
10*2882Svi117747  * See the License for the specific language governing permissions
11*2882Svi117747  * and limitations under the License.
12*2882Svi117747  *
13*2882Svi117747  * When distributing Covered Code, include this CDDL HEADER in each
14*2882Svi117747  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15*2882Svi117747  * If applicable, add the following below this CDDL HEADER, with the
16*2882Svi117747  * fields enclosed by brackets "[]" replaced with your own identifying
17*2882Svi117747  * information: Portions Copyright [yyyy] [name of copyright owner]
18*2882Svi117747  *
19*2882Svi117747  * CDDL HEADER END
20*2882Svi117747  */
21*2882Svi117747 
22*2882Svi117747 /*
23*2882Svi117747  * Copyright 2006 Sun Microsystems, Inc.  All rights reserved.
24*2882Svi117747  * Use is subject to license terms.
25*2882Svi117747  */
26*2882Svi117747 
27*2882Svi117747 #pragma ident	"%Z%%M%	%I%	%E% SMI"
28*2882Svi117747 
29*2882Svi117747 /*
30*2882Svi117747  * Simple implementation of timeout functionality. The granuality is a sec
31*2882Svi117747  */
32*2882Svi117747 #include <stdio.h>
33*2882Svi117747 #include <pthread.h>
34*2882Svi117747 #include <sys/errno.h>
35*2882Svi117747 #include <stdlib.h>
36*2882Svi117747 
37*2882Svi117747 #include "sip_miscdefs.h"
38*2882Svi117747 
39*2882Svi117747 uint_t		sip_timeout(void *arg, void (*callback_func)(void *),
40*2882Svi117747 		    struct timeval *timeout_time);
41*2882Svi117747 boolean_t	sip_untimeout(uint_t);
42*2882Svi117747 
43*2882Svi117747 typedef struct timeout {
44*2882Svi117747 	struct timeout *sip_timeout_next;
45*2882Svi117747 	hrtime_t sip_timeout_val;
46*2882Svi117747 	void (*sip_timeout_callback_func)(void *);
47*2882Svi117747 	void *sip_timeout_callback_func_arg;
48*2882Svi117747 	int   sip_timeout_id;
49*2882Svi117747 } sip_timeout_t;
50*2882Svi117747 
51*2882Svi117747 static pthread_mutex_t timeout_mutex = PTHREAD_MUTEX_INITIALIZER;
52*2882Svi117747 static pthread_cond_t  timeout_cond_var = PTHREAD_COND_INITIALIZER;
53*2882Svi117747 static sip_timeout_t *timeout_list;
54*2882Svi117747 static sip_timeout_t *timeout_current_start;
55*2882Svi117747 static sip_timeout_t *timeout_current_end;
56*2882Svi117747 
57*2882Svi117747 /*
58*2882Svi117747  * LONG_SLEEP_TIME = (24 * 60 * 60 * NANOSEC)
59*2882Svi117747  */
60*2882Svi117747 #define	LONG_SLEEP_TIME	(0x15180LL * 0x3B9ACA00LL)
61*2882Svi117747 
62*2882Svi117747 uint_t timer_id = 0;
63*2882Svi117747 
64*2882Svi117747 /*
65*2882Svi117747  * Invoke the callback function
66*2882Svi117747  */
67*2882Svi117747 /* ARGSUSED */
68*2882Svi117747 static void *
69*2882Svi117747 sip_run_to_functions(void *arg)
70*2882Svi117747 {
71*2882Svi117747 	sip_timeout_t *timeout = NULL;
72*2882Svi117747 
73*2882Svi117747 	(void) pthread_mutex_lock(&timeout_mutex);
74*2882Svi117747 	while (timeout_current_start != NULL) {
75*2882Svi117747 		timeout = timeout_current_start;
76*2882Svi117747 		if (timeout_current_end == timeout_current_start)
77*2882Svi117747 			timeout_current_start = timeout_current_end = NULL;
78*2882Svi117747 		else
79*2882Svi117747 			timeout_current_start = timeout->sip_timeout_next;
80*2882Svi117747 		(void) pthread_mutex_unlock(&timeout_mutex);
81*2882Svi117747 		timeout->sip_timeout_callback_func(
82*2882Svi117747 		    timeout->sip_timeout_callback_func_arg);
83*2882Svi117747 		free(timeout);
84*2882Svi117747 		(void) pthread_mutex_lock(&timeout_mutex);
85*2882Svi117747 	}
86*2882Svi117747 	(void) pthread_mutex_unlock(&timeout_mutex);
87*2882Svi117747 	pthread_exit(NULL);
88*2882Svi117747 	return ((void *)0);
89*2882Svi117747 }
90*2882Svi117747 
91*2882Svi117747 /*
92*2882Svi117747  * In the very very unlikely case timer id wraps around and we have two timers
93*2882Svi117747  * with the same id. If that happens timer with the least amount of time left
94*2882Svi117747  * will be deleted. In case both timers have same time left than the one that
95*2882Svi117747  * was scheduled first will be deleted as it will be in the front of the list.
96*2882Svi117747  */
97*2882Svi117747 boolean_t
98*2882Svi117747 sip_untimeout(uint_t id)
99*2882Svi117747 {
100*2882Svi117747 	boolean_t	ret = B_FALSE;
101*2882Svi117747 	sip_timeout_t	*current, *last;
102*2882Svi117747 
103*2882Svi117747 	last = NULL;
104*2882Svi117747 	(void) pthread_mutex_lock(&timeout_mutex);
105*2882Svi117747 
106*2882Svi117747 	/*
107*2882Svi117747 	 * Check if this is in the to-be run list
108*2882Svi117747 	 */
109*2882Svi117747 	if (timeout_current_start != NULL) {
110*2882Svi117747 		current = timeout_current_start;
111*2882Svi117747 		while (current != NULL) {
112*2882Svi117747 			if (current->sip_timeout_id == id) {
113*2882Svi117747 				if (current == timeout_current_start) {
114*2882Svi117747 					timeout_current_start =
115*2882Svi117747 					    current->sip_timeout_next;
116*2882Svi117747 				} else {
117*2882Svi117747 					last->sip_timeout_next =
118*2882Svi117747 					    current->sip_timeout_next;
119*2882Svi117747 				}
120*2882Svi117747 				if (current == timeout_current_end)
121*2882Svi117747 					timeout_current_end = last;
122*2882Svi117747 				if (current->sip_timeout_callback_func_arg !=
123*2882Svi117747 				    NULL) {
124*2882Svi117747 					free(current->
125*2882Svi117747 					    sip_timeout_callback_func_arg);
126*2882Svi117747 					current->sip_timeout_callback_func_arg =
127*2882Svi117747 					    NULL;
128*2882Svi117747 				}
129*2882Svi117747 				free(current);
130*2882Svi117747 				ret = B_TRUE;
131*2882Svi117747 				break;
132*2882Svi117747 			}
133*2882Svi117747 			last = current;
134*2882Svi117747 			current = current->sip_timeout_next;
135*2882Svi117747 		}
136*2882Svi117747 	}
137*2882Svi117747 
138*2882Svi117747 	/*
139*2882Svi117747 	 * Check if this is in the to-be scheduled list
140*2882Svi117747 	 */
141*2882Svi117747 	if (!ret && timeout_list != NULL) {
142*2882Svi117747 		last = NULL;
143*2882Svi117747 		current = timeout_list;
144*2882Svi117747 		while (current != NULL) {
145*2882Svi117747 			if (current->sip_timeout_id == id) {
146*2882Svi117747 				if (current == timeout_list) {
147*2882Svi117747 					timeout_list =
148*2882Svi117747 					    current->sip_timeout_next;
149*2882Svi117747 				} else {
150*2882Svi117747 					last->sip_timeout_next =
151*2882Svi117747 					    current->sip_timeout_next;
152*2882Svi117747 				}
153*2882Svi117747 				if (current->sip_timeout_callback_func_arg !=
154*2882Svi117747 				    NULL) {
155*2882Svi117747 					free(current->
156*2882Svi117747 					    sip_timeout_callback_func_arg);
157*2882Svi117747 					current->sip_timeout_callback_func_arg =
158*2882Svi117747 					    NULL;
159*2882Svi117747 				}
160*2882Svi117747 				free(current);
161*2882Svi117747 				ret = B_TRUE;
162*2882Svi117747 				break;
163*2882Svi117747 			}
164*2882Svi117747 			last = current;
165*2882Svi117747 			current = current->sip_timeout_next;
166*2882Svi117747 		}
167*2882Svi117747 	}
168*2882Svi117747 	(void) pthread_mutex_unlock(&timeout_mutex);
169*2882Svi117747 	return (ret);
170*2882Svi117747 }
171*2882Svi117747 
172*2882Svi117747 /*
173*2882Svi117747  * Add a new timeout
174*2882Svi117747  */
175*2882Svi117747 uint_t
176*2882Svi117747 sip_timeout(void *arg, void (*callback_func)(void *),
177*2882Svi117747     struct timeval *timeout_time)
178*2882Svi117747 {
179*2882Svi117747 	sip_timeout_t	*new_timeout;
180*2882Svi117747 	sip_timeout_t	*current, *last;
181*2882Svi117747 	hrtime_t	future_time;
182*2882Svi117747 	uint_t		tid;
183*2882Svi117747 #ifdef	__linux__
184*2882Svi117747 	struct timespec	tspec;
185*2882Svi117747 	hrtime_t	now;
186*2882Svi117747 #endif
187*2882Svi117747 
188*2882Svi117747 	new_timeout = malloc(sizeof (sip_timeout_t));
189*2882Svi117747 	if (new_timeout == NULL)
190*2882Svi117747 		return (0);
191*2882Svi117747 
192*2882Svi117747 #ifdef	__linux__
193*2882Svi117747 	if (clock_gettime(CLOCK_REALTIME, &tspec) != 0)
194*2882Svi117747 		return (0);
195*2882Svi117747 	now = (hrtime_t)tspec.tv_sec * (hrtime_t)NANOSEC + tspec.tv_nsec;
196*2882Svi117747 	future_time = (hrtime_t)timeout_time->tv_sec * (hrtime_t)NANOSEC +
197*2882Svi117747 	    (hrtime_t)(timeout_time->tv_usec * MILLISEC) + now;
198*2882Svi117747 #else
199*2882Svi117747 	future_time = (hrtime_t)timeout_time->tv_sec * (hrtime_t)NANOSEC +
200*2882Svi117747 	    (hrtime_t)(timeout_time->tv_usec * MILLISEC) + gethrtime();
201*2882Svi117747 #endif
202*2882Svi117747 	if (future_time <= 0L) {
203*2882Svi117747 		free(new_timeout);
204*2882Svi117747 		return (0);
205*2882Svi117747 	}
206*2882Svi117747 
207*2882Svi117747 	new_timeout->sip_timeout_next = NULL;
208*2882Svi117747 	new_timeout->sip_timeout_val = future_time;
209*2882Svi117747 	new_timeout->sip_timeout_callback_func = callback_func;
210*2882Svi117747 	new_timeout->sip_timeout_callback_func_arg = arg;
211*2882Svi117747 	(void) pthread_mutex_lock(&timeout_mutex);
212*2882Svi117747 	timer_id++;
213*2882Svi117747 	if (timer_id == 0)
214*2882Svi117747 		timer_id++;
215*2882Svi117747 	tid = timer_id;
216*2882Svi117747 	new_timeout->sip_timeout_id = tid;
217*2882Svi117747 	last = current = timeout_list;
218*2882Svi117747 	while (current != NULL) {
219*2882Svi117747 		if (current->sip_timeout_val <= new_timeout->sip_timeout_val) {
220*2882Svi117747 			last = current;
221*2882Svi117747 			current = current->sip_timeout_next;
222*2882Svi117747 		} else {
223*2882Svi117747 			break;
224*2882Svi117747 		}
225*2882Svi117747 	}
226*2882Svi117747 
227*2882Svi117747 	if (current == timeout_list) {
228*2882Svi117747 		new_timeout->sip_timeout_next  = timeout_list;
229*2882Svi117747 		timeout_list = new_timeout;
230*2882Svi117747 	} else {
231*2882Svi117747 		new_timeout->sip_timeout_next = current,
232*2882Svi117747 		last->sip_timeout_next = new_timeout;
233*2882Svi117747 	}
234*2882Svi117747 	(void) pthread_cond_signal(&timeout_cond_var);
235*2882Svi117747 	(void) pthread_mutex_unlock(&timeout_mutex);
236*2882Svi117747 	return (tid);
237*2882Svi117747 }
238*2882Svi117747 
239*2882Svi117747 /*
240*2882Svi117747  * Schedule the next timeout
241*2882Svi117747  */
242*2882Svi117747 static hrtime_t
243*2882Svi117747 sip_schedule_to_functions()
244*2882Svi117747 {
245*2882Svi117747 	sip_timeout_t		*timeout = NULL;
246*2882Svi117747 	sip_timeout_t		*last = NULL;
247*2882Svi117747 	boolean_t		create_thread = B_FALSE;
248*2882Svi117747 	hrtime_t		current_time;
249*2882Svi117747 #ifdef	__linux__
250*2882Svi117747 	struct timespec	tspec;
251*2882Svi117747 #endif
252*2882Svi117747 
253*2882Svi117747 	/*
254*2882Svi117747 	 * Thread is holding the mutex.
255*2882Svi117747 	 */
256*2882Svi117747 #ifdef	__linux__
257*2882Svi117747 	if (clock_gettime(CLOCK_REALTIME, &tspec) != 0)
258*2882Svi117747 		return ((hrtime_t)LONG_SLEEP_TIME + current_time);
259*2882Svi117747 	current_time = (hrtime_t)tspec.tv_sec * (hrtime_t)NANOSEC +
260*2882Svi117747 	    tspec.tv_nsec;
261*2882Svi117747 #else
262*2882Svi117747 	current_time = gethrtime();
263*2882Svi117747 #endif
264*2882Svi117747 	if (timeout_list == NULL)
265*2882Svi117747 		return ((hrtime_t)LONG_SLEEP_TIME + current_time);
266*2882Svi117747 	timeout = timeout_list;
267*2882Svi117747 
268*2882Svi117747 	/*
269*2882Svi117747 	 * Get all the timeouts that have fired.
270*2882Svi117747 	 */
271*2882Svi117747 	while (timeout != NULL && timeout->sip_timeout_val <= current_time) {
272*2882Svi117747 		last = timeout;
273*2882Svi117747 		timeout = timeout->sip_timeout_next;
274*2882Svi117747 	}
275*2882Svi117747 
276*2882Svi117747 	timeout = last;
277*2882Svi117747 	if (timeout != NULL) {
278*2882Svi117747 		if (timeout_current_end != NULL) {
279*2882Svi117747 			timeout_current_end->sip_timeout_next = timeout_list;
280*2882Svi117747 			timeout_current_end = timeout;
281*2882Svi117747 		} else {
282*2882Svi117747 			timeout_current_start = timeout_list;
283*2882Svi117747 			timeout_current_end = timeout;
284*2882Svi117747 			create_thread = B_TRUE;
285*2882Svi117747 		}
286*2882Svi117747 		timeout_list = timeout->sip_timeout_next;
287*2882Svi117747 		timeout->sip_timeout_next = NULL;
288*2882Svi117747 		if (create_thread) {
289*2882Svi117747 			pthread_t	thr;
290*2882Svi117747 
291*2882Svi117747 			(void) pthread_create(&thr, NULL, sip_run_to_functions,
292*2882Svi117747 			    NULL);
293*2882Svi117747 			(void) pthread_detach(thr);
294*2882Svi117747 		}
295*2882Svi117747 	}
296*2882Svi117747 	if (timeout_list != NULL)
297*2882Svi117747 		return (timeout_list->sip_timeout_val);
298*2882Svi117747 	else
299*2882Svi117747 		return ((hrtime_t)LONG_SLEEP_TIME + current_time);
300*2882Svi117747 }
301*2882Svi117747 
302*2882Svi117747 /*
303*2882Svi117747  * The timer routine
304*2882Svi117747  */
305*2882Svi117747 /* ARGSUSED */
306*2882Svi117747 static void *
307*2882Svi117747 sip_timer_thr(void *arg)
308*2882Svi117747 {
309*2882Svi117747 	timestruc_t	to;
310*2882Svi117747 	hrtime_t	current_time;
311*2882Svi117747 	hrtime_t	next_timeout;
312*2882Svi117747 	hrtime_t	delta;
313*2882Svi117747 #ifdef	__linux__
314*2882Svi117747 	struct timespec	tspec;
315*2882Svi117747 #endif
316*2882Svi117747 	to.tv_sec = time(NULL) + 5;
317*2882Svi117747 	to.tv_nsec = 0;
318*2882Svi117747 	(void) pthread_mutex_lock(&timeout_mutex);
319*2882Svi117747 	for (;;) {
320*2882Svi117747 		(void) pthread_cond_timedwait(&timeout_cond_var,
321*2882Svi117747 		    &timeout_mutex, &to);
322*2882Svi117747 		/*
323*2882Svi117747 		 * We return from timedwait because we either timed out
324*2882Svi117747 		 * or a new element was added and we need to reset the time
325*2882Svi117747 		 */
326*2882Svi117747 again:
327*2882Svi117747 		next_timeout =  sip_schedule_to_functions();
328*2882Svi117747 #ifdef	__linux__
329*2882Svi117747 		if (clock_gettime(CLOCK_REALTIME, &tspec) != 0)
330*2882Svi117747 			goto again; /* ??? */
331*2882Svi117747 		current_time = (hrtime_t)tspec.tv_sec * (hrtime_t)NANOSEC +
332*2882Svi117747 		    tspec.tv_nsec;
333*2882Svi117747 #else
334*2882Svi117747 		current_time = gethrtime();
335*2882Svi117747 #endif
336*2882Svi117747 		delta = next_timeout - current_time;
337*2882Svi117747 		if (delta <= 0)
338*2882Svi117747 			goto again;
339*2882Svi117747 		to.tv_sec = time(NULL) + (delta / NANOSEC);
340*2882Svi117747 		to.tv_nsec = delta % NANOSEC;
341*2882Svi117747 	}
342*2882Svi117747 	/* NOTREACHED */
343*2882Svi117747 	return ((void *)0);
344*2882Svi117747 }
345*2882Svi117747 
346*2882Svi117747 /*
347*2882Svi117747  * The init routine, starts the timer thread
348*2882Svi117747  */
349*2882Svi117747 void
350*2882Svi117747 sip_timeout_init()
351*2882Svi117747 {
352*2882Svi117747 	static boolean_t	timout_init = B_FALSE;
353*2882Svi117747 	pthread_t		thread1;
354*2882Svi117747 
355*2882Svi117747 	(void) pthread_mutex_lock(&timeout_mutex);
356*2882Svi117747 	if (timout_init == B_FALSE) {
357*2882Svi117747 		timout_init = B_TRUE;
358*2882Svi117747 		(void) pthread_mutex_unlock(&timeout_mutex);
359*2882Svi117747 	} else {
360*2882Svi117747 		(void) pthread_mutex_unlock(&timeout_mutex);
361*2882Svi117747 		return;
362*2882Svi117747 	}
363*2882Svi117747 	(void) pthread_create(&thread1, NULL, sip_timer_thr, NULL);
364*2882Svi117747 }
365