1/* Copyright (c) 2013, The Linux Foundation. All rights reserved. 2 * 3 * Redistribution and use in source and binary forms, with or without 4 * modification, are permitted provided that the following conditions are 5 * met: 6 * * Redistributions of source code must retain the above copyright 7 * notice, this list of conditions and the following disclaimer. 8 * * Redistributions in binary form must reproduce the above 9 * copyright notice, this list of conditions and the following 10 * disclaimer in the documentation and/or other materials provided 11 * with the distribution. 12 * * Neither the name of The Linux Foundation, nor the names of its 13 * contributors may be used to endorse or promote products derived 14 * from this software without specific prior written permission. 15 * 16 * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED 17 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 18 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT 19 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS 20 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 21 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 22 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR 23 * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 24 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE 25 * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN 26 * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 27 * 28 */ 29 30#include<stdio.h> 31#include<stdlib.h> 32#include<sys/time.h> 33#include "loc_timer.h" 34#include<time.h> 35#include<errno.h> 36 37enum timer_state { 38 READY = 100, 39 WAITING, 40 DONE, 41 ABORT 42}; 43 44typedef struct { 45 loc_timer_callback callback_func; 46 void *user_data; 47 unsigned int time_msec; 48 pthread_cond_t timer_cond; 49 pthread_mutex_t timer_mutex; 50 enum timer_state state; 51}timer_data; 52 53static void *timer_thread(void *thread_data) 54{ 55 int ret = -ETIMEDOUT; 56 struct timespec ts; 57 struct timeval tv; 58 timer_data* t = (timer_data*)thread_data; 59 60 LOC_LOGD("%s:%d]: Enter. Delay = %d\n", __func__, __LINE__, t->time_msec); 61 62 gettimeofday(&tv, NULL); 63 clock_gettime(CLOCK_REALTIME, &ts); 64 if(t->time_msec >= 1000) { 65 ts.tv_sec += t->time_msec/1000; 66 t->time_msec = t->time_msec % 1000; 67 } 68 if(t->time_msec) 69 ts.tv_nsec += t->time_msec * 1000000; 70 if(ts.tv_nsec > 999999999) { 71 LOC_LOGD("%s:%d]: Large nanosecs\n", __func__, __LINE__); 72 ts.tv_sec += 1; 73 ts.tv_nsec -= 1000000000; 74 } 75 LOC_LOGD("%s:%d]: ts.tv_sec:%d; ts.tv_nsec:%d\n" 76 "\t Current time: %d sec; %d nsec", 77 __func__, __LINE__, (int)ts.tv_sec, (int)ts.tv_nsec, 78 (int)tv.tv_sec, (int)tv.tv_usec*1000); 79 80 pthread_mutex_lock(&(t->timer_mutex)); 81 if (READY == t->state) { 82 t->state = WAITING; 83 ret = pthread_cond_timedwait(&t->timer_cond, &t->timer_mutex, &ts); 84 t->state = DONE; 85 } 86 pthread_mutex_unlock(&(t->timer_mutex)); 87 88 switch (ret) { 89 case ETIMEDOUT: 90 LOC_LOGV("%s:%d]: loc_timer timed out", __func__, __LINE__); 91 break; 92 case 0: 93 LOC_LOGV("%s:%d]: loc_timer stopped", __func__, __LINE__); 94 break; 95 case -ETIMEDOUT: 96 LOC_LOGV("%s:%d]: loc_timer cancelled", __func__, __LINE__); 97 break; 98 default: 99 LOC_LOGE("%s:%d]: Call to pthread timedwait failed; ret=%d\n", 100 __func__, __LINE__, ret); 101 break; 102 } 103 104 if(ETIMEDOUT == ret) 105 t->callback_func(t->user_data, ret); 106 107 // A (should be rare) race condition is that, when the loc_time_stop is called 108 // and acquired mutex, we reach here. pthread_mutex_destroy will fail with 109 // error code EBUSY. We give it 6 tries in 5 seconds. Should be eanough time 110 // for loc_timer_stop to complete. With the 7th try, we also perform unlock 111 // prior to destroy. 112 { 113 int i; 114 for (i = 0; EBUSY == pthread_mutex_destroy(&t->timer_mutex) && i <= 5; i++) { 115 if (i < 5) { 116 sleep(1); 117 } else { 118 // nah, forget it, something is seriously wrong. Mutex has been 119 // held too long. Unlock the mutext here. 120 pthread_mutex_unlock(&t->timer_mutex); 121 } 122 } 123 } 124 pthread_cond_destroy(&t->timer_cond); 125 126 free(t); 127 LOC_LOGD("%s:%d]: Exit\n", __func__, __LINE__); 128 return NULL; 129} 130 131void* loc_timer_start(unsigned int msec, loc_timer_callback cb_func, 132 void* caller_data) 133{ 134 timer_data *t=NULL; 135 pthread_attr_t tattr; 136 pthread_t id; 137 LOC_LOGD("%s:%d]: Enter\n", __func__, __LINE__); 138 if(cb_func == NULL || msec == 0) { 139 LOC_LOGE("%s:%d]: Error: Wrong parameters\n", __func__, __LINE__); 140 goto _err; 141 } 142 t = (timer_data *)calloc(1, sizeof(timer_data)); 143 if(t == NULL) { 144 LOC_LOGE("%s:%d]: Could not allocate memory. Failing.\n", 145 __func__, __LINE__); 146 goto _err; 147 } 148 149 if(pthread_cond_init(&(t->timer_cond), NULL)) { 150 LOC_LOGE("%s:%d]: Pthread cond init failed\n", __func__, __LINE__); 151 goto t_err; 152 } 153 if(pthread_mutex_init(&(t->timer_mutex), NULL)) { 154 LOC_LOGE("%s:%d]: Pthread mutex init failed\n", __func__, __LINE__); 155 goto cond_err; 156 } 157 158 t->callback_func = cb_func; 159 t->user_data = caller_data; 160 t->time_msec = msec; 161 t->state = READY; 162 163 if (pthread_attr_init(&tattr)) { 164 LOC_LOGE("%s:%d]: Pthread mutex init failed\n", __func__, __LINE__); 165 goto mutex_err; 166 } 167 pthread_attr_setdetachstate(&tattr, PTHREAD_CREATE_DETACHED); 168 169 if(pthread_create(&(id), &tattr, timer_thread, (void *)t)) { 170 LOC_LOGE("%s:%d]: Could not create thread\n", __func__, __LINE__); 171 goto attr_err; 172 } 173 174 LOC_LOGD("%s:%d]: Created thread with id: %d\n", 175 __func__, __LINE__, (int)id); 176 goto _err; 177 178attr_err: 179 pthread_attr_destroy(&tattr); 180mutex_err: 181 pthread_mutex_destroy(&t->timer_mutex); 182cond_err: 183 pthread_cond_destroy(&t->timer_cond); 184t_err: 185 free(t); 186 t = NULL; 187_err: 188 LOC_LOGD("%s:%d]: Exit\n", __func__, __LINE__); 189 return t; 190} 191 192void loc_timer_stop(void* handle) { 193 timer_data* t = (timer_data*)handle; 194 195 if (NULL != t && (READY == t->state || WAITING == t->state) && 196 pthread_mutex_lock(&(t->timer_mutex)) == 0) { 197 if (READY == t->state || WAITING == t->state) { 198 pthread_cond_signal(&t->timer_cond); 199 t->state = ABORT; 200 } 201 pthread_mutex_unlock(&(t->timer_mutex)); 202 } 203} 204