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