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