1 /*
2  * Copyright (C) 2021 Huawei Device Co., Ltd.
3  * Licensed under the Apache License, Version 2.0 (the "License");
4  * you may not use this file except in compliance with the License.
5  * You may obtain a copy of the License at
6  *
7  *     http://www.apache.org/licenses/LICENSE-2.0
8  *
9  * Unless required by applicable law or agreed to in writing, software
10  * distributed under the License is distributed on an "AS IS" BASIS,
11  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12  * See the License for the specific language governing permissions and
13  * limitations under the License.
14  */
15 
16 #include "nstackx_timer.h"
17 #include "nstackx_log.h"
18 #include "nstackx_error.h"
19 #include "securec.h"
20 #include <signal.h>
21 #include <time.h>
22 
23 #define TAG "nStackXTimer"
24 
25 #define TIMERID_TO_TASKFD(timerid) ((int32_t)(uintptr_t)(timerid))
26 #define TASKFD_TO_TIMERID(taskfd) ((timer_t)(uintptr_t)(taskfd))
27 
TimerDelete(Timer * timer)28 void TimerDelete(Timer *timer)
29 {
30     if (timer == NULL) {
31         return;
32     }
33     if (TASKFD_TO_TIMERID(timer->task.taskfd) != NULL) {
34         if (timer_delete(TASKFD_TO_TIMERID(timer->task.taskfd)) < 0) {
35             LOGE(TAG, "close timer task failed");
36         }
37         timer->task.taskfd = TIMERID_TO_TASKFD(NULL);
38     }
39     free(timer);
40 }
41 
TimerReadHandle(void * arg)42 static void TimerReadHandle(void *arg)
43 {
44     EpollTask *task = arg;
45     Timer *timer = NULL;
46     if (task == NULL) {
47         LOGE(TAG, "Timer task is NULL");
48         return;
49     }
50 
51     timer = task->ptr;
52     if (timer == NULL) {
53         LOGE(TAG, "Timer is NULL");
54         return;
55     }
56 
57     if (timer->disabled) {
58         LOGD(TAG, "User disable timer before timer callback.");
59         return;
60     }
61 
62     if (timer->timeoutHandle != NULL) {
63         timer->timeoutHandle(timer->data);
64     }
65     return;
66 }
67 
TimerGetRemainTime(Timer * timer,uint32_t * remainTimeMsPtr)68 int32_t TimerGetRemainTime(Timer *timer, uint32_t *remainTimeMsPtr)
69 {
70     struct itimerspec currValue = {{0}, {0}};
71 
72     if (timer == NULL || remainTimeMsPtr == NULL) {
73         LOGE(TAG, "Invalid timer parameter");
74         return NSTACKX_EINVAL;
75     }
76 
77     if (timer_gettime(TASKFD_TO_TIMERID(timer->task.taskfd), &currValue) < 0) {
78         LOGE(TAG, "timerfd_gettime() failed! %d", errno);
79         return NSTACKX_EFAILED;
80     }
81 
82     *remainTimeMsPtr = (uint32_t)(currValue.it_value.tv_sec * NSTACKX_MILLI_TICKS +
83         currValue.it_value.tv_nsec / NSTACKX_MILLI_TICKS / NSTACKX_MILLI_TICKS);
84 
85     return NSTACKX_EOK;
86 }
87 
TimerSetTimeout(Timer * timer,uint32_t timeoutMs,uint8_t repeated)88 int32_t TimerSetTimeout(Timer *timer, uint32_t timeoutMs, uint8_t repeated)
89 {
90     struct itimerspec ts;
91 
92     if (timer == NULL) {
93         LOGE(TAG, "Invalid timer parameter");
94         return NSTACKX_EINVAL;
95     }
96 
97     (void)memset_s(&ts, sizeof(ts), 0, sizeof(ts));
98     if (timeoutMs) {
99         ts.it_value.tv_sec = timeoutMs / NSTACKX_MILLI_TICKS;
100         ts.it_value.tv_nsec = (timeoutMs % NSTACKX_MILLI_TICKS) * NSTACKX_NANO_SEC_PER_MILLI_SEC;
101         if (repeated) {
102             ts.it_interval.tv_sec = ts.it_value.tv_sec;
103             ts.it_interval.tv_nsec = ts.it_value.tv_nsec;
104         }
105         timer->disabled = NSTACKX_FALSE;
106     } else {
107         timer->disabled = NSTACKX_TRUE;
108     }
109 
110     if (timer_settime(TASKFD_TO_TIMERID(timer->task.taskfd), 0, &ts, NULL) < 0) {
111         LOGE(TAG, "timerfd_settime failed! %d", errno);
112         return NSTACKX_EFAILED;
113     }
114 
115     return NSTACKX_EOK;
116 }
117 
TimerTimeoutHandle(union sigval v)118 static void TimerTimeoutHandle(union sigval v)
119 {
120     EpollTask *task = (EpollTask *)(v.sival_ptr);
121 
122     if (RunEpollTask((void *)task, EPOLLIN) != NSTACKX_EOK) {
123         LOGE(TAG, "TimerTimeoutHandle failed!");
124     }
125 }
126 
TimerStart(EpollDesc epollfd,uint32_t ms,uint8_t repeated,TimeoutHandle handle,void * data)127 Timer *TimerStart(EpollDesc epollfd, uint32_t ms, uint8_t repeated, TimeoutHandle handle, void *data)
128 {
129     struct sigevent evp;
130     timer_t timerid;
131     Timer *timer = calloc(1, sizeof(Timer));
132     if (timer == NULL) {
133         LOGE(TAG, "timer malloc failed");
134         return NULL;
135     }
136 
137     timer->timeoutHandle = handle;
138     timer->data = data;
139     timer->disabled = NSTACKX_FALSE;
140 
141     (void)memset_s(&evp, sizeof(struct sigevent), 0, sizeof(struct sigevent));
142     evp.sigev_value.sival_ptr = (void *)&timer->task;
143     evp.sigev_notify = SIGEV_THREAD;
144     evp.sigev_notify_function = TimerTimeoutHandle;
145 
146     if (timer_create(CLOCK_REALTIME, &evp, &timerid) < 0) {
147         LOGE(TAG, "timer create failed! errno %d", errno);
148         TimerDelete(timer);
149         return NULL;
150     }
151     timer->task.taskfd = TIMERID_TO_TASKFD(timerid);
152     timer->task.epollfd = epollfd;
153     timer->task.readHandle = TimerReadHandle;
154     timer->task.writeHandle = NULL;
155     timer->task.errorHandle = NULL;
156     timer->task.ptr = timer;
157 
158     if (TimerSetTimeout(timer, ms, repeated) != NSTACKX_EOK) {
159         TimerDelete(timer);
160         return NULL;
161     }
162 
163     return timer;
164 }
165