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