1219820Sjeff/*
2219820Sjeff * Copyright (c) 2004-2006 Voltaire, Inc. All rights reserved.
3219820Sjeff * Copyright (c) 2002-2005 Mellanox Technologies LTD. All rights reserved.
4219820Sjeff * Copyright (c) 1996-2003 Intel Corporation. All rights reserved.
5219820Sjeff *
6219820Sjeff * This software is available to you under a choice of one of two
7219820Sjeff * licenses.  You may choose to be licensed under the terms of the GNU
8219820Sjeff * General Public License (GPL) Version 2, available from the file
9219820Sjeff * COPYING in the main directory of this source tree, or the
10219820Sjeff * OpenIB.org BSD license below:
11219820Sjeff *
12219820Sjeff *     Redistribution and use in source and binary forms, with or
13219820Sjeff *     without modification, are permitted provided that the following
14219820Sjeff *     conditions are met:
15219820Sjeff *
16219820Sjeff *      - Redistributions of source code must retain the above
17219820Sjeff *        copyright notice, this list of conditions and the following
18219820Sjeff *        disclaimer.
19219820Sjeff *
20219820Sjeff *      - Redistributions in binary form must reproduce the above
21219820Sjeff *        copyright notice, this list of conditions and the following
22219820Sjeff *        disclaimer in the documentation and/or other materials
23219820Sjeff *        provided with the distribution.
24219820Sjeff *
25219820Sjeff * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
26219820Sjeff * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
27219820Sjeff * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
28219820Sjeff * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
29219820Sjeff * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
30219820Sjeff * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
31219820Sjeff * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
32219820Sjeff * SOFTWARE.
33219820Sjeff *
34219820Sjeff */
35219820Sjeff
36219820Sjeff/*
37219820Sjeff * Abstract:
38219820Sjeff * Abstraction of Timer create, destroy functions.
39219820Sjeff *
40219820Sjeff */
41219820Sjeff
42219820Sjeff#if HAVE_CONFIG_H
43219820Sjeff#  include <config.h>
44219820Sjeff#endif				/* HAVE_CONFIG_H */
45219820Sjeff
46219820Sjeff#include <stdlib.h>
47219820Sjeff#include <string.h>
48219820Sjeff#include <complib/cl_timer.h>
49219820Sjeff#include <sys/time.h>
50219820Sjeff#include <sys/errno.h>
51219820Sjeff#include <stdio.h>
52219820Sjeff
53219820Sjeff/* Timer provider (emulates timers in user mode). */
54219820Sjefftypedef struct _cl_timer_prov {
55219820Sjeff	pthread_t thread;
56219820Sjeff	pthread_mutex_t mutex;
57219820Sjeff	pthread_cond_t cond;
58219820Sjeff	cl_qlist_t queue;
59219820Sjeff
60219820Sjeff	boolean_t exit;
61219820Sjeff
62219820Sjeff} cl_timer_prov_t;
63219820Sjeff
64219820Sjeff/* Global timer provider. */
65219820Sjeffstatic cl_timer_prov_t *gp_timer_prov = NULL;
66219820Sjeff
67219820Sjeffstatic void *__cl_timer_prov_cb(IN void *const context);
68219820Sjeff
69219820Sjeff/*
70219820Sjeff * Creates the process global timer provider.  Must be called by the shared
71219820Sjeff * object framework to solve all serialization issues.
72219820Sjeff */
73219820Sjeffcl_status_t __cl_timer_prov_create(void)
74219820Sjeff{
75219820Sjeff	CL_ASSERT(gp_timer_prov == NULL);
76219820Sjeff
77219820Sjeff	gp_timer_prov = malloc(sizeof(cl_timer_prov_t));
78219820Sjeff	if (!gp_timer_prov)
79219820Sjeff		return (CL_INSUFFICIENT_MEMORY);
80219820Sjeff	else
81219820Sjeff		memset(gp_timer_prov, 0, sizeof(cl_timer_prov_t));
82219820Sjeff
83219820Sjeff	cl_qlist_init(&gp_timer_prov->queue);
84219820Sjeff
85219820Sjeff	pthread_mutex_init(&gp_timer_prov->mutex, NULL);
86219820Sjeff	pthread_cond_init(&gp_timer_prov->cond, NULL);
87219820Sjeff
88219820Sjeff	if (pthread_create(&gp_timer_prov->thread, NULL,
89219820Sjeff			   __cl_timer_prov_cb, NULL)) {
90219820Sjeff		__cl_timer_prov_destroy();
91219820Sjeff		return (CL_ERROR);
92219820Sjeff	}
93219820Sjeff
94219820Sjeff	return (CL_SUCCESS);
95219820Sjeff}
96219820Sjeff
97219820Sjeffvoid __cl_timer_prov_destroy(void)
98219820Sjeff{
99219820Sjeff	pthread_t tid;
100219820Sjeff
101219820Sjeff	if (!gp_timer_prov)
102219820Sjeff		return;
103219820Sjeff
104219820Sjeff	tid = gp_timer_prov->thread;
105219820Sjeff	pthread_mutex_lock(&gp_timer_prov->mutex);
106219820Sjeff	gp_timer_prov->exit = TRUE;
107219820Sjeff	pthread_cond_broadcast(&gp_timer_prov->cond);
108219820Sjeff	pthread_mutex_unlock(&gp_timer_prov->mutex);
109219820Sjeff	pthread_join(tid, NULL);
110219820Sjeff
111219820Sjeff	/* Destroy the mutex and condition variable. */
112219820Sjeff	pthread_mutex_destroy(&gp_timer_prov->mutex);
113219820Sjeff	pthread_cond_destroy(&gp_timer_prov->cond);
114219820Sjeff
115219820Sjeff	/* Free the memory and reset the global pointer. */
116219820Sjeff	free(gp_timer_prov);
117219820Sjeff	gp_timer_prov = NULL;
118219820Sjeff}
119219820Sjeff
120219820Sjeff/*
121219820Sjeff * This is the internal work function executed by the timer's thread.
122219820Sjeff */
123219820Sjeffstatic void *__cl_timer_prov_cb(IN void *const context)
124219820Sjeff{
125219820Sjeff	int ret;
126219820Sjeff	cl_timer_t *p_timer;
127219820Sjeff
128219820Sjeff	pthread_mutex_lock(&gp_timer_prov->mutex);
129219820Sjeff	while (!gp_timer_prov->exit) {
130219820Sjeff		if (cl_is_qlist_empty(&gp_timer_prov->queue)) {
131219820Sjeff			/* Wait until we exit or a timer is queued. */
132219820Sjeff			/* cond wait does:
133219820Sjeff			 * pthread_cond_wait atomically unlocks the mutex (as per
134219820Sjeff			 * pthread_unlock_mutex) and waits for the condition variable
135219820Sjeff			 * cond to be signaled. The thread execution is suspended and
136219820Sjeff			 * does not consume any CPU time until the condition variable is
137219820Sjeff			 * signaled. The mutex must be locked by the calling thread on
138219820Sjeff			 * entrance to pthread_cond_wait. Before RETURNING TO THE
139219820Sjeff			 * CALLING THREAD, PTHREAD_COND_WAIT RE-ACQUIRES MUTEX (as per
140219820Sjeff			 * pthread_lock_mutex).
141219820Sjeff			 */
142219820Sjeff			ret = pthread_cond_wait(&gp_timer_prov->cond,
143219820Sjeff						&gp_timer_prov->mutex);
144219820Sjeff		} else {
145219820Sjeff			/*
146219820Sjeff			 * The timer elements are on the queue in expiration order.
147219820Sjeff			 * Get the first in the list to determine how long to wait.
148219820Sjeff			 */
149219820Sjeff
150219820Sjeff			p_timer =
151219820Sjeff			    (cl_timer_t *) cl_qlist_head(&gp_timer_prov->queue);
152219820Sjeff			ret =
153219820Sjeff			    pthread_cond_timedwait(&gp_timer_prov->cond,
154219820Sjeff						   &gp_timer_prov->mutex,
155219820Sjeff						   &p_timer->timeout);
156219820Sjeff
157219820Sjeff			/*
158219820Sjeff			   Sleep again on every event other than timeout and invalid
159219820Sjeff			   Note: EINVAL means that we got behind. This can occur when
160219820Sjeff			   we are very busy...
161219820Sjeff			 */
162219820Sjeff			if (ret != ETIMEDOUT && ret != EINVAL)
163219820Sjeff				continue;
164219820Sjeff
165219820Sjeff			/*
166219820Sjeff			 * The timer expired.  Check the state in case it was cancelled
167219820Sjeff			 * after it expired but before we got a chance to invoke the
168219820Sjeff			 * callback.
169219820Sjeff			 */
170219820Sjeff			if (p_timer->timer_state != CL_TIMER_QUEUED)
171219820Sjeff				continue;
172219820Sjeff
173219820Sjeff			/*
174219820Sjeff			 * Mark the timer as running to synchronize with its
175219820Sjeff			 * cancelation since we can't hold the mutex during the
176219820Sjeff			 * callback.
177219820Sjeff			 */
178219820Sjeff			p_timer->timer_state = CL_TIMER_RUNNING;
179219820Sjeff
180219820Sjeff			/* Remove the item from the timer queue. */
181219820Sjeff			cl_qlist_remove_item(&gp_timer_prov->queue,
182219820Sjeff					     &p_timer->list_item);
183219820Sjeff			pthread_mutex_unlock(&gp_timer_prov->mutex);
184219820Sjeff			/* Invoke the callback. */
185219820Sjeff			p_timer->pfn_callback((void *)p_timer->context);
186219820Sjeff
187219820Sjeff			/* Acquire the mutex again. */
188219820Sjeff			pthread_mutex_lock(&gp_timer_prov->mutex);
189219820Sjeff			/*
190219820Sjeff			 * Only set the state to idle if the timer has not been accessed
191219820Sjeff			 * from the callback
192219820Sjeff			 */
193219820Sjeff			if (p_timer->timer_state == CL_TIMER_RUNNING)
194219820Sjeff				p_timer->timer_state = CL_TIMER_IDLE;
195219820Sjeff
196219820Sjeff			/*
197219820Sjeff			 * Signal any thread trying to manipulate the timer
198219820Sjeff			 * that expired.
199219820Sjeff			 */
200219820Sjeff			pthread_cond_signal(&p_timer->cond);
201219820Sjeff		}
202219820Sjeff	}
203219820Sjeff	gp_timer_prov->thread = 0;
204219820Sjeff	pthread_mutex_unlock(&gp_timer_prov->mutex);
205219820Sjeff	pthread_exit(NULL);
206219820Sjeff}
207219820Sjeff
208219820Sjeff/* Timer implementation. */
209219820Sjeffvoid cl_timer_construct(IN cl_timer_t * const p_timer)
210219820Sjeff{
211219820Sjeff	memset(p_timer, 0, sizeof(cl_timer_t));
212219820Sjeff	p_timer->state = CL_UNINITIALIZED;
213219820Sjeff}
214219820Sjeff
215219820Sjeffcl_status_t
216219820Sjeffcl_timer_init(IN cl_timer_t * const p_timer,
217219820Sjeff	      IN cl_pfn_timer_callback_t pfn_callback,
218219820Sjeff	      IN const void *const context)
219219820Sjeff{
220219820Sjeff	CL_ASSERT(p_timer);
221219820Sjeff	CL_ASSERT(pfn_callback);
222219820Sjeff
223219820Sjeff	cl_timer_construct(p_timer);
224219820Sjeff
225219820Sjeff	if (!gp_timer_prov)
226219820Sjeff		return (CL_ERROR);
227219820Sjeff
228219820Sjeff	/* Store timer parameters. */
229219820Sjeff	p_timer->pfn_callback = pfn_callback;
230219820Sjeff	p_timer->context = context;
231219820Sjeff
232219820Sjeff	/* Mark the timer as idle. */
233219820Sjeff	p_timer->timer_state = CL_TIMER_IDLE;
234219820Sjeff
235219820Sjeff	/* Create the condition variable that is used when cancelling a timer. */
236219820Sjeff	pthread_cond_init(&p_timer->cond, NULL);
237219820Sjeff
238219820Sjeff	p_timer->state = CL_INITIALIZED;
239219820Sjeff
240219820Sjeff	return (CL_SUCCESS);
241219820Sjeff}
242219820Sjeff
243219820Sjeffvoid cl_timer_destroy(IN cl_timer_t * const p_timer)
244219820Sjeff{
245219820Sjeff	CL_ASSERT(p_timer);
246219820Sjeff	CL_ASSERT(cl_is_state_valid(p_timer->state));
247219820Sjeff
248219820Sjeff	if (p_timer->state == CL_INITIALIZED)
249219820Sjeff		cl_timer_stop(p_timer);
250219820Sjeff
251219820Sjeff	p_timer->state = CL_UNINITIALIZED;
252219820Sjeff
253219820Sjeff	/* is it possible we have some threads waiting on the cond now? */
254219820Sjeff	pthread_cond_broadcast(&p_timer->cond);
255219820Sjeff	pthread_cond_destroy(&p_timer->cond);
256219820Sjeff
257219820Sjeff}
258219820Sjeff
259219820Sjeff/*
260219820Sjeff * Return TRUE if timeout value 1 is earlier than timeout value 2.
261219820Sjeff */
262219820Sjeffstatic __inline boolean_t
263219820Sjeff__cl_timer_is_earlier(IN struct timespec *p_timeout1,
264219820Sjeff		      IN struct timespec *p_timeout2)
265219820Sjeff{
266219820Sjeff	return ((p_timeout1->tv_sec < p_timeout2->tv_sec) ||
267219820Sjeff		((p_timeout1->tv_sec == p_timeout2->tv_sec) &&
268219820Sjeff		 (p_timeout1->tv_nsec < p_timeout2->tv_nsec)));
269219820Sjeff}
270219820Sjeff
271219820Sjeff/*
272219820Sjeff * Search for a timer with an earlier timeout than the one provided by
273219820Sjeff * the context.  Both the list item and the context are pointers to
274219820Sjeff * a cl_timer_t structure with valid timeouts.
275219820Sjeff */
276219820Sjeffstatic cl_status_t
277219820Sjeff__cl_timer_find(IN const cl_list_item_t * const p_list_item,
278219820Sjeff		IN void *const context)
279219820Sjeff{
280219820Sjeff	cl_timer_t *p_in_list;
281219820Sjeff	cl_timer_t *p_new;
282219820Sjeff
283219820Sjeff	CL_ASSERT(p_list_item);
284219820Sjeff	CL_ASSERT(context);
285219820Sjeff
286219820Sjeff	p_in_list = (cl_timer_t *) p_list_item;
287219820Sjeff	p_new = (cl_timer_t *) context;
288219820Sjeff
289219820Sjeff	CL_ASSERT(p_in_list->state == CL_INITIALIZED);
290219820Sjeff	CL_ASSERT(p_new->state == CL_INITIALIZED);
291219820Sjeff
292219820Sjeff	CL_ASSERT(p_in_list->timer_state == CL_TIMER_QUEUED);
293219820Sjeff
294219820Sjeff	if (__cl_timer_is_earlier(&p_in_list->timeout, &p_new->timeout))
295219820Sjeff		return (CL_SUCCESS);
296219820Sjeff
297219820Sjeff	return (CL_NOT_FOUND);
298219820Sjeff}
299219820Sjeff
300219820Sjeffcl_status_t
301219820Sjeffcl_timer_start(IN cl_timer_t * const p_timer, IN const uint32_t time_ms)
302219820Sjeff{
303219820Sjeff	struct timeval curtime;
304219820Sjeff	cl_list_item_t *p_list_item;
305219820Sjeff	uint32_t delta_time = time_ms;
306219820Sjeff
307219820Sjeff	CL_ASSERT(p_timer);
308219820Sjeff	CL_ASSERT(p_timer->state == CL_INITIALIZED);
309219820Sjeff
310219820Sjeff	pthread_mutex_lock(&gp_timer_prov->mutex);
311219820Sjeff	/* Signal the timer provider thread to wake up. */
312219820Sjeff	pthread_cond_signal(&gp_timer_prov->cond);
313219820Sjeff
314219820Sjeff	/* Remove the timer from the queue if currently queued. */
315219820Sjeff	if (p_timer->timer_state == CL_TIMER_QUEUED)
316219820Sjeff		cl_qlist_remove_item(&gp_timer_prov->queue,
317219820Sjeff				     &p_timer->list_item);
318219820Sjeff
319219820Sjeff	/* Get the current time */
320219820Sjeff#ifndef timerclear
321219820Sjeff#define	timerclear(tvp)		(tvp)->tv_sec = (time_t)0, (tvp)->tv_usec = 0L
322219820Sjeff#endif
323219820Sjeff	timerclear(&curtime);
324219820Sjeff	gettimeofday(&curtime, NULL);
325219820Sjeff
326219820Sjeff	/* do not do 0 wait ! */
327219820Sjeff	/* if (delta_time < 1000.0) {delta_time = 1000;} */
328219820Sjeff
329219820Sjeff	/* Calculate the timeout. */
330219820Sjeff	p_timer->timeout.tv_sec = curtime.tv_sec + (delta_time / 1000);
331219820Sjeff	p_timer->timeout.tv_nsec =
332219820Sjeff	    (curtime.tv_usec + ((delta_time % 1000) * 1000)) * 1000;
333219820Sjeff
334219820Sjeff	/* Add the timer to the queue. */
335219820Sjeff	if (cl_is_qlist_empty(&gp_timer_prov->queue)) {
336219820Sjeff		/* The timer list is empty.  Add to the head. */
337219820Sjeff		cl_qlist_insert_head(&gp_timer_prov->queue,
338219820Sjeff				     &p_timer->list_item);
339219820Sjeff	} else {
340219820Sjeff		/* Find the correct insertion place in the list for the timer. */
341219820Sjeff		p_list_item = cl_qlist_find_from_tail(&gp_timer_prov->queue,
342219820Sjeff						      __cl_timer_find, p_timer);
343219820Sjeff
344219820Sjeff		/* Insert the timer. */
345219820Sjeff		cl_qlist_insert_next(&gp_timer_prov->queue, p_list_item,
346219820Sjeff				     &p_timer->list_item);
347219820Sjeff	}
348219820Sjeff	/* Set the state. */
349219820Sjeff	p_timer->timer_state = CL_TIMER_QUEUED;
350219820Sjeff	pthread_mutex_unlock(&gp_timer_prov->mutex);
351219820Sjeff
352219820Sjeff	return (CL_SUCCESS);
353219820Sjeff}
354219820Sjeff
355219820Sjeffvoid cl_timer_stop(IN cl_timer_t * const p_timer)
356219820Sjeff{
357219820Sjeff	CL_ASSERT(p_timer);
358219820Sjeff	CL_ASSERT(p_timer->state == CL_INITIALIZED);
359219820Sjeff
360219820Sjeff	pthread_mutex_lock(&gp_timer_prov->mutex);
361219820Sjeff	switch (p_timer->timer_state) {
362219820Sjeff	case CL_TIMER_RUNNING:
363219820Sjeff		/* Wait for the callback to complete. */
364219820Sjeff		pthread_cond_wait(&p_timer->cond, &gp_timer_prov->mutex);
365219820Sjeff		/* Timer could have been queued while we were waiting. */
366219820Sjeff		if (p_timer->timer_state != CL_TIMER_QUEUED)
367219820Sjeff			break;
368219820Sjeff
369219820Sjeff	case CL_TIMER_QUEUED:
370219820Sjeff		/* Change the state of the timer. */
371219820Sjeff		p_timer->timer_state = CL_TIMER_IDLE;
372219820Sjeff		/* Remove the timer from the queue. */
373219820Sjeff		cl_qlist_remove_item(&gp_timer_prov->queue,
374219820Sjeff				     &p_timer->list_item);
375219820Sjeff		/*
376219820Sjeff		 * Signal the timer provider thread to move onto the
377219820Sjeff		 * next timer in the queue.
378219820Sjeff		 */
379219820Sjeff		pthread_cond_signal(&gp_timer_prov->cond);
380219820Sjeff		break;
381219820Sjeff
382219820Sjeff	case CL_TIMER_IDLE:
383219820Sjeff		break;
384219820Sjeff	}
385219820Sjeff	pthread_mutex_unlock(&gp_timer_prov->mutex);
386219820Sjeff}
387219820Sjeff
388219820Sjeffcl_status_t
389219820Sjeffcl_timer_trim(IN cl_timer_t * const p_timer, IN const uint32_t time_ms)
390219820Sjeff{
391219820Sjeff	struct timeval curtime;
392219820Sjeff	struct timespec newtime;
393219820Sjeff	cl_status_t status;
394219820Sjeff
395219820Sjeff	CL_ASSERT(p_timer);
396219820Sjeff	CL_ASSERT(p_timer->state == CL_INITIALIZED);
397219820Sjeff
398219820Sjeff	pthread_mutex_lock(&gp_timer_prov->mutex);
399219820Sjeff
400219820Sjeff	/* Get the current time */
401219820Sjeff	timerclear(&curtime);
402219820Sjeff	gettimeofday(&curtime, NULL);
403219820Sjeff
404219820Sjeff	/* Calculate the timeout. */
405219820Sjeff	newtime.tv_sec = curtime.tv_sec + (time_ms / 1000);
406219820Sjeff	newtime.tv_nsec = (curtime.tv_usec + ((time_ms % 1000) * 1000)) * 1000;
407219820Sjeff
408219820Sjeff	if (p_timer->timer_state == CL_TIMER_QUEUED) {
409219820Sjeff		/* If the old time is earlier, do not trim it.  Just return. */
410219820Sjeff		if (__cl_timer_is_earlier(&p_timer->timeout, &newtime)) {
411219820Sjeff			pthread_mutex_unlock(&gp_timer_prov->mutex);
412219820Sjeff			return (CL_SUCCESS);
413219820Sjeff		}
414219820Sjeff	}
415219820Sjeff
416219820Sjeff	/* Reset the timer to the new timeout value. */
417219820Sjeff
418219820Sjeff	pthread_mutex_unlock(&gp_timer_prov->mutex);
419219820Sjeff	status = cl_timer_start(p_timer, time_ms);
420219820Sjeff
421219820Sjeff	return (status);
422219820Sjeff}
423219820Sjeff
424219820Sjeffuint64_t cl_get_time_stamp(void)
425219820Sjeff{
426219820Sjeff	uint64_t tstamp;
427219820Sjeff	struct timeval tv;
428219820Sjeff
429219820Sjeff	timerclear(&tv);
430219820Sjeff	gettimeofday(&tv, NULL);
431219820Sjeff
432219820Sjeff	/* Convert the time of day into a microsecond timestamp. */
433219820Sjeff	tstamp = ((uint64_t) tv.tv_sec * 1000000) + (uint64_t) tv.tv_usec;
434219820Sjeff
435219820Sjeff	return (tstamp);
436219820Sjeff}
437219820Sjeff
438219820Sjeffuint32_t cl_get_time_stamp_sec(void)
439219820Sjeff{
440219820Sjeff	struct timeval tv;
441219820Sjeff
442219820Sjeff	timerclear(&tv);
443219820Sjeff	gettimeofday(&tv, NULL);
444219820Sjeff
445219820Sjeff	return (tv.tv_sec);
446219820Sjeff}
447