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