]> CyberLeo.Net >> Repos - FreeBSD/releng/10.0.git/blob - contrib/ofed/management/opensm/complib/cl_timer.c
- Copy stable/10 (r259064) to releng/10.0 as part of the
[FreeBSD/releng/10.0.git] / contrib / ofed / management / opensm / complib / cl_timer.c
1 /*
2  * Copyright (c) 2004-2006 Voltaire, Inc. All rights reserved.
3  * Copyright (c) 2002-2005 Mellanox Technologies LTD. All rights reserved.
4  * Copyright (c) 1996-2003 Intel Corporation. All rights reserved.
5  *
6  * This software is available to you under a choice of one of two
7  * licenses.  You may choose to be licensed under the terms of the GNU
8  * General Public License (GPL) Version 2, available from the file
9  * COPYING in the main directory of this source tree, or the
10  * OpenIB.org BSD license below:
11  *
12  *     Redistribution and use in source and binary forms, with or
13  *     without modification, are permitted provided that the following
14  *     conditions are met:
15  *
16  *      - Redistributions of source code must retain the above
17  *        copyright notice, this list of conditions and the following
18  *        disclaimer.
19  *
20  *      - Redistributions in binary form must reproduce the above
21  *        copyright notice, this list of conditions and the following
22  *        disclaimer in the documentation and/or other materials
23  *        provided with the distribution.
24  *
25  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
26  * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
27  * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
28  * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
29  * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
30  * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
31  * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
32  * SOFTWARE.
33  *
34  */
35
36 /*
37  * Abstract:
38  * Abstraction of Timer create, destroy functions.
39  *
40  */
41
42 #if HAVE_CONFIG_H
43 #  include <config.h>
44 #endif                          /* HAVE_CONFIG_H */
45
46 #include <stdlib.h>
47 #include <string.h>
48 #include <complib/cl_timer.h>
49 #include <sys/time.h>
50 #include <sys/errno.h>
51 #include <stdio.h>
52
53 /* Timer provider (emulates timers in user mode). */
54 typedef struct _cl_timer_prov {
55         pthread_t thread;
56         pthread_mutex_t mutex;
57         pthread_cond_t cond;
58         cl_qlist_t queue;
59
60         boolean_t exit;
61
62 } cl_timer_prov_t;
63
64 /* Global timer provider. */
65 static cl_timer_prov_t *gp_timer_prov = NULL;
66
67 static void *__cl_timer_prov_cb(IN void *const context);
68
69 /*
70  * Creates the process global timer provider.  Must be called by the shared
71  * object framework to solve all serialization issues.
72  */
73 cl_status_t __cl_timer_prov_create(void)
74 {
75         CL_ASSERT(gp_timer_prov == NULL);
76
77         gp_timer_prov = malloc(sizeof(cl_timer_prov_t));
78         if (!gp_timer_prov)
79                 return (CL_INSUFFICIENT_MEMORY);
80         else
81                 memset(gp_timer_prov, 0, sizeof(cl_timer_prov_t));
82
83         cl_qlist_init(&gp_timer_prov->queue);
84
85         pthread_mutex_init(&gp_timer_prov->mutex, NULL);
86         pthread_cond_init(&gp_timer_prov->cond, NULL);
87
88         if (pthread_create(&gp_timer_prov->thread, NULL,
89                            __cl_timer_prov_cb, NULL)) {
90                 __cl_timer_prov_destroy();
91                 return (CL_ERROR);
92         }
93
94         return (CL_SUCCESS);
95 }
96
97 void __cl_timer_prov_destroy(void)
98 {
99         pthread_t tid;
100
101         if (!gp_timer_prov)
102                 return;
103
104         tid = gp_timer_prov->thread;
105         pthread_mutex_lock(&gp_timer_prov->mutex);
106         gp_timer_prov->exit = TRUE;
107         pthread_cond_broadcast(&gp_timer_prov->cond);
108         pthread_mutex_unlock(&gp_timer_prov->mutex);
109         pthread_join(tid, NULL);
110
111         /* Destroy the mutex and condition variable. */
112         pthread_mutex_destroy(&gp_timer_prov->mutex);
113         pthread_cond_destroy(&gp_timer_prov->cond);
114
115         /* Free the memory and reset the global pointer. */
116         free(gp_timer_prov);
117         gp_timer_prov = NULL;
118 }
119
120 /*
121  * This is the internal work function executed by the timer's thread.
122  */
123 static void *__cl_timer_prov_cb(IN void *const context)
124 {
125         int ret;
126         cl_timer_t *p_timer;
127
128         pthread_mutex_lock(&gp_timer_prov->mutex);
129         while (!gp_timer_prov->exit) {
130                 if (cl_is_qlist_empty(&gp_timer_prov->queue)) {
131                         /* Wait until we exit or a timer is queued. */
132                         /* cond wait does:
133                          * pthread_cond_wait atomically unlocks the mutex (as per
134                          * pthread_unlock_mutex) and waits for the condition variable
135                          * cond to be signaled. The thread execution is suspended and
136                          * does not consume any CPU time until the condition variable is
137                          * signaled. The mutex must be locked by the calling thread on
138                          * entrance to pthread_cond_wait. Before RETURNING TO THE
139                          * CALLING THREAD, PTHREAD_COND_WAIT RE-ACQUIRES MUTEX (as per
140                          * pthread_lock_mutex).
141                          */
142                         ret = pthread_cond_wait(&gp_timer_prov->cond,
143                                                 &gp_timer_prov->mutex);
144                 } else {
145                         /*
146                          * The timer elements are on the queue in expiration order.
147                          * Get the first in the list to determine how long to wait.
148                          */
149
150                         p_timer =
151                             (cl_timer_t *) cl_qlist_head(&gp_timer_prov->queue);
152                         ret =
153                             pthread_cond_timedwait(&gp_timer_prov->cond,
154                                                    &gp_timer_prov->mutex,
155                                                    &p_timer->timeout);
156
157                         /*
158                            Sleep again on every event other than timeout and invalid
159                            Note: EINVAL means that we got behind. This can occur when
160                            we are very busy...
161                          */
162                         if (ret != ETIMEDOUT && ret != EINVAL)
163                                 continue;
164
165                         /*
166                          * The timer expired.  Check the state in case it was cancelled
167                          * after it expired but before we got a chance to invoke the
168                          * callback.
169                          */
170                         if (p_timer->timer_state != CL_TIMER_QUEUED)
171                                 continue;
172
173                         /*
174                          * Mark the timer as running to synchronize with its
175                          * cancelation since we can't hold the mutex during the
176                          * callback.
177                          */
178                         p_timer->timer_state = CL_TIMER_RUNNING;
179
180                         /* Remove the item from the timer queue. */
181                         cl_qlist_remove_item(&gp_timer_prov->queue,
182                                              &p_timer->list_item);
183                         pthread_mutex_unlock(&gp_timer_prov->mutex);
184                         /* Invoke the callback. */
185                         p_timer->pfn_callback((void *)p_timer->context);
186
187                         /* Acquire the mutex again. */
188                         pthread_mutex_lock(&gp_timer_prov->mutex);
189                         /*
190                          * Only set the state to idle if the timer has not been accessed
191                          * from the callback
192                          */
193                         if (p_timer->timer_state == CL_TIMER_RUNNING)
194                                 p_timer->timer_state = CL_TIMER_IDLE;
195
196                         /*
197                          * Signal any thread trying to manipulate the timer
198                          * that expired.
199                          */
200                         pthread_cond_signal(&p_timer->cond);
201                 }
202         }
203         gp_timer_prov->thread = 0;
204         pthread_mutex_unlock(&gp_timer_prov->mutex);
205         pthread_exit(NULL);
206 }
207
208 /* Timer implementation. */
209 void cl_timer_construct(IN cl_timer_t * const p_timer)
210 {
211         memset(p_timer, 0, sizeof(cl_timer_t));
212         p_timer->state = CL_UNINITIALIZED;
213 }
214
215 cl_status_t
216 cl_timer_init(IN cl_timer_t * const p_timer,
217               IN cl_pfn_timer_callback_t pfn_callback,
218               IN const void *const context)
219 {
220         CL_ASSERT(p_timer);
221         CL_ASSERT(pfn_callback);
222
223         cl_timer_construct(p_timer);
224
225         if (!gp_timer_prov)
226                 return (CL_ERROR);
227
228         /* Store timer parameters. */
229         p_timer->pfn_callback = pfn_callback;
230         p_timer->context = context;
231
232         /* Mark the timer as idle. */
233         p_timer->timer_state = CL_TIMER_IDLE;
234
235         /* Create the condition variable that is used when cancelling a timer. */
236         pthread_cond_init(&p_timer->cond, NULL);
237
238         p_timer->state = CL_INITIALIZED;
239
240         return (CL_SUCCESS);
241 }
242
243 void cl_timer_destroy(IN cl_timer_t * const p_timer)
244 {
245         CL_ASSERT(p_timer);
246         CL_ASSERT(cl_is_state_valid(p_timer->state));
247
248         if (p_timer->state == CL_INITIALIZED)
249                 cl_timer_stop(p_timer);
250
251         p_timer->state = CL_UNINITIALIZED;
252
253         /* is it possible we have some threads waiting on the cond now? */
254         pthread_cond_broadcast(&p_timer->cond);
255         pthread_cond_destroy(&p_timer->cond);
256
257 }
258
259 /*
260  * Return TRUE if timeout value 1 is earlier than timeout value 2.
261  */
262 static __inline boolean_t
263 __cl_timer_is_earlier(IN struct timespec *p_timeout1,
264                       IN struct timespec *p_timeout2)
265 {
266         return ((p_timeout1->tv_sec < p_timeout2->tv_sec) ||
267                 ((p_timeout1->tv_sec == p_timeout2->tv_sec) &&
268                  (p_timeout1->tv_nsec < p_timeout2->tv_nsec)));
269 }
270
271 /*
272  * Search for a timer with an earlier timeout than the one provided by
273  * the context.  Both the list item and the context are pointers to
274  * a cl_timer_t structure with valid timeouts.
275  */
276 static cl_status_t
277 __cl_timer_find(IN const cl_list_item_t * const p_list_item,
278                 IN void *const context)
279 {
280         cl_timer_t *p_in_list;
281         cl_timer_t *p_new;
282
283         CL_ASSERT(p_list_item);
284         CL_ASSERT(context);
285
286         p_in_list = (cl_timer_t *) p_list_item;
287         p_new = (cl_timer_t *) context;
288
289         CL_ASSERT(p_in_list->state == CL_INITIALIZED);
290         CL_ASSERT(p_new->state == CL_INITIALIZED);
291
292         CL_ASSERT(p_in_list->timer_state == CL_TIMER_QUEUED);
293
294         if (__cl_timer_is_earlier(&p_in_list->timeout, &p_new->timeout))
295                 return (CL_SUCCESS);
296
297         return (CL_NOT_FOUND);
298 }
299
300 cl_status_t
301 cl_timer_start(IN cl_timer_t * const p_timer, IN const uint32_t time_ms)
302 {
303         struct timeval curtime;
304         cl_list_item_t *p_list_item;
305         uint32_t delta_time = time_ms;
306
307         CL_ASSERT(p_timer);
308         CL_ASSERT(p_timer->state == CL_INITIALIZED);
309
310         pthread_mutex_lock(&gp_timer_prov->mutex);
311         /* Signal the timer provider thread to wake up. */
312         pthread_cond_signal(&gp_timer_prov->cond);
313
314         /* Remove the timer from the queue if currently queued. */
315         if (p_timer->timer_state == CL_TIMER_QUEUED)
316                 cl_qlist_remove_item(&gp_timer_prov->queue,
317                                      &p_timer->list_item);
318
319         /* Get the current time */
320 #ifndef timerclear
321 #define timerclear(tvp)         (tvp)->tv_sec = (time_t)0, (tvp)->tv_usec = 0L
322 #endif
323         timerclear(&curtime);
324         gettimeofday(&curtime, NULL);
325
326         /* do not do 0 wait ! */
327         /* if (delta_time < 1000.0) {delta_time = 1000;} */
328
329         /* Calculate the timeout. */
330         p_timer->timeout.tv_sec = curtime.tv_sec + (delta_time / 1000);
331         p_timer->timeout.tv_nsec =
332             (curtime.tv_usec + ((delta_time % 1000) * 1000)) * 1000;
333
334         /* Add the timer to the queue. */
335         if (cl_is_qlist_empty(&gp_timer_prov->queue)) {
336                 /* The timer list is empty.  Add to the head. */
337                 cl_qlist_insert_head(&gp_timer_prov->queue,
338                                      &p_timer->list_item);
339         } else {
340                 /* Find the correct insertion place in the list for the timer. */
341                 p_list_item = cl_qlist_find_from_tail(&gp_timer_prov->queue,
342                                                       __cl_timer_find, p_timer);
343
344                 /* Insert the timer. */
345                 cl_qlist_insert_next(&gp_timer_prov->queue, p_list_item,
346                                      &p_timer->list_item);
347         }
348         /* Set the state. */
349         p_timer->timer_state = CL_TIMER_QUEUED;
350         pthread_mutex_unlock(&gp_timer_prov->mutex);
351
352         return (CL_SUCCESS);
353 }
354
355 void cl_timer_stop(IN cl_timer_t * const p_timer)
356 {
357         CL_ASSERT(p_timer);
358         CL_ASSERT(p_timer->state == CL_INITIALIZED);
359
360         pthread_mutex_lock(&gp_timer_prov->mutex);
361         switch (p_timer->timer_state) {
362         case CL_TIMER_RUNNING:
363                 /* Wait for the callback to complete. */
364                 pthread_cond_wait(&p_timer->cond, &gp_timer_prov->mutex);
365                 /* Timer could have been queued while we were waiting. */
366                 if (p_timer->timer_state != CL_TIMER_QUEUED)
367                         break;
368
369         case CL_TIMER_QUEUED:
370                 /* Change the state of the timer. */
371                 p_timer->timer_state = CL_TIMER_IDLE;
372                 /* Remove the timer from the queue. */
373                 cl_qlist_remove_item(&gp_timer_prov->queue,
374                                      &p_timer->list_item);
375                 /*
376                  * Signal the timer provider thread to move onto the
377                  * next timer in the queue.
378                  */
379                 pthread_cond_signal(&gp_timer_prov->cond);
380                 break;
381
382         case CL_TIMER_IDLE:
383                 break;
384         }
385         pthread_mutex_unlock(&gp_timer_prov->mutex);
386 }
387
388 cl_status_t
389 cl_timer_trim(IN cl_timer_t * const p_timer, IN const uint32_t time_ms)
390 {
391         struct timeval curtime;
392         struct timespec newtime;
393         cl_status_t status;
394
395         CL_ASSERT(p_timer);
396         CL_ASSERT(p_timer->state == CL_INITIALIZED);
397
398         pthread_mutex_lock(&gp_timer_prov->mutex);
399
400         /* Get the current time */
401         timerclear(&curtime);
402         gettimeofday(&curtime, NULL);
403
404         /* Calculate the timeout. */
405         newtime.tv_sec = curtime.tv_sec + (time_ms / 1000);
406         newtime.tv_nsec = (curtime.tv_usec + ((time_ms % 1000) * 1000)) * 1000;
407
408         if (p_timer->timer_state == CL_TIMER_QUEUED) {
409                 /* If the old time is earlier, do not trim it.  Just return. */
410                 if (__cl_timer_is_earlier(&p_timer->timeout, &newtime)) {
411                         pthread_mutex_unlock(&gp_timer_prov->mutex);
412                         return (CL_SUCCESS);
413                 }
414         }
415
416         /* Reset the timer to the new timeout value. */
417
418         pthread_mutex_unlock(&gp_timer_prov->mutex);
419         status = cl_timer_start(p_timer, time_ms);
420
421         return (status);
422 }
423
424 uint64_t cl_get_time_stamp(void)
425 {
426         uint64_t tstamp;
427         struct timeval tv;
428
429         timerclear(&tv);
430         gettimeofday(&tv, NULL);
431
432         /* Convert the time of day into a microsecond timestamp. */
433         tstamp = ((uint64_t) tv.tv_sec * 1000000) + (uint64_t) tv.tv_usec;
434
435         return (tstamp);
436 }
437
438 uint32_t cl_get_time_stamp_sec(void)
439 {
440         struct timeval tv;
441
442         timerclear(&tv);
443         gettimeofday(&tv, NULL);
444
445         return (tv.tv_sec);
446 }