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 epollFd = -1;
105 }
106
107 if (awakenFd >= 0) {
108 close(awakenFd);
109 awakenFd = -1;
110 }
111
112 return false;
113 }
114
WaitFor(std::unique_lock<std::mutex> & lock,int64_t nanoseconds)115 bool EpollIoWaiter::WaitFor(std::unique_lock<std::mutex> &lock, int64_t nanoseconds)
116 {
117 if (epollFd_ < 0) {
118 HILOGE("WaitFor: MUST initialized before waiting");
119 return false;
120 }
121
122 // Increasment of waiting count MUST be done before unlock.
123 ++waitingCount_;
124 lock.unlock();
125
126 // Block on epoll_wait outside of the lock.
127 struct epoll_event epollEvents[MAX_EPOLL_EVENTS_SIZE];
128 int32_t retVal = epoll_wait(epollFd_, epollEvents, MAX_EPOLL_EVENTS_SIZE, NanosecondsToTimeout(nanoseconds));
129 // Decrease waiting count after block at once.
130 --waitingCount_;
131
132 bool result = true;
133 if (retVal < 0) {
134 if (errno != EINTR && errno != EINVAL) {
135 char errmsg[MAX_ERRORMSG_LEN] = {0};
136 GetLastErr(errmsg, MAX_ERRORMSG_LEN);
137 HILOGE("WaitFor: Failed to wait epoll, %{public}s", errmsg);
138 result = false;
139 }
140 } else {
141 for (int32_t i = 0; i < retVal; ++i) {
142 if (epollEvents[i].data.fd == awakenFd_) {
143 // Drain awaken pipe, if woken up by it.
144 DrainAwakenPipe();
145 continue;
146 }
147
148 // Transform epoll events into file descriptor listener events.
149 uint32_t events = 0;
150 if ((epollEvents[i].events & EPOLLIN) != 0) {
151 events |= FILE_DESCRIPTOR_INPUT_EVENT;
152 }
153
154 if ((epollEvents[i].events & EPOLLOUT) != 0) {
155 events |= FILE_DESCRIPTOR_OUTPUT_EVENT;
156 }
157
158 if ((epollEvents[i].events & (EPOLLHUP)) != 0) {
159 events |= FILE_DESCRIPTOR_SHUTDOWN_EVENT;
160 }
161
162 if ((epollEvents[i].events & (EPOLLERR)) != 0) {
163 events |= FILE_DESCRIPTOR_EXCEPTION_EVENT;
164 }
165
166 if (callback_) {
167 callback_(epollEvents[i].data.fd, events);
168 }
169 }
170 }
171
172 lock.lock();
173 return result;
174 }
175
NotifyOne()176 void EpollIoWaiter::NotifyOne()
177 {
178 // Epoll only support wake up all waiting thread.
179 NotifyAll();
180 }
181
NotifyAll()182 void EpollIoWaiter::NotifyAll()
183 {
184 if (awakenFd_ < 0) {
185 HILOGE("NotifyAll: MUST initialized before notifying");
186 return;
187 }
188
189 // Not waiting, so nothing to do.
190 if (waitingCount_.load() == 0) {
191 return;
192 }
193
194 static const uint64_t increment = 1;
195 ssize_t retVal = write(awakenFd_, &increment, sizeof(increment));
196 if (retVal < 0) {
197 char errmsg[MAX_ERRORMSG_LEN] = {0};
198 GetLastErr(errmsg, MAX_ERRORMSG_LEN);
199 HILOGE("NotifyAll: Failed to write data into awaken pipe, %{public}s", errmsg);
200 }
201 }
202
SupportListeningFileDescriptor() const203 bool EpollIoWaiter::SupportListeningFileDescriptor() const
204 {
205 return true;
206 }
207
AddFileDescriptor(int32_t fileDescriptor,uint32_t events)208 bool EpollIoWaiter::AddFileDescriptor(int32_t fileDescriptor, uint32_t events)
209 {
210 if ((fileDescriptor < 0) || ((events & FILE_DESCRIPTOR_EVENTS_MASK) == 0)) {
211 HILOGE("AddFileDescriptor(%{public}d, %{public}u): Invalid parameter", fileDescriptor, events);
212 return false;
213 }
214
215 if (epollFd_ < 0) {
216 HILOGE("AddFileDescriptor: MUST initialized before adding fds");
217 return false;
218 }
219
220 // Transform file descriptor listener events into epoll events.
221 uint32_t epollEvents = 0;
222 if ((events & FILE_DESCRIPTOR_INPUT_EVENT) != 0) {
223 epollEvents |= EPOLLIN;
224 }
225
226 if ((events & FILE_DESCRIPTOR_OUTPUT_EVENT) != 0) {
227 epollEvents |= EPOLLOUT;
228 }
229
230 if (EpollCtrl(epollFd_, EPOLL_CTL_ADD, fileDescriptor, epollEvents) < 0) {
231 char errmsg[MAX_ERRORMSG_LEN] = {0};
232 GetLastErr(errmsg, MAX_ERRORMSG_LEN);
233 HILOGE("AddFileDescriptor: Failed to add file descriptor into epoll, %{public}s", errmsg);
234 return false;
235 }
236
237 return true;
238 }
239
RemoveFileDescriptor(int32_t fileDescriptor)240 void EpollIoWaiter::RemoveFileDescriptor(int32_t fileDescriptor)
241 {
242 if (fileDescriptor < 0) {
243 HILOGE("RemoveFileDescriptor: Invalid param while removing fd, fd = %{public}d", fileDescriptor);
244 return;
245 }
246
247 if (epollFd_ < 0) {
248 HILOGE("RemoveFileDescriptor: MUST initialized before removing fds");
249 return;
250 }
251
252 if (EpollCtrl(epollFd_, EPOLL_CTL_DEL, fileDescriptor, 0) < 0) {
253 char errmsg[MAX_ERRORMSG_LEN] = {0};
254 GetLastErr(errmsg, MAX_ERRORMSG_LEN);
255 HILOGE("RemoveFileDescriptor: Failed to remove file descriptor from epoll, %{public}s", errmsg);
256 return;
257 }
258 }
259
DrainAwakenPipe() const260 void EpollIoWaiter::DrainAwakenPipe() const
261 {
262 uint64_t value = 0;
263 ssize_t retVal = read(awakenFd_, &value, sizeof(value));
264 if (retVal < 0) {
265 char errmsg[MAX_ERRORMSG_LEN] = {0};
266 GetLastErr(errmsg, MAX_ERRORMSG_LEN);
267 HILOGE("DrainAwakenPipe: Failed to read data from awaken pipe, %{public}s", errmsg);
268 }
269 }
270
SetFileDescriptorEventCallback(const IoWaiter::FileDescriptorEventCallback & callback)271 void EpollIoWaiter::SetFileDescriptorEventCallback(const IoWaiter::FileDescriptorEventCallback &callback)
272 {
273 callback_ = callback;
274 }
275 } // namespace AppExecFwk
276 } // namespace OHOS
277