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