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 auto fileDescriptorInfo = GetFileDescriptorMap(epollEvents[i].data.fd);
170 if (callback_ && fileDescriptorInfo != nullptr) {
171 callback_(epollEvents[i].data.fd, events, fileDescriptorInfo->taskName_,
172 fileDescriptorInfo->priority_);
173 }
174 }
175 }
176
177 lock.lock();
178 return result;
179 }
180
NotifyOne()181 void EpollIoWaiter::NotifyOne()
182 {
183 // Epoll only support wake up all waiting thread.
184 NotifyAll();
185 }
186
NotifyAll()187 void EpollIoWaiter::NotifyAll()
188 {
189 if (awakenFd_ < 0) {
190 HILOGE("MUST initialized before notifying");
191 return;
192 }
193
194 // Not waiting, so nothing to do.
195 if (waitingCount_.load() == 0) {
196 return;
197 }
198
199 static const uint64_t increment = 1;
200 ssize_t retVal = write(awakenFd_, &increment, sizeof(increment));
201 if (retVal < 0) {
202 char errmsg[MAX_ERRORMSG_LEN] = {0};
203 GetLastErr(errmsg, MAX_ERRORMSG_LEN);
204 HILOGE("Failed to write data into awaken pipe, %{public}s", errmsg);
205 }
206 }
207
SupportListeningFileDescriptor() const208 bool EpollIoWaiter::SupportListeningFileDescriptor() const
209 {
210 return true;
211 }
212
AddFileDescriptor(int32_t fileDescriptor,uint32_t events,const std::string & taskName,const std::shared_ptr<FileDescriptorListener> & listener,EventQueue::Priority priority)213 bool EpollIoWaiter::AddFileDescriptor(int32_t fileDescriptor, uint32_t events, const std::string &taskName,
214 const std::shared_ptr<FileDescriptorListener>& listener, EventQueue::Priority priority)
215 {
216 if ((fileDescriptor < 0) || ((events & FILE_DESCRIPTOR_EVENTS_MASK) == 0)) {
217 HILOGE("%{public}d, %{public}u: Invalid parameter", fileDescriptor, events);
218 return false;
219 }
220
221 if (epollFd_ < 0) {
222 HILOGE("MUST initialized before adding fds");
223 return false;
224 }
225
226 // Transform file descriptor listener events into epoll events.
227 uint32_t epollEvents = 0;
228 if ((events & FILE_DESCRIPTOR_INPUT_EVENT) != 0) {
229 epollEvents |= EPOLLIN;
230 }
231
232 if ((events & FILE_DESCRIPTOR_OUTPUT_EVENT) != 0) {
233 epollEvents |= EPOLLOUT;
234 }
235
236 InsertFileDescriptorMap(fileDescriptor, taskName, priority, listener);
237 if (EpollCtrl(epollFd_, EPOLL_CTL_ADD, fileDescriptor, epollEvents) < 0) {
238 RemoveFileDescriptor(fileDescriptor);
239 char errmsg[MAX_ERRORMSG_LEN] = {0};
240 GetLastErr(errmsg, MAX_ERRORMSG_LEN);
241 HILOGE("Failed to add file descriptor into epoll, %{public}s", errmsg);
242 return false;
243 }
244 return true;
245 }
246
RemoveFileDescriptor(int32_t fileDescriptor)247 void EpollIoWaiter::RemoveFileDescriptor(int32_t fileDescriptor)
248 {
249 if (fileDescriptor < 0) {
250 HILOGE("Invalid param while removing fd, fd = %{public}d", fileDescriptor);
251 return;
252 }
253
254 if (epollFd_ < 0) {
255 HILOGE("MUST initialized before removing fds");
256 return;
257 }
258
259 if (EpollCtrl(epollFd_, EPOLL_CTL_DEL, fileDescriptor, 0) < 0) {
260 char errmsg[MAX_ERRORMSG_LEN] = {0};
261 GetLastErr(errmsg, MAX_ERRORMSG_LEN);
262 HILOGE("Failed to remove file descriptor from epoll, %{public}s", errmsg);
263 return;
264 }
265 EraseFileDescriptorMap(fileDescriptor);
266 }
267
DrainAwakenPipe() const268 void EpollIoWaiter::DrainAwakenPipe() const
269 {
270 uint64_t value = 0;
271 ssize_t retVal = read(awakenFd_, &value, sizeof(value));
272 if (retVal < 0) {
273 char errmsg[MAX_ERRORMSG_LEN] = {0};
274 GetLastErr(errmsg, MAX_ERRORMSG_LEN);
275 HILOGE("Failed to read data from awaken pipe, %{public}s", errmsg);
276 }
277 }
278
SetFileDescriptorEventCallback(const IoWaiter::FileDescriptorEventCallback & callback)279 void EpollIoWaiter::SetFileDescriptorEventCallback(const IoWaiter::FileDescriptorEventCallback &callback)
280 {
281 callback_ = callback;
282 }
283
InsertFileDescriptorMap(int32_t fileDescriptor,const std::string & taskName,EventQueue::Priority priority,const std::shared_ptr<FileDescriptorListener> & listener)284 void EpollIoWaiter::InsertFileDescriptorMap(int32_t fileDescriptor, const std::string& taskName,
285 EventQueue::Priority priority, const std::shared_ptr<FileDescriptorListener>& listener)
286 {
287 std::lock_guard<std::mutex> lock(fileDescriptorMapLock);
288 std::shared_ptr<FileDescriptorInfo> fileDescriptorInfo =
289 std::make_shared<FileDescriptorInfo>(taskName, priority, listener);
290 fileDescriptorMap_.emplace(fileDescriptor, fileDescriptorInfo);
291 }
292
EraseFileDescriptorMap(int32_t fileDescriptor)293 void EpollIoWaiter::EraseFileDescriptorMap(int32_t fileDescriptor)
294 {
295 std::lock_guard<std::mutex> lock(fileDescriptorMapLock);
296 fileDescriptorMap_.erase(fileDescriptor);
297 }
298
GetFileDescriptorMap(int32_t fileDescriptor)299 std::shared_ptr<FileDescriptorInfo> EpollIoWaiter::GetFileDescriptorMap(int32_t fileDescriptor)
300 {
301 std::lock_guard<std::mutex> lock(fileDescriptorMapLock);
302 auto it = fileDescriptorMap_.find(fileDescriptor);
303 if (it == fileDescriptorMap_.end()) {
304 HILOGW("EpollIoWaiter get file descriptor failed %{public}d", fileDescriptor);
305 return nullptr;
306 }
307 return it->second;
308 }
309 } // namespace AppExecFwk
310 } // namespace OHOS
311