• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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