1 /*
2 * Copyright (c) 2021-2023 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 "epoll_io_waiter.h"
17
18 #include <chrono>
19
20 #include <mutex>
21 #include <sys/epoll.h>
22 #include <sys/eventfd.h>
23 #include <unistd.h>
24
25 #include "event_handler_utils.h"
26 #include "event_logger.h"
27 #include "file_descriptor_listener.h"
28
29 namespace OHOS {
30 namespace AppExecFwk {
31 namespace {
32 const size_t MAX_EPOLL_EVENTS_SIZE = 8;
33 DEFINE_EH_HILOG_LABEL("EpollIoWaiter");
34
EpollCtrl(int32_t epollFd,int32_t operation,int32_t fileDescriptor,uint32_t epollEvents)35 inline int32_t EpollCtrl(int32_t epollFd, int32_t operation, int32_t fileDescriptor, uint32_t epollEvents)
36 {
37 struct epoll_event epollEvent = {
38 .events = epollEvents,
39 .data = {.fd = fileDescriptor},
40 };
41
42 return epoll_ctl(epollFd, operation, fileDescriptor, &epollEvent);
43 }
44 } // unnamed namespace
45
~EpollIoWaiter()46 EpollIoWaiter::~EpollIoWaiter()
47 {
48 HILOGD("enter");
49 // Close all valid file descriptors.
50 if (epollFd_ >= 0) {
51 close(epollFd_);
52 epollFd_ = -1;
53 }
54
55 if (awakenFd_ >= 0) {
56 close(awakenFd_);
57 awakenFd_ = -1;
58 }
59 }
60
Init()61 bool EpollIoWaiter::Init()
62 {
63 HILOGD("enter");
64 if (epollFd_ >= 0) {
65 HILOGE("Already initialized");
66 return true;
67 }
68
69 int32_t epollFd = -1;
70 int32_t awakenFd = -1;
71
72 do {
73 epollFd = epoll_create(MAX_EPOLL_EVENTS_SIZE);
74 if (epollFd < 0) {
75 char errmsg[MAX_ERRORMSG_LEN] = {0};
76 GetLastErr(errmsg, MAX_ERRORMSG_LEN);
77 HILOGE("Failed to create epoll, %{public}s", errmsg);
78 break;
79 }
80
81 awakenFd = eventfd(0, EFD_CLOEXEC | EFD_NONBLOCK);
82 if (awakenFd < 0) {
83 char errmsg[MAX_ERRORMSG_LEN] = {0};
84 GetLastErr(errmsg, MAX_ERRORMSG_LEN);
85 HILOGE("Failed to create event fd, %{public}s", errmsg);
86 break;
87 }
88
89 // Add readable file descriptor of pipe, used to wake up blocked thread.
90 if (EpollCtrl(epollFd, EPOLL_CTL_ADD, awakenFd, EPOLLIN | EPOLLET) < 0) {
91 char errmsg[MAX_ERRORMSG_LEN] = {0};
92 GetLastErr(errmsg, MAX_ERRORMSG_LEN);
93 HILOGE("Failed to add awaken file descriptor into epoll, %{public}s", errmsg);
94 break;
95 }
96
97 // Prepare epoll successfully.
98 epollFd_ = epollFd;
99 awakenFd_ = awakenFd;
100
101 return true;
102 } while (0);
103
104 // If any error happened, close all valid file descriptors.
105 if (epollFd >= 0) {
106 close(epollFd);
107 }
108
109 if (awakenFd >= 0) {
110 close(awakenFd);
111 }
112
113 return false;
114 }
115
WaitFor(std::unique_lock<std::mutex> & lock,int64_t nanoseconds)116 bool EpollIoWaiter::WaitFor(std::unique_lock<std::mutex> &lock, int64_t nanoseconds)
117 {
118 if (epollFd_ < 0) {
119 HILOGE("MUST initialized before waiting");
120 return false;
121 }
122
123 // Increasment of waiting count MUST be done before unlock.
124 ++waitingCount_;
125 lock.unlock();
126
127 // Block on epoll_wait outside of the lock.
128 struct epoll_event epollEvents[MAX_EPOLL_EVENTS_SIZE];
129 int32_t retVal = epoll_wait(epollFd_, epollEvents, MAX_EPOLL_EVENTS_SIZE, NanosecondsToTimeout(nanoseconds));
130 // Decrease waiting count after block at once.
131 --waitingCount_;
132 if (waitingCount_ < 0) {
133 HILOGE("WaitingCount_ become negative: %{public}d", waitingCount_.load());
134 }
135
136 bool result = true;
137 if (retVal < 0) {
138 if (errno != EINTR && errno != EINVAL) {
139 char errmsg[MAX_ERRORMSG_LEN] = {0};
140 GetLastErr(errmsg, MAX_ERRORMSG_LEN);
141 HILOGE("Failed to wait epoll, %{public}s", errmsg);
142 result = false;
143 }
144 } else {
145 for (int32_t i = 0; i < retVal; ++i) {
146 if (epollEvents[i].data.fd == awakenFd_) {
147 // Drain awaken pipe, if woken up by it.
148 DrainAwakenPipe();
149 continue;
150 }
151
152 // Transform epoll events into file descriptor listener events.
153 uint32_t events = 0;
154 if ((epollEvents[i].events & EPOLLIN) != 0) {
155 events |= FILE_DESCRIPTOR_INPUT_EVENT;
156 }
157
158 if ((epollEvents[i].events & EPOLLOUT) != 0) {
159 events |= FILE_DESCRIPTOR_OUTPUT_EVENT;
160 }
161
162 if ((epollEvents[i].events & (EPOLLHUP)) != 0) {
163 events |= FILE_DESCRIPTOR_SHUTDOWN_EVENT;
164 }
165
166 if ((epollEvents[i].events & (EPOLLERR)) != 0) {
167 events |= FILE_DESCRIPTOR_EXCEPTION_EVENT;
168 }
169 std::string taskName = GetTaskNameMap(epollEvents[i].data.fd);
170 if (callback_) {
171 callback_(epollEvents[i].data.fd, events, taskName);
172 }
173 }
174 }
175
176 lock.lock();
177 return result;
178 }
179
NotifyOne()180 void EpollIoWaiter::NotifyOne()
181 {
182 // Epoll only support wake up all waiting thread.
183 NotifyAll();
184 }
185
NotifyAll()186 void EpollIoWaiter::NotifyAll()
187 {
188 if (awakenFd_ < 0) {
189 HILOGE("MUST initialized before notifying");
190 return;
191 }
192
193 // Not waiting, so nothing to do.
194 if (waitingCount_.load() == 0) {
195 return;
196 }
197
198 static const uint64_t increment = 1;
199 ssize_t retVal = write(awakenFd_, &increment, sizeof(increment));
200 if (retVal < 0) {
201 char errmsg[MAX_ERRORMSG_LEN] = {0};
202 GetLastErr(errmsg, MAX_ERRORMSG_LEN);
203 HILOGE("Failed to write data into awaken pipe, %{public}s", errmsg);
204 }
205 }
206
SupportListeningFileDescriptor() const207 bool EpollIoWaiter::SupportListeningFileDescriptor() const
208 {
209 return true;
210 }
211
AddFileDescriptor(int32_t fileDescriptor,uint32_t events,const std::string & taskName)212 bool EpollIoWaiter::AddFileDescriptor(int32_t fileDescriptor, uint32_t events, const std::string &taskName)
213 {
214 if ((fileDescriptor < 0) || ((events & FILE_DESCRIPTOR_EVENTS_MASK) == 0)) {
215 HILOGE("%{public}d, %{public}u: Invalid parameter", fileDescriptor, events);
216 return false;
217 }
218
219 if (epollFd_ < 0) {
220 HILOGE("MUST initialized before adding fds");
221 return false;
222 }
223
224 // Transform file descriptor listener events into epoll events.
225 uint32_t epollEvents = 0;
226 if ((events & FILE_DESCRIPTOR_INPUT_EVENT) != 0) {
227 epollEvents |= EPOLLIN;
228 }
229
230 if ((events & FILE_DESCRIPTOR_OUTPUT_EVENT) != 0) {
231 epollEvents |= EPOLLOUT;
232 }
233
234 if (EpollCtrl(epollFd_, EPOLL_CTL_ADD, fileDescriptor, epollEvents) < 0) {
235 char errmsg[MAX_ERRORMSG_LEN] = {0};
236 GetLastErr(errmsg, MAX_ERRORMSG_LEN);
237 HILOGE("Failed to add file descriptor into epoll, %{public}s", errmsg);
238 return false;
239 }
240 InsertTaskNameMap(fileDescriptor, taskName);
241 return true;
242 }
243
RemoveFileDescriptor(int32_t fileDescriptor)244 void EpollIoWaiter::RemoveFileDescriptor(int32_t fileDescriptor)
245 {
246 if (fileDescriptor < 0) {
247 HILOGE("Invalid param while removing fd, fd = %{public}d", fileDescriptor);
248 return;
249 }
250
251 if (epollFd_ < 0) {
252 HILOGE("MUST initialized before removing fds");
253 return;
254 }
255
256 if (EpollCtrl(epollFd_, EPOLL_CTL_DEL, fileDescriptor, 0) < 0) {
257 char errmsg[MAX_ERRORMSG_LEN] = {0};
258 GetLastErr(errmsg, MAX_ERRORMSG_LEN);
259 HILOGE("Failed to remove file descriptor from epoll, %{public}s", errmsg);
260 return;
261 }
262 EraseTaskNameMap(fileDescriptor);
263 }
264
DrainAwakenPipe() const265 void EpollIoWaiter::DrainAwakenPipe() const
266 {
267 uint64_t value = 0;
268 ssize_t retVal = read(awakenFd_, &value, sizeof(value));
269 if (retVal < 0) {
270 char errmsg[MAX_ERRORMSG_LEN] = {0};
271 GetLastErr(errmsg, MAX_ERRORMSG_LEN);
272 HILOGE("Failed to read data from awaken pipe, %{public}s", errmsg);
273 }
274 }
275
SetFileDescriptorEventCallback(const IoWaiter::FileDescriptorEventCallback & callback)276 void EpollIoWaiter::SetFileDescriptorEventCallback(const IoWaiter::FileDescriptorEventCallback &callback)
277 {
278 callback_ = callback;
279 }
280
281
InsertTaskNameMap(int32_t fileDescriptor,const std::string & taskName)282 void EpollIoWaiter::InsertTaskNameMap(int32_t fileDescriptor, const std::string& taskName)
283 {
284 std::lock_guard<std::mutex> lock(taskNameMapLock);
285 taskNameMap_.emplace(fileDescriptor, taskName);
286 }
287
EraseTaskNameMap(int32_t fileDescriptor)288 void EpollIoWaiter::EraseTaskNameMap(int32_t fileDescriptor)
289 {
290 std::lock_guard<std::mutex> lock(taskNameMapLock);
291 taskNameMap_.erase(fileDescriptor);
292 }
293
GetTaskNameMap(int32_t fileDescriptor)294 std::string EpollIoWaiter::GetTaskNameMap(int32_t fileDescriptor)
295 {
296 std::lock_guard<std::mutex> lock(taskNameMapLock);
297 auto it = taskNameMap_.find(fileDescriptor);
298 if (it == taskNameMap_.end()) {
299 return std::string();
300 }
301 return it->second;
302 }
303 } // namespace AppExecFwk
304 } // namespace OHOS
305