1 /*
2  * Copyright (C) 2015-2021 Alibaba Group Holding Limited
3  */
4 
5 #include <stdint.h>
6 #include <stdlib.h>
7 #include <string.h>
8 #include <signal.h>
9 #include <time.h>
10 #include <pthread.h>
11 #include <aos/errno.h>
12 #include <aos/kernel.h>
13 
14 #define POSIX_TIMER_ID_MIN 1
15 
16 typedef struct timer_list_s {
17     timer_t   id;
18     aos_timer_t aos_timer;
19     void *evp;  /* The sigevent as the parameter of timer_callback. */
20     struct timer_list_s *next;
21 } timer_list_t;
22 
23 timer_list_t *g_timer_list_head = NULL; /* The timer list head. */
24 pthread_mutex_t g_timer_lock = PTHREAD_MUTEX_INITIALIZER;  /* The lock to protect timer list. */
25 
26 /* Add the unify timer_callback to avoid the argument of callback is an automatic variable,
27  * we malloc a sigevent and store the argument it it when timer_create, and free it when
28  * timer_delete.
29  */
timer_callback(void * timer,void * arg)30 static void timer_callback(void *timer, void *arg)
31 {
32     struct sigevent *evp =(struct sigevent *)arg;
33 
34     if ((evp != NULL) && (evp->sigev_notify_function != NULL)) {
35             evp->sigev_notify_function(evp->sigev_value);
36     }
37 }
38 
timespec_to_nanosecond(struct timespec * t)39 static inline int64_t timespec_to_nanosecond(struct timespec *t)
40 {
41     return ((uint64_t)t->tv_sec * 1000000000UL + t->tv_nsec);
42 }
43 
nanosecond_to_timespec(int64_t ns)44 static inline struct timespec nanosecond_to_timespec(int64_t ns)
45 {
46     struct timespec ret_time;
47 
48     ret_time.tv_sec = ns / 1000000000UL;
49     ret_time.tv_nsec = ns % 1000000000UL;
50 
51     return ret_time;
52 }
53 
timespec_abs_to_relate(struct timespec * time_abs,struct timespec * time_relate)54 static int timespec_abs_to_relate(struct timespec *time_abs, struct timespec *time_relate)
55 {
56     int ret = 0;
57     int64_t ns = 0;
58     struct timespec time_now;
59 
60     memset(&time_now, 0, sizeof(time_now));
61 
62     ret = clock_gettime(CLOCK_MONOTONIC, &time_now);
63     if (ret != 0) {
64         return -1;
65     }
66 
67     ns = timespec_to_nanosecond(time_abs) - timespec_to_nanosecond(&time_now);
68     if (ns < 0) {
69         return -1;
70     }
71 
72     *time_relate = nanosecond_to_timespec(ns);
73 
74     return 0;
75 }
76 
timer_create(clockid_t clockid,struct sigevent * evp,timer_t * timerid)77 int timer_create(clockid_t clockid, struct sigevent * evp, timer_t * timerid)
78 {
79     int ret = 0;
80     timer_list_t *timer_list_m = NULL;
81     timer_list_t *timer_list = NULL;
82 
83     if ((evp == NULL) || (timerid == NULL) ||
84         ((clockid != CLOCK_REALTIME) && (clockid != CLOCK_MONOTONIC))) {
85         errno = EINVAL;
86         return -1;
87     }
88 
89     /* Only support SIGEV_THREAD. */
90     if (evp->sigev_notify != SIGEV_THREAD) {
91         errno = ENOTSUP;
92         return -1;
93     }
94 
95     /* The sigev_notify_function should be set for SIGEV_THREAD */
96     if (evp->sigev_notify_function == NULL) {
97         errno = EINVAL;
98         return -1;
99     }
100 
101     /* malloc new timer struct */
102     timer_list_m = (timer_list_t *)malloc(sizeof(timer_list_t));
103     if (timer_list_m == NULL) {
104         return -1;
105     }
106 
107     memset(timer_list_m, 0, sizeof(timer_list_t));
108 
109     timer_list_m->evp = malloc(sizeof(struct sigevent));
110     if(timer_list_m->evp == NULL) {
111         free(timer_list_m);
112         timer_list_m = NULL;
113         return -1;
114     }
115     memcpy(timer_list_m->evp, evp, sizeof(struct sigevent));
116 
117     ret = pthread_mutex_lock(&g_timer_lock);
118     if (ret != 0) {
119         free(timer_list_m->evp);
120         free(timer_list_m);
121         return -1;
122     }
123 
124     /* find the last node add the new timer to the list */
125     if (g_timer_list_head == NULL) {
126 
127         /* init the id to POSIX_TIMER_ID_MIN */
128         timer_list_m->id = (timer_t) POSIX_TIMER_ID_MIN;
129         g_timer_list_head = timer_list_m;
130     } else {
131         timer_list = g_timer_list_head;
132         while(timer_list->next != NULL) {
133             timer_list = timer_list->next;
134         }
135 
136         /* the id of new timer equel to last id plus one */
137         timer_list_m->id = (int)timer_list->id + 1;
138         timer_list->next = timer_list_m;
139     }
140 
141     pthread_mutex_unlock(&g_timer_lock);
142 
143     /* create a timer */
144     ret = aos_timer_new_ext(&(timer_list_m->aos_timer), timer_callback, timer_list_m->evp,
145                             1, 1, 0);
146     if (ret != 0) {
147         return -1;
148     }
149 
150     /* update the timerid */
151     *timerid = timer_list_m->id;
152 
153     return 0;
154 }
155 
timer_delete(timer_t timerid)156 int timer_delete(timer_t timerid)
157 {
158     timer_list_t *timer_list = NULL;
159     timer_list_t *timer_list_l = NULL;
160     int ret = 0;
161 
162     /* Timer list is empty. */
163     if (g_timer_list_head == NULL) {
164         return -1;
165     }
166 
167     ret = pthread_mutex_lock(&g_timer_lock);
168     if (ret != 0) {
169         return -1;
170     }
171 
172     /* Scan the list to find the timer according to timerid. */
173     timer_list = g_timer_list_head;
174     timer_list_l = g_timer_list_head;
175     while ((timer_list != NULL) && (timer_list->id != timerid)) {
176         timer_list_l = timer_list;
177         timer_list = timer_list->next;
178     }
179 
180     /* Do not find the timerid. */
181     if (timer_list == NULL) {
182         pthread_mutex_unlock(&g_timer_lock);
183         return -1;
184     }
185 
186     /* Stop and detete the timer. */
187     aos_timer_stop(&(timer_list->aos_timer));
188 
189     aos_timer_free(&(timer_list->aos_timer));
190     /* Delete the timer from the list and free the timer. */
191     if (timer_list_l == g_timer_list_head) {
192         g_timer_list_head = timer_list->next;
193     } else {
194         timer_list_l->next = timer_list->next;
195     }
196 
197     if (timer_list->evp != NULL) {
198         free(timer_list->evp);
199     }
200     free(timer_list);
201 
202     pthread_mutex_unlock(&g_timer_lock);
203 
204     return ret;
205 }
206 
timer_settime(timer_t timerid,int flags,const struct itimerspec * restrict value,struct itimerspec * restrict ovalue)207 int timer_settime(timer_t timerid, int flags, const struct itimerspec *restrict value,
208                   struct itimerspec *restrict ovalue)
209 {
210     int     ret            = 0;
211     int64_t value_ns       = 0;
212     int64_t interval_ns    = 0;
213     struct timespec value_spec = {0};
214     struct timespec interval_spec = {0};
215     timer_list_t   *timer_list = NULL;
216 
217     if (value == NULL) {
218         return -1;
219     }
220 
221     if (g_timer_list_head == NULL) {
222         return -1;
223     }
224 
225     /* If the time is absolute time transform it to relative time. */
226     if((flags & TIMER_ABSTIME) == TIMER_ABSTIME) {
227         ret = timespec_abs_to_relate((struct timespec *)&value->it_value, &value_spec);
228         ret |= timespec_abs_to_relate((struct timespec *)&value->it_interval, &interval_spec);
229         if (ret != 0) {
230             return -1;
231         }
232 
233         value_ns = timespec_to_nanosecond(&value_spec);
234         interval_ns = timespec_to_nanosecond(&interval_spec);
235     } else {
236         value_ns = timespec_to_nanosecond((struct timespec *)&value->it_value);
237         interval_ns = timespec_to_nanosecond((struct timespec *)&value->it_interval);
238     }
239 
240     /* Get the old parameters of timer if ovalue is not NULL. */
241     if (ovalue != NULL) {
242         ret = timer_gettime(timerid, ovalue);
243         if (ret != 0) {
244             return -1;
245         }
246     }
247 
248     ret = pthread_mutex_lock(&g_timer_lock);
249     if (ret != 0) {
250         return -1;
251     }
252 
253     /* Scan the list to find the timer according to timerid. */
254     timer_list = g_timer_list_head;
255     while ((timer_list != NULL) && (timer_list->id != timerid)) {
256         timer_list = timer_list->next;
257     }
258 
259     /* Do not find the timer. */
260     if (timer_list == NULL) {
261         pthread_mutex_unlock(&g_timer_lock);
262         return -1;
263     }
264 
265     aos_timer_stop(&(timer_list->aos_timer));
266     /* TODO: support interval and first round timer different. */
267     ret = aos_timer_change(&(timer_list->aos_timer), value_ns / 1000000);
268     aos_timer_start(&(timer_list->aos_timer));
269 
270     pthread_mutex_unlock(&g_timer_lock);
271     return ret;
272 }
273 
timer_gettime(timer_t timerid,struct itimerspec * value)274 int timer_gettime(timer_t timerid, struct itimerspec *value)
275 {
276     int ret = 0;
277     uint64_t time_ns[4] = {0};
278     timer_list_t *timer_list = NULL;
279 
280     if (value == NULL) {
281         return -1;
282     }
283 
284     ret = pthread_mutex_lock(&g_timer_lock);
285     if (ret != 0) {
286         return -1;
287     }
288 
289     /* Scan the list to find the timer according to timerid */
290     timer_list = g_timer_list_head;
291     while(timer_list != NULL) {
292         if (timer_list->id == timerid) {
293             break;
294         }
295         timer_list = timer_list->next;
296     }
297 
298     if (timer_list == NULL) {
299         /* The timerid is not found. */
300         pthread_mutex_unlock(&g_timer_lock);
301         return -1;
302     }
303 
304     ret = aos_timer_gettime(&(timer_list->aos_timer), time_ns);
305     if (ret != 0) {
306         pthread_mutex_unlock(&g_timer_lock);
307         return -1;
308     }
309     value->it_interval.tv_sec = time_ns[0];
310     value->it_interval.tv_nsec = time_ns[1];
311     value->it_value.tv_sec = time_ns[2];
312     value->it_value.tv_nsec = time_ns[3];
313 
314     pthread_mutex_unlock(&g_timer_lock);
315     return 0;
316 }
317 
timer_getoverrun(timer_t timerid)318 int timer_getoverrun(timer_t timerid)
319 {
320     errno = ENOSYS;
321     return -1;
322 }
323