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 "deamon_io_waiter.h"
17
18 #include <chrono>
19
20 #include <mutex>
21 #include <sys/eventfd.h>
22 #include <unistd.h>
23
24 #include "event_handler_utils.h"
25 #include "event_logger.h"
26 #include "event_handler.h"
27 #include "frame_report_sched.h"
28 #ifdef RES_SCHED_ENABLE
29 #include "res_type.h"
30 #include "res_sched_client.h"
31 #endif
32
33 namespace OHOS {
34 namespace AppExecFwk {
35 namespace {
36 const size_t MAX_EPOLL_EVENTS_SIZE = 8;
37 DEFINE_EH_HILOG_LABEL("DeamonIoWaiter");
38
EpollCtrl(int32_t epollFd,int32_t operation,int32_t fileDescriptor,uint32_t epollEvents)39 inline int32_t EpollCtrl(int32_t epollFd, int32_t operation, int32_t fileDescriptor, uint32_t epollEvents)
40 {
41 struct epoll_event epollEvent = {
42 .events = epollEvents,
43 .data = {.fd = fileDescriptor},
44 };
45
46 return epoll_ctl(epollFd, operation, fileDescriptor, &epollEvent);
47 }
48 } // unnamed namespace
49
~DeamonIoWaiter()50 DeamonIoWaiter::~DeamonIoWaiter()
51 {
52 HILOGD("enter");
53 if (!isFinished_) {
54 StopEpollIoWaiter();
55 }
56 // Close all valid file descriptors.
57 if (epollFd_ >= 0) {
58 close(epollFd_);
59 epollFd_ = -1;
60 }
61
62 if (awakenFd_ >= 0) {
63 close(awakenFd_);
64 awakenFd_ = -1;
65 }
66 std::lock_guard<std::mutex> lock(fileDescriptorMapLock);
67 fileDescriptorMap_.clear();
68 }
69
GetInstance()70 DeamonIoWaiter& DeamonIoWaiter::GetInstance()
71 {
72 static DeamonIoWaiter DeamonIoWaiter;
73 return DeamonIoWaiter;
74 }
75
Init()76 bool DeamonIoWaiter::Init()
77 {
78 HILOGD("enter");
79 if (running_.load() == true) {
80 return true;
81 }
82 if (epollFd_ >= 0) {
83 HILOGE("Already initialized");
84 return true;
85 }
86
87 int32_t epollFd = -1;
88 int32_t awakenFd = -1;
89
90 do {
91 epollFd = epoll_create(MAX_EPOLL_EVENTS_SIZE);
92 if (epollFd < 0) {
93 char errmsg[MAX_ERRORMSG_LEN] = {0};
94 GetLastErr(errmsg, MAX_ERRORMSG_LEN);
95 HILOGE("Failed to create epoll, %{public}s", errmsg);
96 break;
97 }
98
99 awakenFd = eventfd(0, EFD_CLOEXEC | EFD_NONBLOCK);
100 if (awakenFd < 0) {
101 char errmsg[MAX_ERRORMSG_LEN] = {0};
102 GetLastErr(errmsg, MAX_ERRORMSG_LEN);
103 HILOGE("Failed to create event fd, %{public}s", errmsg);
104 break;
105 }
106
107 // Add readable file descriptor of pipe, used to wake up blocked thread.
108 if (EpollCtrl(epollFd, EPOLL_CTL_ADD, awakenFd, EPOLLIN | EPOLLET) < 0) {
109 char errmsg[MAX_ERRORMSG_LEN] = {0};
110 GetLastErr(errmsg, MAX_ERRORMSG_LEN);
111 HILOGE("Failed to add awaken file descriptor into epoll, %{public}s", errmsg);
112 break;
113 }
114
115 // Prepare epoll successfully.
116 epollFd_ = epollFd;
117 awakenFd_ = awakenFd;
118
119 return true;
120 } while (0);
121
122 // If any error happened, close all valid file descriptors.
123 if (epollFd >= 0) {
124 close(epollFd);
125 }
126
127 if (awakenFd >= 0) {
128 close(awakenFd);
129 }
130
131 return false;
132 }
133
StartEpollIoWaiter()134 void DeamonIoWaiter::StartEpollIoWaiter()
135 {
136 if (running_.exchange(true)) {
137 return;
138 }
139 auto task = std::bind(&DeamonIoWaiter::EpollWaitFor, this);
140 epollThread_ = std::make_unique<std::thread>(task);
141 }
142
StopEpollIoWaiter()143 void DeamonIoWaiter::StopEpollIoWaiter()
144 {
145 isFinished_ = true;
146 NotifyAll();
147 if (epollThread_ != nullptr && epollThread_->joinable()) {
148 epollThread_->join();
149 }
150 running_.store(false);
151 }
152
VsyncReport(const std::shared_ptr<EventHandler> & handler)153 void DeamonIoWaiter::VsyncReport(const std::shared_ptr<EventHandler> &handler)
154 {
155 auto runner = handler->GetEventRunner();
156 if (!runner) {
157 return;
158 }
159
160 if (runner == EventRunner::GetMainEventRunner()) {
161 FrameReport::GetInstance().ReportSchedEvent(FrameSchedEvent::UI_EVENT_HANDLE_BEGIN, {});
162 }
163 }
164
HandleFileDescriptorEvent(int32_t fileDescriptor,uint32_t events)165 void DeamonIoWaiter::HandleFileDescriptorEvent(int32_t fileDescriptor, uint32_t events)
166 __attribute__((no_sanitize("cfi")))
167 {
168 auto fileDescriptorInfo = GetFileDescriptorMap(fileDescriptor);
169 if (fileDescriptorInfo != nullptr && fileDescriptorInfo->listener_ != nullptr) {
170 auto handler = fileDescriptorInfo->listener_->GetOwner();
171 if (!handler) {
172 HILOGW("Owner of listener is released %{public}d.", fileDescriptor);
173 return;
174 }
175
176 if (fileDescriptorInfo->taskName_ == "vSyncTask") {
177 VsyncReport(handler);
178 return;
179 }
180
181 std::weak_ptr<FileDescriptorListener> wp = fileDescriptorInfo->listener_;
182 auto f = [fileDescriptor, events, wp]() {
183 auto listener = wp.lock();
184 if (!listener) {
185 HILOGW("Listener is released");
186 return;
187 }
188
189 if ((events & FILE_DESCRIPTOR_INPUT_EVENT) != 0) {
190 listener->OnReadable(fileDescriptor);
191 }
192
193 if ((events & FILE_DESCRIPTOR_OUTPUT_EVENT) != 0) {
194 listener->OnWritable(fileDescriptor);
195 }
196
197 if ((events & FILE_DESCRIPTOR_SHUTDOWN_EVENT) != 0) {
198 listener->OnShutdown(fileDescriptor);
199 }
200
201 if ((events & FILE_DESCRIPTOR_EXCEPTION_EVENT) != 0) {
202 listener->OnException(fileDescriptor);
203 }
204 };
205
206 HILOGD("Post fd %{public}d, task %{public}s, priority %{public}d.", fileDescriptor,
207 fileDescriptorInfo->taskName_.c_str(), fileDescriptorInfo->priority_);
208 // Post a high priority task to handle file descriptor events.
209 handler->PostTask(f, fileDescriptorInfo->taskName_, 0, fileDescriptorInfo->priority_);
210 }
211 }
212
HandleEpollEvents(struct epoll_event * epollEvents,int32_t eventsCount)213 void DeamonIoWaiter::HandleEpollEvents(struct epoll_event *epollEvents, int32_t eventsCount)
214 {
215 for (int32_t i = 0; i < eventsCount; ++i) {
216 if (epollEvents[i].data.fd == awakenFd_) {
217 // Drain awaken pipe, if woken up by it.
218 DrainAwakenPipe();
219 continue;
220 }
221
222 // Transform epoll events into file descriptor listener events.
223 uint32_t events = 0;
224 if ((epollEvents[i].events & EPOLLIN) != 0) {
225 events |= FILE_DESCRIPTOR_INPUT_EVENT;
226 }
227
228 if ((epollEvents[i].events & EPOLLOUT) != 0) {
229 events |= FILE_DESCRIPTOR_OUTPUT_EVENT;
230 }
231
232 if ((epollEvents[i].events & (EPOLLHUP)) != 0) {
233 events |= FILE_DESCRIPTOR_SHUTDOWN_EVENT;
234 }
235
236 if ((epollEvents[i].events & (EPOLLERR)) != 0) {
237 events |= FILE_DESCRIPTOR_EXCEPTION_EVENT;
238 }
239 HandleFileDescriptorEvent(epollEvents[i].data.fd, events);
240 }
241 }
242
EpollWaitFor()243 void DeamonIoWaiter::EpollWaitFor()
244 {
245 if (epollFd_ < 0) {
246 HILOGE("MUST initialized before waiting");
247 return;
248 }
249 HILOGD("Epoll io waiter start polling.");
250 pthread_setname_np(pthread_self(), "OS_EVENT_POLL");
251 #ifdef RES_SCHED_ENABLE
252 std::unordered_map<std::string, std::string> payload {
253 {"pid", std::to_string(getprocpid())}
254 };
255 uint32_t type = ResourceSchedule::ResType::RES_TYPE_REPORT_DISTRIBUTE_TID;
256 int64_t value = getproctid();
257 ResourceSchedule::ResSchedClient::GetInstance().ReportData(type, value, payload);
258 HILOGD("Epoll io waiter set thread sched. pid: %{public}d, tid: %{public}d", getprocpid(), getproctid());
259 #endif
260 while (!isFinished_) {
261 // Increasment of waiting count MUST be done before unlock.
262 ++waitingCount_;
263
264 // Block on epoll_wait outside of the lock.
265 struct epoll_event epollEvents[MAX_EPOLL_EVENTS_SIZE];
266 int32_t retVal = epoll_wait(epollFd_, epollEvents, MAX_EPOLL_EVENTS_SIZE, -1);
267 // Decrease waiting count after block at once.
268 --waitingCount_;
269 if (waitingCount_ < 0) {
270 HILOGE("WaitingCount_ become negative: %{public}d", waitingCount_.load());
271 }
272
273 if (retVal < 0) {
274 if (errno != EINTR && errno != EINVAL) {
275 char errmsg[MAX_ERRORMSG_LEN] = {0};
276 GetLastErr(errmsg, MAX_ERRORMSG_LEN);
277 HILOGE("Failed to wait epoll, %{public}s", errmsg);
278 }
279 } else {
280 HandleEpollEvents(epollEvents, retVal);
281 }
282 }
283 }
284
NotifyOne()285 void DeamonIoWaiter::NotifyOne()
286 {
287 // Epoll only support wake up all waiting thread.
288 NotifyAll();
289 }
290
NotifyAll()291 void DeamonIoWaiter::NotifyAll()
292 {
293 if (awakenFd_ < 0) {
294 HILOGE("MUST initialized before notifying");
295 return;
296 }
297
298 // Not waiting, so nothing to do.
299 if (waitingCount_.load() == 0) {
300 return;
301 }
302
303 static const uint64_t increment = 1;
304 ssize_t retVal = write(awakenFd_, &increment, sizeof(increment));
305 if (retVal < 0) {
306 char errmsg[MAX_ERRORMSG_LEN] = {0};
307 GetLastErr(errmsg, MAX_ERRORMSG_LEN);
308 HILOGE("Failed to write data into awaken pipe, %{public}s", errmsg);
309 }
310 }
311
SupportListeningFileDescriptor() const312 bool DeamonIoWaiter::SupportListeningFileDescriptor() const
313 {
314 return true;
315 }
316
AddFileDescriptor(int32_t fileDescriptor,uint32_t events,const std::string & taskName,const std::shared_ptr<FileDescriptorListener> & listener,EventQueue::Priority priority)317 bool DeamonIoWaiter::AddFileDescriptor(int32_t fileDescriptor, uint32_t events, const std::string &taskName,
318 const std::shared_ptr<FileDescriptorListener>& listener, EventQueue::Priority priority)
319 {
320 if ((fileDescriptor < 0) || ((events & FILE_DESCRIPTOR_EVENTS_MASK) == 0)) {
321 HILOGE("%{public}d, %{public}u: Invalid parameter", fileDescriptor, events);
322 return false;
323 }
324
325 if (epollFd_ < 0) {
326 HILOGE("MUST initialized before adding fds");
327 return false;
328 }
329
330 // Transform file descriptor listener events into epoll events.
331 uint32_t epollEvents = 0;
332 if ((events & FILE_DESCRIPTOR_INPUT_EVENT) != 0) {
333 epollEvents |= EPOLLIN;
334 }
335
336 if ((events & FILE_DESCRIPTOR_OUTPUT_EVENT) != 0) {
337 epollEvents |= EPOLLOUT;
338 }
339
340 InsertFileDescriptorMap(fileDescriptor, taskName, priority, listener);
341 if (EpollCtrl(epollFd_, EPOLL_CTL_ADD, fileDescriptor, epollEvents | EPOLLET) < 0) {
342 RemoveFileDescriptor(fileDescriptor);
343 char errmsg[MAX_ERRORMSG_LEN] = {0};
344 GetLastErr(errmsg, MAX_ERRORMSG_LEN);
345 HILOGE("Failed to add file descriptor into epoll, %{public}s", errmsg);
346 return false;
347 }
348 HILOGD("DeamonIoWaiter add file %{public}d, %{public}s, %{public}d", fileDescriptor, taskName.c_str(), priority);
349 return true;
350 }
351
RemoveFileDescriptor(int32_t fileDescriptor)352 void DeamonIoWaiter::RemoveFileDescriptor(int32_t fileDescriptor)
353 {
354 if (fileDescriptor < 0) {
355 HILOGE("Invalid param while removing fd: %{public}d", fileDescriptor);
356 return;
357 }
358
359 if (epollFd_ < 0) {
360 HILOGE("MUST initialized before removing fds");
361 return;
362 }
363
364 if (EpollCtrl(epollFd_, EPOLL_CTL_DEL, fileDescriptor, 0) < 0) {
365 char errmsg[MAX_ERRORMSG_LEN] = {0};
366 GetLastErr(errmsg, MAX_ERRORMSG_LEN);
367 HILOGE("Failed to remove file descriptor from epoll, %{public}s", errmsg);
368 return;
369 }
370 EraseFileDescriptorMap(fileDescriptor);
371 }
372
DrainAwakenPipe() const373 void DeamonIoWaiter::DrainAwakenPipe() const
374 {
375 uint64_t value = 0;
376 ssize_t retVal = read(awakenFd_, &value, sizeof(value));
377 if (retVal < 0) {
378 char errmsg[MAX_ERRORMSG_LEN] = {0};
379 GetLastErr(errmsg, MAX_ERRORMSG_LEN);
380 HILOGE("Failed to read data from awaken pipe, %{public}s", errmsg);
381 }
382 }
383
InsertFileDescriptorMap(int32_t fileDescriptor,const std::string & taskName,EventQueue::Priority priority,const std::shared_ptr<FileDescriptorListener> & listener)384 void DeamonIoWaiter::InsertFileDescriptorMap(int32_t fileDescriptor, const std::string& taskName,
385 EventQueue::Priority priority, const std::shared_ptr<FileDescriptorListener>& listener)
386 {
387 std::lock_guard<std::mutex> lock(fileDescriptorMapLock);
388 std::shared_ptr<FileDescriptorInfo> fileDescriptorInfo =
389 std::make_shared<FileDescriptorInfo>(taskName, priority, listener);
390 fileDescriptorMap_.emplace(fileDescriptor, fileDescriptorInfo);
391 }
392
EraseFileDescriptorMap(int32_t fileDescriptor)393 void DeamonIoWaiter::EraseFileDescriptorMap(int32_t fileDescriptor)
394 {
395 std::lock_guard<std::mutex> lock(fileDescriptorMapLock);
396 fileDescriptorMap_.erase(fileDescriptor);
397 }
398
GetFileDescriptorMap(int32_t fileDescriptor)399 std::shared_ptr<FileDescriptorInfo> DeamonIoWaiter::GetFileDescriptorMap(int32_t fileDescriptor)
400 {
401 std::lock_guard<std::mutex> lock(fileDescriptorMapLock);
402 auto it = fileDescriptorMap_.find(fileDescriptor);
403 if (it == fileDescriptorMap_.end()) {
404 HILOGW("DeamonIoWaiter get file descriptor failed %{public}d", fileDescriptor);
405 return nullptr;
406 }
407 return it->second;
408 }
409 } // namespace AppExecFwk
410 } // namespace OHOS
411