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 "platform/include/alarm.h"
17 #include <stdlib.h>
18 #include <unistd.h>
19 #include <sys/timerfd.h>
20 #include "platform/include/thread.h"
21 #include "platform/include/reactor.h"
22 #include "platform/include/mutex.h"
23 #include "platform/include/platform_def.h"
24 #include "securec.h"
25
26 #define BT_CLOCK_MONOTONIC CLOCK_MONOTONIC
27
28 static const char *g_defaultName = "bt-alarm";
29
30 static Thread *g_alarmThread = NULL;
31
32 typedef struct {
33 void *parameter;
34 AlarmCallback run;
35 } AlarmContext;
36
37 typedef struct Alarm {
38 int timerFd;
39 Mutex *mutex;
40 bool isPeriodic;
41 AlarmContext context;
42 ReactorItem *reactorItem;
43 char name[ALARM_NAME_SIZE + 1];
44 } AlarmInternal;
45
AlarmModuleInit()46 int32_t AlarmModuleInit()
47 {
48 g_alarmThread = ThreadCreate("Stack-Alarm");
49 if (g_alarmThread == NULL) {
50 LOG_ERROR("Alarm thread create failed.");
51 return -1;
52 }
53 return 0;
54 }
55
AlarmModuleCleanup()56 void AlarmModuleCleanup()
57 {
58 if (g_alarmThread == NULL) {
59 return;
60 }
61
62 ThreadDelete(g_alarmThread);
63 g_alarmThread = NULL;
64 }
65
AlarmNotify(void * parameter)66 static void AlarmNotify(void *parameter)
67 {
68 Alarm *alarm = (Alarm *)parameter;
69
70 MutexLock(alarm->mutex);
71
72 AlarmCallback run = alarm->context.run;
73 void *param = alarm->context.parameter;
74
75 uint64_t value = 0;
76 int ret = read(alarm->timerFd, &value, sizeof(uint64_t));
77 if (ret == sizeof(uint64_t)) {
78 if (value > 1) {
79 LOG_WARN("Alarm has expired more than one times.");
80 }
81 } else {
82 if (errno == EAGAIN) {
83 LOG_INFO("Alarm is stopped or reset before callback called.");
84 } else {
85 LOG_ERROR("Alarm read value failed, error no: %{public}d.", errno);
86 }
87 run = NULL;
88 }
89 MutexUnlock(alarm->mutex);
90
91 if (run) {
92 run(param);
93 }
94 }
95
AlarmCreate(const char * name,const bool isPeriodic)96 Alarm *AlarmCreate(const char *name, const bool isPeriodic)
97 {
98 Alarm *alarm = (Alarm *)calloc(1, (sizeof(Alarm)));
99 if (alarm == NULL) {
100 LOG_ERROR("Failed to call calloc in func AlarmCreate");
101 return NULL;
102 }
103
104 if (name != NULL) {
105 (void)strncpy_s(alarm->name, ALARM_NAME_SIZE + 1, name, ALARM_NAME_SIZE);
106 } else {
107 (void)strncpy_s(alarm->name, ALARM_NAME_SIZE + 1, g_defaultName, ALARM_NAME_SIZE);
108 }
109
110 int timerFd = timerfd_create(BT_CLOCK_MONOTONIC, TFD_NONBLOCK);
111 if (timerFd == -1) {
112 LOG_ERROR("Alarm create timer-fd failed, error no: %{public}d.", errno);
113 goto ERROR;
114 }
115
116 Mutex *mutex = MutexCreate();
117 if (mutex == NULL) {
118 close(timerFd);
119 LOG_ERROR("Alarm create mutex failed.");
120 goto ERROR;
121 }
122
123 alarm->timerFd = timerFd;
124 alarm->isPeriodic = isPeriodic;
125 alarm->mutex = mutex;
126
127 ReactorItem *item =
128 ReactorRegister(ThreadGetReactor(g_alarmThread), alarm->timerFd, (void *)alarm, AlarmNotify, NULL);
129 if (item == NULL) {
130 close(timerFd);
131 LOG_ERROR("Alarm register reactor failed.");
132 goto ERROR;
133 }
134
135 alarm->reactorItem = item;
136
137 return alarm;
138
139 ERROR:
140
141 if (alarm != NULL) {
142 MutexDelete(alarm->mutex);
143 free(alarm);
144 }
145 return NULL;
146 }
147
AlarmDelete(Alarm * alarm)148 void AlarmDelete(Alarm *alarm)
149 {
150 if (alarm == NULL) {
151 return;
152 }
153
154 ReactorUnregister(alarm->reactorItem);
155 close(alarm->timerFd);
156 MutexDelete(alarm->mutex);
157 free(alarm);
158 }
159
AlarmSet(Alarm * alarm,uint64_t timeMs,AlarmCallback callback,void * parameter)160 int32_t AlarmSet(Alarm *alarm, uint64_t timeMs, AlarmCallback callback, void *parameter)
161 {
162 ASSERT(alarm);
163
164 MutexLock(alarm->mutex);
165
166 alarm->context.parameter = parameter;
167 alarm->context.run = callback;
168
169 struct itimerspec its = {0};
170 its.it_value.tv_sec = timeMs / MS_PER_SECOND;
171 its.it_value.tv_nsec = (timeMs % MS_PER_SECOND) * NS_PER_MS;
172 if (alarm->isPeriodic) {
173 its.it_interval = its.it_value;
174 }
175
176 if (timerfd_settime(alarm->timerFd, 0, &its, NULL) == -1) {
177 LOG_ERROR("Alarm settime failed, error no: %{public}d.", errno);
178 MutexUnlock(alarm->mutex);
179 return -1;
180 }
181
182 MutexUnlock(alarm->mutex);
183 return 0;
184 }
185
AlarmCancel(Alarm * alarm)186 void AlarmCancel(Alarm *alarm)
187 {
188 ASSERT(alarm);
189
190 MutexLock(alarm->mutex);
191 struct itimerspec its = {0};
192 if (timerfd_settime(alarm->timerFd, 0, &its, NULL) == -1) {
193 LOG_ERROR("Alarm cancel: settime 0 failed, error no: %{public}d.", errno);
194 }
195 MutexUnlock(alarm->mutex);
196 }