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