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    pthread_mutex_destroy(&t->timer_mutex);
105    pthread_cond_destroy(&t->timer_cond);
106
107    if(ETIMEDOUT == ret)
108        t->callback_func(t->user_data, ret);
109
110    free(t);
111    LOC_LOGD("%s:%d]: Exit\n", __func__, __LINE__);
112    return NULL;
113}
114
115void* loc_timer_start(unsigned int msec, loc_timer_callback cb_func,
116                      void* caller_data)
117{
118    timer_data *t=NULL;
119    pthread_attr_t tattr;
120    pthread_t id;
121    LOC_LOGD("%s:%d]: Enter\n", __func__, __LINE__);
122    if(cb_func == NULL || msec == 0) {
123        LOC_LOGE("%s:%d]: Error: Wrong parameters\n", __func__, __LINE__);
124        goto _err;
125    }
126    t = (timer_data *)calloc(1, sizeof(timer_data));
127    if(t == NULL) {
128        LOC_LOGE("%s:%d]: Could not allocate memory. Failing.\n",
129                 __func__, __LINE__);
130        goto _err;
131    }
132
133    if(pthread_cond_init(&(t->timer_cond), NULL)) {
134        LOC_LOGE("%s:%d]: Pthread cond init failed\n", __func__, __LINE__);
135        goto t_err;
136    }
137    if(pthread_mutex_init(&(t->timer_mutex), NULL)) {
138        LOC_LOGE("%s:%d]: Pthread mutex init failed\n", __func__, __LINE__);
139        goto cond_err;
140    }
141
142    t->callback_func = cb_func;
143    t->user_data = caller_data;
144    t->time_msec = msec;
145    t->state = READY;
146
147    if (pthread_attr_init(&tattr)) {
148        LOC_LOGE("%s:%d]: Pthread mutex init failed\n", __func__, __LINE__);
149        goto mutex_err;
150    }
151    pthread_attr_setdetachstate(&tattr, PTHREAD_CREATE_DETACHED);
152
153    if(pthread_create(&(id), &tattr, timer_thread, (void *)t)) {
154        LOC_LOGE("%s:%d]: Could not create thread\n", __func__, __LINE__);
155        goto attr_err;
156    }
157
158    LOC_LOGD("%s:%d]: Created thread with id: %d\n",
159             __func__, __LINE__, (int)id);
160    goto _err;
161
162attr_err:
163    pthread_attr_destroy(&tattr);
164mutex_err:
165    pthread_mutex_destroy(&t->timer_mutex);
166cond_err:
167    pthread_cond_destroy(&t->timer_cond);
168t_err:
169    free(t);
170_err:
171    LOC_LOGD("%s:%d]: Exit\n", __func__, __LINE__);
172    return t;
173}
174
175void loc_timer_stop(void* handle) {
176    timer_data* t = (timer_data*)handle;
177
178    if (NULL != t && (READY == t->state || WAITING == t->state)) {
179        pthread_mutex_lock(&(t->timer_mutex));
180        if (READY == t->state || WAITING == t->state) {
181            pthread_cond_signal(&t->timer_cond);
182            t->state = ABORT;
183        }
184        pthread_mutex_unlock(&(t->timer_mutex));
185    }
186}
187