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_epoll.h"
17 #include "nstackx_log.h"
18 #include "nstackx_list.h"
19 #include "nstackx_error.h"
20
21 #define TAG "nStackXEpoll"
22 #define MAX_EPOLL_SIZE 128
23 #define PRINT_RIGHT_MOVE 48
24 static List g_epollTaskList;
25 static bool g_isInit = false;
26 static pthread_mutex_t g_taskListMutex = PTHREAD_MUTEX_INITIALIZER;
27
28 typedef struct {
29 List list;
30 EpollTask *task;
31 } TaskList;
32
PrintTaskInfo(EpollTask * task)33 static inline void PrintTaskInfo(EpollTask *task)
34 {
35 uint64_t taskAddress = (uint64_t)task;
36 uint64_t readHandleAddress = (uint64_t)&task->readHandle;
37 LOGI(TAG, "task : %02x******%06x, task->epollfd: %d, task->taskFd: %d, task->readHandle: %02x******%06x",
38 (taskAddress >> PRINT_RIGHT_MOVE) & 0xff, taskAddress & 0xffffff, task->epollfd, task->taskfd,
39 (readHandleAddress >> PRINT_RIGHT_MOVE) & 0xff, readHandleAddress & 0xffffff);
40 }
41
PrintTaskDebugInfo(EpollTask * task,const char * str)42 static inline void PrintTaskDebugInfo(EpollTask *task, const char *str)
43 {
44 uint64_t taskAddress = (uint64_t)task;
45 uint64_t readHandleAddress = (uint64_t)&task->readHandle;
46 LOGD(TAG, "%s task : %02x******%06x, task->epollfd: %d, task->taskFd: %d, task->readHandle: %02x******%06x",
47 str, (taskAddress >> PRINT_RIGHT_MOVE) & 0xff, taskAddress & 0xffffff, task->epollfd, task->taskfd,
48 (readHandleAddress >> PRINT_RIGHT_MOVE) & 0xff, readHandleAddress & 0xffffff);
49 }
50
IsEpollTaskEqual(EpollTask * oldTask,EpollTask * newTask)51 static bool IsEpollTaskEqual(EpollTask *oldTask, EpollTask *newTask)
52 {
53 if (oldTask == newTask && oldTask->epollfd == newTask->epollfd &&
54 oldTask->taskfd == newTask->taskfd && oldTask->readHandle == newTask->readHandle) {
55 return true;
56 }
57 return false;
58 }
59
GetTaskFromList(EpollTask * task)60 static TaskList *GetTaskFromList(EpollTask *task)
61 {
62 if (pthread_mutex_lock(&g_taskListMutex) != 0) {
63 LOGE(TAG, "lock g_taskListMutex failed");
64 return NULL;
65 }
66 if (ListIsEmpty(&g_epollTaskList)) {
67 (void)pthread_mutex_unlock(&g_taskListMutex);
68 return NULL;
69 }
70 TaskList *taskList = NULL;
71 List *pos = NULL;
72 TaskList *node = NULL;
73 LIST_FOR_EACH(pos, &g_epollTaskList) {
74 node = (TaskList *)pos;
75 if (IsEpollTaskEqual(task, node->task)) {
76 taskList = node;
77 break;
78 }
79 }
80 (void)pthread_mutex_unlock(&g_taskListMutex);
81 return taskList;
82 }
83
AddTaskToList(EpollTask * task)84 static int32_t AddTaskToList(EpollTask *task)
85 {
86 if (task == NULL) {
87 return NSTACKX_EINVAL;
88 }
89
90 if (!g_isInit) {
91 if (pthread_mutex_lock(&g_taskListMutex) != 0) {
92 LOGE(TAG, "lock g_taskListMutex failed");
93 return NSTACKX_EFAILED;
94 }
95 if (!g_isInit) {
96 ListInitHead(&g_epollTaskList);
97 g_isInit = true;
98 }
99 (void)pthread_mutex_unlock(&g_taskListMutex);
100 }
101
102 TaskList *taskListCheck = GetTaskFromList(task);
103 if (taskListCheck != NULL) {
104 LOGE(TAG, "taskListCheck GetTaskFromList failed");
105 return NSTACKX_EFAILED;
106 }
107 TaskList *taskList = calloc(1, sizeof(TaskList));
108 if (taskList == NULL) {
109 LOGE(TAG, "calloc failed");
110 return NSTACKX_EFAILED;
111 }
112 taskList->task = task;
113 if (pthread_mutex_lock(&g_taskListMutex) != 0) {
114 free(taskList);
115 LOGE(TAG, "lock g_taskListMutex failed");
116 return NSTACKX_EFAILED;
117 }
118 ListInsertTail(&g_epollTaskList, &taskList->list);
119 (void)pthread_mutex_unlock(&g_taskListMutex);
120
121 return NSTACKX_EOK;
122 }
123
DelTaskFromList(EpollTask * task)124 static int32_t DelTaskFromList(EpollTask *task)
125 {
126 if (task == NULL) {
127 LOGE(TAG, "task is null");
128 return NSTACKX_EINVAL;
129 }
130 TaskList *taskList = GetTaskFromList(task);
131 if (taskList == NULL) {
132 LOGE(TAG, "task is not exist");
133 PrintTaskInfo(task);
134 return NSTACKX_EFAILED;
135 }
136 if (pthread_mutex_lock(&g_taskListMutex) != 0) {
137 LOGE(TAG, "lock g_taskListMutex failed");
138 return NSTACKX_EFAILED;
139 }
140 ListRemoveNode(&taskList->list);
141 (void)pthread_mutex_unlock(&g_taskListMutex);
142 free(taskList);
143 return NSTACKX_EOK;
144 }
145
RefreshEpollTask(EpollTask * task,uint32_t events)146 int32_t RefreshEpollTask(EpollTask *task, uint32_t events)
147 {
148 struct epoll_event event;
149 if (task == NULL) {
150 return NSTACKX_EINVAL;
151 }
152
153 TaskList *taskList = GetTaskFromList(task);
154 if (taskList == NULL) {
155 LOGE(TAG, "task is not exist");
156 PrintTaskInfo(task);
157 return NSTACKX_EFAILED;
158 }
159 event.data.ptr = task;
160 event.events = events;
161
162 if (epoll_ctl(task->epollfd, EPOLL_CTL_MOD, task->taskfd, &event) < 0) {
163 LOGE(TAG, "Refresh task failed: %d", errno);
164 return NSTACKX_EFAILED;
165 }
166
167 PrintTaskDebugInfo(task, "RefreshEpollTask");
168 return NSTACKX_EOK;
169 }
170
RegisterEpollTask(EpollTask * task,uint32_t events)171 int32_t RegisterEpollTask(EpollTask *task, uint32_t events)
172 {
173 struct epoll_event event;
174 if (task == NULL) {
175 return NSTACKX_EINVAL;
176 }
177
178 if (AddTaskToList(task) != NSTACKX_EOK) {
179 LOGE(TAG, "task is exist");
180 PrintTaskInfo(task);
181 return NSTACKX_EFAILED;
182 }
183
184 event.data.ptr = task;
185 event.events = events;
186 if (epoll_ctl(task->epollfd, EPOLL_CTL_ADD, task->taskfd, &event) < 0) {
187 LOGE(TAG, "Register task failed: %d", errno);
188 DelTaskFromList(task);
189 return NSTACKX_EFAILED;
190 }
191
192 PrintTaskDebugInfo(task, "RegisterEpollTask");
193 return NSTACKX_EOK;
194 }
195
DeRegisterEpollTask(EpollTask * task)196 int32_t DeRegisterEpollTask(EpollTask *task)
197 {
198 if (task == NULL) {
199 return NSTACKX_EINVAL;
200 }
201
202 if (DelTaskFromList(task) != NSTACKX_EOK) {
203 LOGE(TAG, "task is not exist");
204 PrintTaskInfo(task);
205 return NSTACKX_EFAILED;
206 }
207 if (epoll_ctl(task->epollfd, EPOLL_CTL_DEL, task->taskfd, NULL) < 0) {
208 LOGE(TAG, "De-register task failed: %d", errno);
209 return NSTACKX_EFAILED;
210 }
211
212 PrintTaskDebugInfo(task, "DeRegisterEpollTask");
213 return NSTACKX_EOK;
214 }
215
CreateEpollDesc(void)216 EpollDesc CreateEpollDesc(void)
217 {
218 return epoll_create(1);
219 }
220
EpollLoop(EpollDesc epollfd,int32_t timeout)221 int32_t EpollLoop(EpollDesc epollfd, int32_t timeout)
222 {
223 int32_t i, nfds;
224 EpollTask *task = NULL;
225 struct epoll_event events[MAX_EPOLL_SIZE];
226
227 nfds = epoll_wait(epollfd, events, MAX_EPOLL_SIZE, timeout);
228 if (nfds < 0) {
229 if (errno == EINTR) {
230 LOGD(TAG, "epoll_wait EINTR");
231 return NSTACKX_EINTR;
232 }
233 LOGE(TAG, "epoll_wait returned n=%d, error: %d", nfds, errno);
234 return NSTACKX_EFAILED;
235 }
236
237 for (i = 0; i < nfds; i++) {
238 task = events[i].data.ptr;
239 if (task == NULL) {
240 continue;
241 }
242
243 if (events[i].events & EPOLLIN) {
244 TaskList *taskList = GetTaskFromList(task);
245 if (taskList == NULL) {
246 LOGE(TAG, "task is not exist");
247 PrintTaskInfo(task);
248 return NSTACKX_OVERFLOW;
249 }
250 if (task->readHandle != NULL) {
251 task->readHandle(task);
252 }
253 }
254
255 if (events[i].events & EPOLLOUT) {
256 if (task->writeHandle != NULL) {
257 task->writeHandle(task);
258 }
259 }
260 }
261
262 return ((nfds > 0) ? nfds : NSTACKX_ETIMEOUT);
263 }
264