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
37 enum timer_state {
38 READY = 100,
39 WAITING,
40 DONE,
41 ABORT
42 };
43
44 typedef 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
timer_thread(void * thread_data)53 static 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
loc_timer_start(unsigned int msec,loc_timer_callback cb_func,void * caller_data)131 void* 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
178 attr_err:
179 pthread_attr_destroy(&tattr);
180 mutex_err:
181 pthread_mutex_destroy(&t->timer_mutex);
182 cond_err:
183 pthread_cond_destroy(&t->timer_cond);
184 t_err:
185 free(t);
186 _err:
187 LOC_LOGD("%s:%d]: Exit\n", __func__, __LINE__);
188 return t;
189 }
190
loc_timer_stop(void * handle)191 void loc_timer_stop(void* handle) {
192 timer_data* t = (timer_data*)handle;
193
194 if (NULL != t && (READY == t->state || WAITING == t->state) &&
195 pthread_mutex_lock(&(t->timer_mutex)) == 0) {
196 if (READY == t->state || WAITING == t->state) {
197 pthread_cond_signal(&t->timer_cond);
198 t->state = ABORT;
199 }
200 pthread_mutex_unlock(&(t->timer_mutex));
201 }
202 }
203