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