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