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 
21 #define TAG "nStackXTimer"
22 
TimerDelete(Timer * timer)23 void TimerDelete(Timer *timer)
24 {
25     if (timer == NULL) {
26         return;
27     }
28     if (timer->task.taskfd != INVALID_TASK_DESC) {
29         if (DeRegisterEpollTask(&(timer->task)) != NSTACKX_EOK) {
30             LOGE(TAG, "DeRegisterEpollTask failed");
31         }
32         if (close(timer->task.taskfd) < 0) {
33             LOGE(TAG, "close timer task failed");
34         }
35         timer->task.taskfd = INVALID_TASK_DESC;
36     }
37     free(timer);
38 }
39 
TimerReadHandle(void * arg)40 static void TimerReadHandle(void *arg)
41 {
42     EpollTask *task = arg;
43     Timer *timer = NULL;
44     uint64_t exp;
45     if (task == NULL) {
46         LOGE(TAG, "Timer task is NULL");
47         return;
48     }
49 
50     timer = task->ptr;
51     if (timer == NULL) {
52         LOGE(TAG, "Timer is NULL");
53         return;
54     }
55 
56     if (timer->disabled) {
57         LOGD(TAG, "User disable timer before timer callback.");
58         return;
59     }
60 
61     if (read(task->taskfd, &exp, sizeof(exp)) != (ssize_t)(sizeof(uint64_t))) {
62         LOGE(TAG, "read invalid exp");
63         return;
64     }
65 
66     if (timer->timeoutHandle != NULL) {
67         timer->timeoutHandle(timer->data);
68     }
69     return;
70 }
71 
TimerGetRemainTime(Timer * timer,uint32_t * remainTimeMsPtr)72 int32_t TimerGetRemainTime(Timer *timer, uint32_t *remainTimeMsPtr)
73 {
74     struct itimerspec currValue = {{0}, {0}};
75 
76     if (timer == NULL || remainTimeMsPtr == NULL) {
77         LOGE(TAG, "Invalid timer parameter");
78         return NSTACKX_EINVAL;
79     }
80 
81     if (timerfd_gettime(timer->task.taskfd, &currValue) < 0) {
82         LOGE(TAG, "timerfd_gettime() failed! %d", errno);
83         return NSTACKX_EFAILED;
84     }
85 
86     *remainTimeMsPtr = (uint32_t)(currValue.it_value.tv_sec * NSTACKX_MILLI_TICKS +
87         currValue.it_value.tv_nsec / NSTACKX_MILLI_TICKS / NSTACKX_MILLI_TICKS);
88 
89     return NSTACKX_EOK;
90 }
91 
TimerSetTimeout(Timer * timer,uint32_t timeoutMs,uint8_t repeated)92 int32_t TimerSetTimeout(Timer *timer, uint32_t timeoutMs, uint8_t repeated)
93 {
94     struct itimerspec ts;
95 
96     if (timer == NULL) {
97         LOGE(TAG, "Invalid timer parameter");
98         return NSTACKX_EINVAL;
99     }
100 
101     (void)memset_s(&ts, sizeof(ts), 0, sizeof(ts));
102     if (timeoutMs) {
103         ts.it_value.tv_sec = timeoutMs / NSTACKX_MILLI_TICKS;
104         ts.it_value.tv_nsec = (timeoutMs % NSTACKX_MILLI_TICKS) * NSTACKX_NANO_SEC_PER_MILLI_SEC;
105         if (repeated) {
106             ts.it_interval.tv_sec = ts.it_value.tv_sec;
107             ts.it_interval.tv_nsec = ts.it_value.tv_nsec;
108         }
109         timer->disabled = NSTACKX_FALSE;
110     } else {
111         timer->disabled = NSTACKX_TRUE;
112     }
113 
114     if (timerfd_settime(timer->task.taskfd, 0, &ts, NULL) < 0) {
115         LOGE(TAG, "timerfd_settime failed! %d", errno);
116         return NSTACKX_EFAILED;
117     }
118 
119     return NSTACKX_EOK;
120 }
121 
TimerStart(EpollDesc epollfd,uint32_t ms,uint8_t repeated,TimeoutHandle handle,void * data)122 Timer *TimerStart(EpollDesc epollfd, uint32_t ms, uint8_t repeated, TimeoutHandle handle, void *data)
123 {
124     Timer *timer = calloc(1, sizeof(Timer));
125     if (timer == NULL) {
126         LOGE(TAG, "timer malloc failed");
127         return NULL;
128     }
129 
130     timer->timeoutHandle = handle;
131     timer->data = data;
132     timer->disabled = NSTACKX_FALSE;
133 
134     timer->task.taskfd = timerfd_create(CLOCK_MONOTONIC, TFD_CLOEXEC | TFD_NONBLOCK);
135     if (timer->task.taskfd < 0) {
136         LOGE(TAG, "timer create failed! errno %d", errno);
137         TimerDelete(timer);
138         return NULL;
139     }
140     timer->task.epollfd = epollfd;
141     timer->task.readHandle = TimerReadHandle;
142     timer->task.writeHandle = NULL;
143     timer->task.errorHandle = NULL;
144     timer->task.ptr = timer;
145 
146     if (TimerSetTimeout(timer, ms, repeated) != NSTACKX_EOK) {
147         TimerDelete(timer);
148         return NULL;
149     }
150 
151     if (RegisterEpollTask(&timer->task, EPOLLIN) != NSTACKX_EOK) {
152         LOGE(TAG, "RegisterEpollTask failed");
153         TimerDelete(timer);
154         return NULL;
155     }
156     return timer;
157 }
158