sip_timeout.c revision 2
5e989a96186a37eb528fb7bb4d28a150874ec799David Höppner/*
5e989a96186a37eb528fb7bb4d28a150874ec799David Höppner * CDDL HEADER START
5e989a96186a37eb528fb7bb4d28a150874ec799David Höppner *
5e989a96186a37eb528fb7bb4d28a150874ec799David Höppner * The contents of this file are subject to the terms of the
5e989a96186a37eb528fb7bb4d28a150874ec799David Höppner * Common Development and Distribution License (the "License").
5e989a96186a37eb528fb7bb4d28a150874ec799David Höppner * You may not use this file except in compliance with the License.
5e989a96186a37eb528fb7bb4d28a150874ec799David Höppner *
5e989a96186a37eb528fb7bb4d28a150874ec799David Höppner * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
5e989a96186a37eb528fb7bb4d28a150874ec799David Höppner * or http://www.opensolaris.org/os/licensing.
5e989a96186a37eb528fb7bb4d28a150874ec799David Höppner * See the License for the specific language governing permissions
5e989a96186a37eb528fb7bb4d28a150874ec799David Höppner * and limitations under the License.
5e989a96186a37eb528fb7bb4d28a150874ec799David Höppner *
5e989a96186a37eb528fb7bb4d28a150874ec799David Höppner * When distributing Covered Code, include this CDDL HEADER in each
5e989a96186a37eb528fb7bb4d28a150874ec799David Höppner * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
5e989a96186a37eb528fb7bb4d28a150874ec799David Höppner * If applicable, add the following below this CDDL HEADER, with the
5e989a96186a37eb528fb7bb4d28a150874ec799David Höppner * fields enclosed by brackets "[]" replaced with your own identifying
5e989a96186a37eb528fb7bb4d28a150874ec799David Höppner * information: Portions Copyright [yyyy] [name of copyright owner]
5e989a96186a37eb528fb7bb4d28a150874ec799David Höppner *
5e989a96186a37eb528fb7bb4d28a150874ec799David Höppner * CDDL HEADER END
5e989a96186a37eb528fb7bb4d28a150874ec799David Höppner */
5e989a96186a37eb528fb7bb4d28a150874ec799David Höppner
5e989a96186a37eb528fb7bb4d28a150874ec799David Höppner/*
5e989a96186a37eb528fb7bb4d28a150874ec799David Höppner * Copyright (c) 2006, 2012, Oracle and/or its affiliates. All rights reserved.
5e989a96186a37eb528fb7bb4d28a150874ec799David Höppner */
5e989a96186a37eb528fb7bb4d28a150874ec799David Höppner
5e989a96186a37eb528fb7bb4d28a150874ec799David Höppner
5e989a96186a37eb528fb7bb4d28a150874ec799David Höppner/*
5e989a96186a37eb528fb7bb4d28a150874ec799David Höppner * Simple implementation of timeout functionality. The granuality is a sec
5e989a96186a37eb528fb7bb4d28a150874ec799David Höppner */
5e989a96186a37eb528fb7bb4d28a150874ec799David Höppner#include <pthread.h>
5e989a96186a37eb528fb7bb4d28a150874ec799David Höppner#include <stdlib.h>
5e989a96186a37eb528fb7bb4d28a150874ec799David Höppner
5e989a96186a37eb528fb7bb4d28a150874ec799David Höppneruint_t sip_timeout(void *arg, void (*callback_func)(void *),
5e989a96186a37eb528fb7bb4d28a150874ec799David Höppner struct timeval *timeout_time);
5e989a96186a37eb528fb7bb4d28a150874ec799David Höppnerboolean_t sip_untimeout(uint_t);
5e989a96186a37eb528fb7bb4d28a150874ec799David Höppner
5e989a96186a37eb528fb7bb4d28a150874ec799David Höppnertypedef struct timeout {
5e989a96186a37eb528fb7bb4d28a150874ec799David Höppner struct timeout *sip_timeout_next;
5e989a96186a37eb528fb7bb4d28a150874ec799David Höppner hrtime_t sip_timeout_val;
5e989a96186a37eb528fb7bb4d28a150874ec799David Höppner void (*sip_timeout_callback_func)(void *);
5e989a96186a37eb528fb7bb4d28a150874ec799David Höppner void *sip_timeout_callback_func_arg;
5e989a96186a37eb528fb7bb4d28a150874ec799David Höppner int sip_timeout_id;
5e989a96186a37eb528fb7bb4d28a150874ec799David Höppner} sip_timeout_t;
5e989a96186a37eb528fb7bb4d28a150874ec799David Höppner
5e989a96186a37eb528fb7bb4d28a150874ec799David Höppnerstatic pthread_mutex_t timeout_mutex = PTHREAD_MUTEX_INITIALIZER;
5e989a96186a37eb528fb7bb4d28a150874ec799David Höppnerstatic pthread_cond_t timeout_cond_var = PTHREAD_COND_INITIALIZER;
5e989a96186a37eb528fb7bb4d28a150874ec799David Höppnerstatic sip_timeout_t *timeout_list;
5e989a96186a37eb528fb7bb4d28a150874ec799David Höppnerstatic sip_timeout_t *timeout_current_start;
5e989a96186a37eb528fb7bb4d28a150874ec799David Höppnerstatic sip_timeout_t *timeout_current_end;
5e989a96186a37eb528fb7bb4d28a150874ec799David Höppner
5e989a96186a37eb528fb7bb4d28a150874ec799David Höppner/*
5e989a96186a37eb528fb7bb4d28a150874ec799David Höppner * LONG_SLEEP_TIME = (24 * 60 * 60 * NANOSEC)
5e989a96186a37eb528fb7bb4d28a150874ec799David Höppner */
5e989a96186a37eb528fb7bb4d28a150874ec799David Höppner#define LONG_SLEEP_TIME (0x15180LL * 0x3B9ACA00LL)
5e989a96186a37eb528fb7bb4d28a150874ec799David Höppner
5e989a96186a37eb528fb7bb4d28a150874ec799David Höppneruint_t timer_id = 0;
5e989a96186a37eb528fb7bb4d28a150874ec799David Höppner
5e989a96186a37eb528fb7bb4d28a150874ec799David Höppner/*
5e989a96186a37eb528fb7bb4d28a150874ec799David Höppner * Invoke the callback function
5e989a96186a37eb528fb7bb4d28a150874ec799David Höppner */
5e989a96186a37eb528fb7bb4d28a150874ec799David Höppner/* ARGSUSED */
5e989a96186a37eb528fb7bb4d28a150874ec799David Höppnerstatic void *
5e989a96186a37eb528fb7bb4d28a150874ec799David Höppnersip_run_to_functions(void *arg)
5e989a96186a37eb528fb7bb4d28a150874ec799David Höppner{
5e989a96186a37eb528fb7bb4d28a150874ec799David Höppner sip_timeout_t *timeout = NULL;
5e989a96186a37eb528fb7bb4d28a150874ec799David Höppner
5e989a96186a37eb528fb7bb4d28a150874ec799David Höppner (void) pthread_mutex_lock(&timeout_mutex);
5e989a96186a37eb528fb7bb4d28a150874ec799David Höppner while (timeout_current_start != NULL) {
5e989a96186a37eb528fb7bb4d28a150874ec799David Höppner timeout = timeout_current_start;
5e989a96186a37eb528fb7bb4d28a150874ec799David Höppner if (timeout_current_end == timeout_current_start)
5e989a96186a37eb528fb7bb4d28a150874ec799David Höppner timeout_current_start = timeout_current_end = NULL;
5e989a96186a37eb528fb7bb4d28a150874ec799David Höppner else
5e989a96186a37eb528fb7bb4d28a150874ec799David Höppner timeout_current_start = timeout->sip_timeout_next;
5e989a96186a37eb528fb7bb4d28a150874ec799David Höppner (void) pthread_mutex_unlock(&timeout_mutex);
5e989a96186a37eb528fb7bb4d28a150874ec799David Höppner timeout->sip_timeout_callback_func(
5e989a96186a37eb528fb7bb4d28a150874ec799David Höppner timeout->sip_timeout_callback_func_arg);
5e989a96186a37eb528fb7bb4d28a150874ec799David Höppner free(timeout);
5e989a96186a37eb528fb7bb4d28a150874ec799David Höppner (void) pthread_mutex_lock(&timeout_mutex);
5e989a96186a37eb528fb7bb4d28a150874ec799David Höppner }
5e989a96186a37eb528fb7bb4d28a150874ec799David Höppner (void) pthread_mutex_unlock(&timeout_mutex);
5e989a96186a37eb528fb7bb4d28a150874ec799David Höppner pthread_exit(NULL);
5e989a96186a37eb528fb7bb4d28a150874ec799David Höppner /* LINTED - suppress E_FUNC_HAS_NO_RETURN_STMT (lint bug 7122677) */
5e989a96186a37eb528fb7bb4d28a150874ec799David Höppner}
5e989a96186a37eb528fb7bb4d28a150874ec799David Höppner
5e989a96186a37eb528fb7bb4d28a150874ec799David Höppner/*
5e989a96186a37eb528fb7bb4d28a150874ec799David Höppner * In the very very unlikely case timer id wraps around and we have two timers
5e989a96186a37eb528fb7bb4d28a150874ec799David Höppner * with the same id. If that happens timer with the least amount of time left
5e989a96186a37eb528fb7bb4d28a150874ec799David Höppner * will be deleted. In case both timers have same time left than the one that
5e989a96186a37eb528fb7bb4d28a150874ec799David Höppner * was scheduled first will be deleted as it will be in the front of the list.
5e989a96186a37eb528fb7bb4d28a150874ec799David Höppner */
5e989a96186a37eb528fb7bb4d28a150874ec799David Höppnerboolean_t
5e989a96186a37eb528fb7bb4d28a150874ec799David Höppnersip_untimeout(uint_t id)
5e989a96186a37eb528fb7bb4d28a150874ec799David Höppner{
5e989a96186a37eb528fb7bb4d28a150874ec799David Höppner boolean_t ret = B_FALSE;
5e989a96186a37eb528fb7bb4d28a150874ec799David Höppner sip_timeout_t *current, *last;
5e989a96186a37eb528fb7bb4d28a150874ec799David Höppner
5e989a96186a37eb528fb7bb4d28a150874ec799David Höppner last = NULL;
5e989a96186a37eb528fb7bb4d28a150874ec799David Höppner (void) pthread_mutex_lock(&timeout_mutex);
5e989a96186a37eb528fb7bb4d28a150874ec799David Höppner
5e989a96186a37eb528fb7bb4d28a150874ec799David Höppner /*
5e989a96186a37eb528fb7bb4d28a150874ec799David Höppner * Check if this is in the to-be run list
5e989a96186a37eb528fb7bb4d28a150874ec799David Höppner */
5e989a96186a37eb528fb7bb4d28a150874ec799David Höppner if (timeout_current_start != NULL) {
5e989a96186a37eb528fb7bb4d28a150874ec799David Höppner current = timeout_current_start;
5e989a96186a37eb528fb7bb4d28a150874ec799David Höppner while (current != NULL) {
5e989a96186a37eb528fb7bb4d28a150874ec799David Höppner if (current->sip_timeout_id == id) {
5e989a96186a37eb528fb7bb4d28a150874ec799David Höppner if (current == timeout_current_start) {
5e989a96186a37eb528fb7bb4d28a150874ec799David Höppner timeout_current_start =
5e989a96186a37eb528fb7bb4d28a150874ec799David Höppner current->sip_timeout_next;
5e989a96186a37eb528fb7bb4d28a150874ec799David Höppner } else {
5e989a96186a37eb528fb7bb4d28a150874ec799David Höppner last->sip_timeout_next =
5e989a96186a37eb528fb7bb4d28a150874ec799David Höppner current->sip_timeout_next;
5e989a96186a37eb528fb7bb4d28a150874ec799David Höppner }
5e989a96186a37eb528fb7bb4d28a150874ec799David Höppner if (current == timeout_current_end)
5e989a96186a37eb528fb7bb4d28a150874ec799David Höppner timeout_current_end = last;
5e989a96186a37eb528fb7bb4d28a150874ec799David Höppner if (current->sip_timeout_callback_func_arg !=
5e989a96186a37eb528fb7bb4d28a150874ec799David Höppner NULL) {
5e989a96186a37eb528fb7bb4d28a150874ec799David Höppner free(current->
5e989a96186a37eb528fb7bb4d28a150874ec799David Höppner sip_timeout_callback_func_arg);
5e989a96186a37eb528fb7bb4d28a150874ec799David Höppner current->sip_timeout_callback_func_arg =
5e989a96186a37eb528fb7bb4d28a150874ec799David Höppner NULL;
5e989a96186a37eb528fb7bb4d28a150874ec799David Höppner }
5e989a96186a37eb528fb7bb4d28a150874ec799David Höppner free(current);
5e989a96186a37eb528fb7bb4d28a150874ec799David Höppner ret = B_TRUE;
5e989a96186a37eb528fb7bb4d28a150874ec799David Höppner break;
5e989a96186a37eb528fb7bb4d28a150874ec799David Höppner }
5e989a96186a37eb528fb7bb4d28a150874ec799David Höppner last = current;
5e989a96186a37eb528fb7bb4d28a150874ec799David Höppner current = current->sip_timeout_next;
5e989a96186a37eb528fb7bb4d28a150874ec799David Höppner }
5e989a96186a37eb528fb7bb4d28a150874ec799David Höppner }
5e989a96186a37eb528fb7bb4d28a150874ec799David Höppner
5e989a96186a37eb528fb7bb4d28a150874ec799David Höppner /*
5e989a96186a37eb528fb7bb4d28a150874ec799David Höppner * Check if this is in the to-be scheduled list
5e989a96186a37eb528fb7bb4d28a150874ec799David Höppner */
5e989a96186a37eb528fb7bb4d28a150874ec799David Höppner if (!ret && timeout_list != NULL) {
5e989a96186a37eb528fb7bb4d28a150874ec799David Höppner last = NULL;
5e989a96186a37eb528fb7bb4d28a150874ec799David Höppner current = timeout_list;
5e989a96186a37eb528fb7bb4d28a150874ec799David Höppner while (current != NULL) {
5e989a96186a37eb528fb7bb4d28a150874ec799David Höppner if (current->sip_timeout_id == id) {
5e989a96186a37eb528fb7bb4d28a150874ec799David Höppner if (current == timeout_list) {
5e989a96186a37eb528fb7bb4d28a150874ec799David Höppner timeout_list =
5e989a96186a37eb528fb7bb4d28a150874ec799David Höppner current->sip_timeout_next;
5e989a96186a37eb528fb7bb4d28a150874ec799David Höppner } else {
5e989a96186a37eb528fb7bb4d28a150874ec799David Höppner last->sip_timeout_next =
5e989a96186a37eb528fb7bb4d28a150874ec799David Höppner current->sip_timeout_next;
5e989a96186a37eb528fb7bb4d28a150874ec799David Höppner }
5e989a96186a37eb528fb7bb4d28a150874ec799David Höppner if (current->sip_timeout_callback_func_arg !=
5e989a96186a37eb528fb7bb4d28a150874ec799David Höppner NULL) {
5e989a96186a37eb528fb7bb4d28a150874ec799David Höppner free(current->
5e989a96186a37eb528fb7bb4d28a150874ec799David Höppner sip_timeout_callback_func_arg);
5e989a96186a37eb528fb7bb4d28a150874ec799David Höppner current->sip_timeout_callback_func_arg =
5e989a96186a37eb528fb7bb4d28a150874ec799David Höppner NULL;
5e989a96186a37eb528fb7bb4d28a150874ec799David Höppner }
5e989a96186a37eb528fb7bb4d28a150874ec799David Höppner free(current);
5e989a96186a37eb528fb7bb4d28a150874ec799David Höppner ret = B_TRUE;
5e989a96186a37eb528fb7bb4d28a150874ec799David Höppner break;
5e989a96186a37eb528fb7bb4d28a150874ec799David Höppner }
5e989a96186a37eb528fb7bb4d28a150874ec799David Höppner last = current;
5e989a96186a37eb528fb7bb4d28a150874ec799David Höppner current = current->sip_timeout_next;
5e989a96186a37eb528fb7bb4d28a150874ec799David Höppner }
5e989a96186a37eb528fb7bb4d28a150874ec799David Höppner }
5e989a96186a37eb528fb7bb4d28a150874ec799David Höppner (void) pthread_mutex_unlock(&timeout_mutex);
5e989a96186a37eb528fb7bb4d28a150874ec799David Höppner return (ret);
5e989a96186a37eb528fb7bb4d28a150874ec799David Höppner}
5e989a96186a37eb528fb7bb4d28a150874ec799David Höppner
5e989a96186a37eb528fb7bb4d28a150874ec799David Höppner/*
5e989a96186a37eb528fb7bb4d28a150874ec799David Höppner * Add a new timeout
5e989a96186a37eb528fb7bb4d28a150874ec799David Höppner */
uint_t
sip_timeout(void *arg, void (*callback_func)(void *),
struct timeval *timeout_time)
{
sip_timeout_t *new_timeout;
sip_timeout_t *current;
sip_timeout_t *last;
hrtime_t future_time;
uint_t tid;
#ifdef __linux__
struct timespec tspec;
hrtime_t now;
#endif
new_timeout = malloc(sizeof (sip_timeout_t));
if (new_timeout == NULL)
return (0);
#ifdef __linux__
if (clock_gettime(CLOCK_REALTIME, &tspec) != 0)
return (0);
now = (hrtime_t)tspec.tv_sec * (hrtime_t)NANOSEC + tspec.tv_nsec;
future_time = (hrtime_t)timeout_time->tv_sec * (hrtime_t)NANOSEC +
(hrtime_t)(timeout_time->tv_usec * MILLISEC) + now;
#else
future_time = (hrtime_t)timeout_time->tv_sec * (hrtime_t)NANOSEC +
(hrtime_t)(timeout_time->tv_usec * MILLISEC) + gethrtime();
#endif
if (future_time <= 0L) {
free(new_timeout);
return (0);
}
new_timeout->sip_timeout_next = NULL;
new_timeout->sip_timeout_val = future_time;
new_timeout->sip_timeout_callback_func = callback_func;
new_timeout->sip_timeout_callback_func_arg = arg;
(void) pthread_mutex_lock(&timeout_mutex);
timer_id++;
if (timer_id == 0)
timer_id++;
tid = timer_id;
new_timeout->sip_timeout_id = tid;
last = current = timeout_list;
while (current != NULL) {
if (current->sip_timeout_val <= new_timeout->sip_timeout_val) {
last = current;
current = current->sip_timeout_next;
} else {
break;
}
}
if (current == timeout_list) {
new_timeout->sip_timeout_next = timeout_list;
timeout_list = new_timeout;
} else {
new_timeout->sip_timeout_next = current;
last->sip_timeout_next = new_timeout;
}
(void) pthread_cond_signal(&timeout_cond_var);
(void) pthread_mutex_unlock(&timeout_mutex);
return (tid);
}
/*
* Schedule the next timeout
*/
static hrtime_t
sip_schedule_to_functions()
{
sip_timeout_t *timeout = NULL;
sip_timeout_t *last = NULL;
boolean_t create_thread = B_FALSE;
hrtime_t current_time;
#ifdef __linux__
struct timespec tspec;
#endif
/*
* Thread is holding the mutex.
*/
#ifdef __linux__
if (clock_gettime(CLOCK_REALTIME, &tspec) != 0)
return ((hrtime_t)LONG_SLEEP_TIME + current_time);
current_time = (hrtime_t)tspec.tv_sec * (hrtime_t)NANOSEC +
tspec.tv_nsec;
#else
current_time = gethrtime();
#endif
if (timeout_list == NULL)
return ((hrtime_t)LONG_SLEEP_TIME + current_time);
timeout = timeout_list;
/*
* Get all the timeouts that have fired.
*/
while (timeout != NULL && timeout->sip_timeout_val <= current_time) {
last = timeout;
timeout = timeout->sip_timeout_next;
}
timeout = last;
if (timeout != NULL) {
if (timeout_current_end != NULL) {
timeout_current_end->sip_timeout_next = timeout_list;
timeout_current_end = timeout;
} else {
timeout_current_start = timeout_list;
timeout_current_end = timeout;
create_thread = B_TRUE;
}
timeout_list = timeout->sip_timeout_next;
timeout->sip_timeout_next = NULL;
if (create_thread) {
pthread_t thr;
(void) pthread_create(&thr, NULL, sip_run_to_functions,
NULL);
(void) pthread_detach(thr);
}
}
if (timeout_list != NULL)
return (timeout_list->sip_timeout_val);
else
return ((hrtime_t)LONG_SLEEP_TIME + current_time);
}
/*
* The timer routine
*/
/* ARGSUSED */
static void *
sip_timer_thr(void *arg)
{
timestruc_t to;
hrtime_t current_time;
hrtime_t next_timeout;
hrtime_t delta;
struct timeval tim;
#ifdef __linux__
struct timespec tspec;
#endif
delta = (hrtime_t)5 * NANOSEC;
(void) pthread_mutex_lock(&timeout_mutex);
for (;;) {
(void) gettimeofday(&tim, NULL);
to.tv_sec = tim.tv_sec + (delta / NANOSEC);
to.tv_nsec = (hrtime_t)(tim.tv_usec * MILLISEC) +
(delta % NANOSEC);
if (to.tv_nsec > NANOSEC) {
to.tv_sec += (to.tv_nsec / NANOSEC);
to.tv_nsec %= NANOSEC;
}
(void) pthread_cond_timedwait(&timeout_cond_var,
&timeout_mutex, &to);
/*
* We return from timedwait because we either timed out
* or a new element was added and we need to reset the time
*/
again:
next_timeout = sip_schedule_to_functions();
#ifdef __linux__
if (clock_gettime(CLOCK_REALTIME, &tspec) != 0)
goto again; /* ??? */
current_time = (hrtime_t)tspec.tv_sec * (hrtime_t)NANOSEC +
tspec.tv_nsec;
#else
current_time = gethrtime();
#endif
delta = next_timeout - current_time;
if (delta <= 0)
goto again;
}
/* NOTREACHED */
return ((void *)0);
}
/*
* The init routine, starts the timer thread
*/
void
sip_timeout_init()
{
static boolean_t timout_init = B_FALSE;
pthread_t thread1;
(void) pthread_mutex_lock(&timeout_mutex);
if (timout_init == B_FALSE) {
timout_init = B_TRUE;
(void) pthread_mutex_unlock(&timeout_mutex);
} else {
(void) pthread_mutex_unlock(&timeout_mutex);
return;
}
(void) pthread_create(&thread1, NULL, sip_timer_thr, NULL);
}