Blame complib/cl_timer.c

Packit 13e616
/*
Packit 13e616
 * Copyright (c) 2004-2006 Voltaire, Inc. All rights reserved.
Packit 13e616
 * Copyright (c) 2002-2005 Mellanox Technologies LTD. All rights reserved.
Packit 13e616
 * Copyright (c) 1996-2003 Intel Corporation. All rights reserved.
Packit 13e616
 *
Packit 13e616
 * This software is available to you under a choice of one of two
Packit 13e616
 * licenses.  You may choose to be licensed under the terms of the GNU
Packit 13e616
 * General Public License (GPL) Version 2, available from the file
Packit 13e616
 * COPYING in the main directory of this source tree, or the
Packit 13e616
 * OpenIB.org BSD license below:
Packit 13e616
 *
Packit 13e616
 *     Redistribution and use in source and binary forms, with or
Packit 13e616
 *     without modification, are permitted provided that the following
Packit 13e616
 *     conditions are met:
Packit 13e616
 *
Packit 13e616
 *      - Redistributions of source code must retain the above
Packit 13e616
 *        copyright notice, this list of conditions and the following
Packit 13e616
 *        disclaimer.
Packit 13e616
 *
Packit 13e616
 *      - Redistributions in binary form must reproduce the above
Packit 13e616
 *        copyright notice, this list of conditions and the following
Packit 13e616
 *        disclaimer in the documentation and/or other materials
Packit 13e616
 *        provided with the distribution.
Packit 13e616
 *
Packit 13e616
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
Packit 13e616
 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
Packit 13e616
 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
Packit 13e616
 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
Packit 13e616
 * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
Packit 13e616
 * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
Packit 13e616
 * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
Packit 13e616
 * SOFTWARE.
Packit 13e616
 *
Packit 13e616
 */
Packit 13e616
Packit 13e616
/*
Packit 13e616
 * Abstract:
Packit 13e616
 * Abstraction of Timer create, destroy functions.
Packit 13e616
 *
Packit 13e616
 */
Packit 13e616
Packit 13e616
#if HAVE_CONFIG_H
Packit 13e616
#  include <config.h>
Packit 13e616
#endif				/* HAVE_CONFIG_H */
Packit 13e616
Packit 13e616
#include <stdlib.h>
Packit 13e616
#include <string.h>
Packit 13e616
#include <complib/cl_timer.h>
Packit 13e616
#include <sys/time.h>
Packit 13e616
#include <sys/errno.h>
Packit 13e616
#include <stdio.h>
Packit 13e616
Packit 13e616
/* Timer provider (emulates timers in user mode). */
Packit 13e616
typedef struct _cl_timer_prov {
Packit 13e616
	pthread_t thread;
Packit 13e616
	pthread_mutex_t mutex;
Packit 13e616
	pthread_cond_t cond;
Packit 13e616
	cl_qlist_t queue;
Packit 13e616
Packit 13e616
	boolean_t exit;
Packit 13e616
Packit 13e616
} cl_timer_prov_t;
Packit 13e616
Packit 13e616
/* Global timer provider. */
Packit 13e616
static cl_timer_prov_t *gp_timer_prov = NULL;
Packit 13e616
Packit 13e616
static void *__cl_timer_prov_cb(IN void *const context);
Packit 13e616
Packit 13e616
/*
Packit 13e616
 * Creates the process global timer provider.  Must be called by the shared
Packit 13e616
 * object framework to solve all serialization issues.
Packit 13e616
 */
Packit 13e616
cl_status_t __cl_timer_prov_create(void)
Packit 13e616
{
Packit 13e616
	CL_ASSERT(gp_timer_prov == NULL);
Packit 13e616
Packit 13e616
	gp_timer_prov = malloc(sizeof(cl_timer_prov_t));
Packit 13e616
	if (!gp_timer_prov)
Packit 13e616
		return (CL_INSUFFICIENT_MEMORY);
Packit 13e616
	else
Packit 13e616
		memset(gp_timer_prov, 0, sizeof(cl_timer_prov_t));
Packit 13e616
Packit 13e616
	cl_qlist_init(&gp_timer_prov->queue);
Packit 13e616
Packit 13e616
	pthread_mutex_init(&gp_timer_prov->mutex, NULL);
Packit 13e616
	pthread_cond_init(&gp_timer_prov->cond, NULL);
Packit 13e616
Packit 13e616
	if (pthread_create(&gp_timer_prov->thread, NULL,
Packit 13e616
			   __cl_timer_prov_cb, NULL)) {
Packit 13e616
		__cl_timer_prov_destroy();
Packit 13e616
		return (CL_ERROR);
Packit 13e616
	}
Packit 13e616
Packit 13e616
	return (CL_SUCCESS);
Packit 13e616
}
Packit 13e616
Packit 13e616
void __cl_timer_prov_destroy(void)
Packit 13e616
{
Packit 13e616
	pthread_t tid;
Packit 13e616
Packit 13e616
	if (!gp_timer_prov)
Packit 13e616
		return;
Packit 13e616
Packit 13e616
	tid = gp_timer_prov->thread;
Packit 13e616
	pthread_mutex_lock(&gp_timer_prov->mutex);
Packit 13e616
	gp_timer_prov->exit = TRUE;
Packit 13e616
	pthread_cond_broadcast(&gp_timer_prov->cond);
Packit 13e616
	pthread_mutex_unlock(&gp_timer_prov->mutex);
Packit 13e616
	pthread_join(tid, NULL);
Packit 13e616
Packit 13e616
	/* Destroy the mutex and condition variable. */
Packit 13e616
	pthread_mutex_destroy(&gp_timer_prov->mutex);
Packit 13e616
	pthread_cond_destroy(&gp_timer_prov->cond);
Packit 13e616
Packit 13e616
	/* Free the memory and reset the global pointer. */
Packit 13e616
	free(gp_timer_prov);
Packit 13e616
	gp_timer_prov = NULL;
Packit 13e616
}
Packit 13e616
Packit 13e616
/*
Packit 13e616
 * This is the internal work function executed by the timer's thread.
Packit 13e616
 */
Packit 13e616
static void *__cl_timer_prov_cb(IN void *const context)
Packit 13e616
{
Packit 13e616
	int ret;
Packit 13e616
	cl_timer_t *p_timer;
Packit 13e616
Packit 13e616
	pthread_mutex_lock(&gp_timer_prov->mutex);
Packit 13e616
	while (!gp_timer_prov->exit) {
Packit 13e616
		if (cl_is_qlist_empty(&gp_timer_prov->queue)) {
Packit 13e616
			/* Wait until we exit or a timer is queued. */
Packit 13e616
			/* cond wait does:
Packit 13e616
			 * pthread_cond_wait atomically unlocks the mutex (as per
Packit 13e616
			 * pthread_unlock_mutex) and waits for the condition variable
Packit 13e616
			 * cond to be signaled. The thread execution is suspended and
Packit 13e616
			 * does not consume any CPU time until the condition variable is
Packit 13e616
			 * signaled. The mutex must be locked by the calling thread on
Packit 13e616
			 * entrance to pthread_cond_wait. Before RETURNING TO THE
Packit 13e616
			 * CALLING THREAD, PTHREAD_COND_WAIT RE-ACQUIRES MUTEX (as per
Packit 13e616
			 * pthread_lock_mutex).
Packit 13e616
			 */
Packit 13e616
			ret = pthread_cond_wait(&gp_timer_prov->cond,
Packit 13e616
						&gp_timer_prov->mutex);
Packit 13e616
		} else {
Packit 13e616
			/*
Packit 13e616
			 * The timer elements are on the queue in expiration order.
Packit 13e616
			 * Get the first in the list to determine how long to wait.
Packit 13e616
			 */
Packit 13e616
Packit 13e616
			p_timer =
Packit 13e616
			    (cl_timer_t *) cl_qlist_head(&gp_timer_prov->queue);
Packit 13e616
			ret =
Packit 13e616
			    pthread_cond_timedwait(&gp_timer_prov->cond,
Packit 13e616
						   &gp_timer_prov->mutex,
Packit 13e616
						   &p_timer->timeout);
Packit 13e616
Packit 13e616
			/*
Packit 13e616
			   Sleep again on every event other than timeout and invalid
Packit 13e616
			   Note: EINVAL means that we got behind. This can occur when
Packit 13e616
			   we are very busy...
Packit 13e616
			 */
Packit 13e616
			if (ret != ETIMEDOUT && ret != EINVAL)
Packit 13e616
				continue;
Packit 13e616
Packit 13e616
			/*
Packit 13e616
			 * The timer expired.  Check the state in case it was cancelled
Packit 13e616
			 * after it expired but before we got a chance to invoke the
Packit 13e616
			 * callback.
Packit 13e616
			 */
Packit 13e616
			if (p_timer->timer_state != CL_TIMER_QUEUED)
Packit 13e616
				continue;
Packit 13e616
Packit 13e616
			/*
Packit 13e616
			 * Mark the timer as running to synchronize with its
Packit 13e616
			 * cancelation since we can't hold the mutex during the
Packit 13e616
			 * callback.
Packit 13e616
			 */
Packit 13e616
			p_timer->timer_state = CL_TIMER_RUNNING;
Packit 13e616
Packit 13e616
			/* Remove the item from the timer queue. */
Packit 13e616
			cl_qlist_remove_item(&gp_timer_prov->queue,
Packit 13e616
					     &p_timer->list_item);
Packit 13e616
			pthread_mutex_unlock(&gp_timer_prov->mutex);
Packit 13e616
			/* Invoke the callback. */
Packit 13e616
			p_timer->pfn_callback((void *)p_timer->context);
Packit 13e616
Packit 13e616
			/* Acquire the mutex again. */
Packit 13e616
			pthread_mutex_lock(&gp_timer_prov->mutex);
Packit 13e616
			/*
Packit 13e616
			 * Only set the state to idle if the timer has not been accessed
Packit 13e616
			 * from the callback
Packit 13e616
			 */
Packit 13e616
			if (p_timer->timer_state == CL_TIMER_RUNNING)
Packit 13e616
				p_timer->timer_state = CL_TIMER_IDLE;
Packit 13e616
Packit 13e616
			/*
Packit 13e616
			 * Signal any thread trying to manipulate the timer
Packit 13e616
			 * that expired.
Packit 13e616
			 */
Packit 13e616
			pthread_cond_signal(&p_timer->cond);
Packit 13e616
		}
Packit 13e616
	}
Packit 13e616
	gp_timer_prov->thread = 0;
Packit 13e616
	pthread_mutex_unlock(&gp_timer_prov->mutex);
Packit 13e616
	pthread_exit(NULL);
Packit 13e616
}
Packit 13e616
Packit 13e616
/* Timer implementation. */
Packit 13e616
void cl_timer_construct(IN cl_timer_t * const p_timer)
Packit 13e616
{
Packit 13e616
	memset(p_timer, 0, sizeof(cl_timer_t));
Packit 13e616
	p_timer->state = CL_UNINITIALIZED;
Packit 13e616
}
Packit 13e616
Packit 13e616
cl_status_t cl_timer_init(IN cl_timer_t * const p_timer,
Packit 13e616
			  IN cl_pfn_timer_callback_t pfn_callback,
Packit 13e616
			  IN const void *const context)
Packit 13e616
{
Packit 13e616
	CL_ASSERT(p_timer);
Packit 13e616
	CL_ASSERT(pfn_callback);
Packit 13e616
Packit 13e616
	cl_timer_construct(p_timer);
Packit 13e616
Packit 13e616
	if (!gp_timer_prov)
Packit 13e616
		return (CL_ERROR);
Packit 13e616
Packit 13e616
	/* Store timer parameters. */
Packit 13e616
	p_timer->pfn_callback = pfn_callback;
Packit 13e616
	p_timer->context = context;
Packit 13e616
Packit 13e616
	/* Mark the timer as idle. */
Packit 13e616
	p_timer->timer_state = CL_TIMER_IDLE;
Packit 13e616
Packit 13e616
	/* Create the condition variable that is used when cancelling a timer. */
Packit 13e616
	pthread_cond_init(&p_timer->cond, NULL);
Packit 13e616
Packit 13e616
	p_timer->state = CL_INITIALIZED;
Packit 13e616
Packit 13e616
	return (CL_SUCCESS);
Packit 13e616
}
Packit 13e616
Packit 13e616
void cl_timer_destroy(IN cl_timer_t * const p_timer)
Packit 13e616
{
Packit 13e616
	CL_ASSERT(p_timer);
Packit 13e616
	CL_ASSERT(cl_is_state_valid(p_timer->state));
Packit 13e616
Packit 13e616
	if (p_timer->state == CL_INITIALIZED)
Packit 13e616
		cl_timer_stop(p_timer);
Packit 13e616
Packit 13e616
	p_timer->state = CL_UNINITIALIZED;
Packit 13e616
Packit 13e616
	/* is it possible we have some threads waiting on the cond now? */
Packit 13e616
	pthread_cond_broadcast(&p_timer->cond);
Packit 13e616
	pthread_cond_destroy(&p_timer->cond);
Packit 13e616
Packit 13e616
}
Packit 13e616
Packit 13e616
/*
Packit 13e616
 * Return TRUE if timeout value 1 is earlier than timeout value 2.
Packit 13e616
 */
Packit 13e616
static __inline boolean_t __cl_timer_is_earlier(IN struct timespec *p_timeout1,
Packit 13e616
						IN struct timespec *p_timeout2)
Packit 13e616
{
Packit 13e616
	return ((p_timeout1->tv_sec < p_timeout2->tv_sec) ||
Packit 13e616
		((p_timeout1->tv_sec == p_timeout2->tv_sec) &&
Packit 13e616
		 (p_timeout1->tv_nsec < p_timeout2->tv_nsec)));
Packit 13e616
}
Packit 13e616
Packit 13e616
/*
Packit 13e616
 * Search for a timer with an earlier timeout than the one provided by
Packit 13e616
 * the context.  Both the list item and the context are pointers to
Packit 13e616
 * a cl_timer_t structure with valid timeouts.
Packit 13e616
 */
Packit 13e616
static cl_status_t __cl_timer_find(IN const cl_list_item_t * const p_list_item,
Packit 13e616
				   IN void *const context)
Packit 13e616
{
Packit 13e616
	cl_timer_t *p_in_list;
Packit 13e616
	cl_timer_t *p_new;
Packit 13e616
Packit 13e616
	CL_ASSERT(p_list_item);
Packit 13e616
	CL_ASSERT(context);
Packit 13e616
Packit 13e616
	p_in_list = (cl_timer_t *) p_list_item;
Packit 13e616
	p_new = (cl_timer_t *) context;
Packit 13e616
Packit 13e616
	CL_ASSERT(p_in_list->state == CL_INITIALIZED);
Packit 13e616
	CL_ASSERT(p_new->state == CL_INITIALIZED);
Packit 13e616
Packit 13e616
	CL_ASSERT(p_in_list->timer_state == CL_TIMER_QUEUED);
Packit 13e616
Packit 13e616
	if (__cl_timer_is_earlier(&p_in_list->timeout, &p_new->timeout))
Packit 13e616
		return (CL_SUCCESS);
Packit 13e616
Packit 13e616
	return (CL_NOT_FOUND);
Packit 13e616
}
Packit 13e616
Packit 13e616
/*
Packit 13e616
 * Calculate 'struct timespec' value that is the
Packit 13e616
 * current time plus the 'time_ms' milliseconds.
Packit 13e616
 */
Packit 13e616
static __inline void __cl_timer_calculate(IN const uint32_t time_ms,
Packit 13e616
					  OUT struct timespec * const p_timer)
Packit 13e616
{
Packit 13e616
	struct timeval curtime, deltatime, endtime;
Packit 13e616
Packit 13e616
	gettimeofday(&curtime, NULL);
Packit 13e616
Packit 13e616
	deltatime.tv_sec = time_ms / 1000;
Packit 13e616
	deltatime.tv_usec = (time_ms % 1000) * 1000;
Packit 13e616
	timeradd(&curtime, &deltatime, &endtime);
Packit 13e616
	p_timer->tv_sec = endtime.tv_sec;
Packit 13e616
	p_timer->tv_nsec = endtime.tv_usec * 1000;
Packit 13e616
}
Packit 13e616
Packit 13e616
cl_status_t cl_timer_start(IN cl_timer_t * const p_timer,
Packit 13e616
			   IN const uint32_t time_ms)
Packit 13e616
{
Packit 13e616
	cl_list_item_t *p_list_item;
Packit 13e616
Packit 13e616
	CL_ASSERT(p_timer);
Packit 13e616
	CL_ASSERT(p_timer->state == CL_INITIALIZED);
Packit 13e616
Packit 13e616
	pthread_mutex_lock(&gp_timer_prov->mutex);
Packit 13e616
	/* Signal the timer provider thread to wake up. */
Packit 13e616
	pthread_cond_signal(&gp_timer_prov->cond);
Packit 13e616
Packit 13e616
	/* Remove the timer from the queue if currently queued. */
Packit 13e616
	if (p_timer->timer_state == CL_TIMER_QUEUED)
Packit 13e616
		cl_qlist_remove_item(&gp_timer_prov->queue,
Packit 13e616
				     &p_timer->list_item);
Packit 13e616
Packit 13e616
	__cl_timer_calculate(time_ms, &p_timer->timeout);
Packit 13e616
Packit 13e616
	/* Add the timer to the queue. */
Packit 13e616
	if (cl_is_qlist_empty(&gp_timer_prov->queue)) {
Packit 13e616
		/* The timer list is empty.  Add to the head. */
Packit 13e616
		cl_qlist_insert_head(&gp_timer_prov->queue,
Packit 13e616
				     &p_timer->list_item);
Packit 13e616
	} else {
Packit 13e616
		/* Find the correct insertion place in the list for the timer. */
Packit 13e616
		p_list_item = cl_qlist_find_from_tail(&gp_timer_prov->queue,
Packit 13e616
						      __cl_timer_find, p_timer);
Packit 13e616
Packit 13e616
		/* Insert the timer. */
Packit 13e616
		cl_qlist_insert_next(&gp_timer_prov->queue, p_list_item,
Packit 13e616
				     &p_timer->list_item);
Packit 13e616
	}
Packit 13e616
	/* Set the state. */
Packit 13e616
	p_timer->timer_state = CL_TIMER_QUEUED;
Packit 13e616
	pthread_mutex_unlock(&gp_timer_prov->mutex);
Packit 13e616
Packit 13e616
	return (CL_SUCCESS);
Packit 13e616
}
Packit 13e616
Packit 13e616
void cl_timer_stop(IN cl_timer_t * const p_timer)
Packit 13e616
{
Packit 13e616
	CL_ASSERT(p_timer);
Packit 13e616
	CL_ASSERT(p_timer->state == CL_INITIALIZED);
Packit 13e616
Packit 13e616
	pthread_mutex_lock(&gp_timer_prov->mutex);
Packit 13e616
	switch (p_timer->timer_state) {
Packit 13e616
	case CL_TIMER_RUNNING:
Packit 13e616
		/* Wait for the callback to complete. */
Packit 13e616
		pthread_cond_wait(&p_timer->cond, &gp_timer_prov->mutex);
Packit 13e616
		/* Timer could have been queued while we were waiting. */
Packit 13e616
		if (p_timer->timer_state != CL_TIMER_QUEUED)
Packit 13e616
			break;
Packit 13e616
Packit 13e616
	case CL_TIMER_QUEUED:
Packit 13e616
		/* Change the state of the timer. */
Packit 13e616
		p_timer->timer_state = CL_TIMER_IDLE;
Packit 13e616
		/* Remove the timer from the queue. */
Packit 13e616
		cl_qlist_remove_item(&gp_timer_prov->queue,
Packit 13e616
				     &p_timer->list_item);
Packit 13e616
		/*
Packit 13e616
		 * Signal the timer provider thread to move onto the
Packit 13e616
		 * next timer in the queue.
Packit 13e616
		 */
Packit 13e616
		pthread_cond_signal(&gp_timer_prov->cond);
Packit 13e616
		break;
Packit 13e616
Packit 13e616
	case CL_TIMER_IDLE:
Packit 13e616
		break;
Packit 13e616
	}
Packit 13e616
	pthread_mutex_unlock(&gp_timer_prov->mutex);
Packit 13e616
}
Packit 13e616
Packit 13e616
cl_status_t cl_timer_trim(IN cl_timer_t * const p_timer,
Packit 13e616
			  IN const uint32_t time_ms)
Packit 13e616
{
Packit 13e616
	struct timespec newtime;
Packit 13e616
	cl_status_t status;
Packit 13e616
Packit 13e616
	CL_ASSERT(p_timer);
Packit 13e616
	CL_ASSERT(p_timer->state == CL_INITIALIZED);
Packit 13e616
Packit 13e616
	pthread_mutex_lock(&gp_timer_prov->mutex);
Packit 13e616
Packit 13e616
	__cl_timer_calculate(time_ms, &newtime);
Packit 13e616
Packit 13e616
	if (p_timer->timer_state == CL_TIMER_QUEUED) {
Packit 13e616
		/* If the old time is earlier, do not trim it.  Just return. */
Packit 13e616
		if (__cl_timer_is_earlier(&p_timer->timeout, &newtime)) {
Packit 13e616
			pthread_mutex_unlock(&gp_timer_prov->mutex);
Packit 13e616
			return (CL_SUCCESS);
Packit 13e616
		}
Packit 13e616
	}
Packit 13e616
Packit 13e616
	/* Reset the timer to the new timeout value. */
Packit 13e616
Packit 13e616
	pthread_mutex_unlock(&gp_timer_prov->mutex);
Packit 13e616
	status = cl_timer_start(p_timer, time_ms);
Packit 13e616
Packit 13e616
	return (status);
Packit 13e616
}
Packit 13e616
Packit 13e616
uint64_t cl_get_time_stamp(void)
Packit 13e616
{
Packit 13e616
	uint64_t tstamp;
Packit 13e616
	struct timeval tv;
Packit 13e616
Packit 13e616
	gettimeofday(&tv, NULL);
Packit 13e616
Packit 13e616
	/* Convert the time of day into a microsecond timestamp. */
Packit 13e616
	tstamp = ((uint64_t) tv.tv_sec * 1000000) + (uint64_t) tv.tv_usec;
Packit 13e616
Packit 13e616
	return (tstamp);
Packit 13e616
}
Packit 13e616
Packit 13e616
uint32_t cl_get_time_stamp_sec(void)
Packit 13e616
{
Packit 13e616
	struct timeval tv;
Packit 13e616
Packit 13e616
	gettimeofday(&tv, NULL);
Packit 13e616
Packit 13e616
	return (tv.tv_sec);
Packit 13e616
}