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     t = NULL;
187 _err:
188     LOC_LOGD("%s:%d]: Exit\n", __func__, __LINE__);
189     return t;
190 }
191 
loc_timer_stop(void * handle)192 void 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