• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (c) 2021-2022 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 <sys/epoll.h>
21 #include <sys/eventfd.h>
22 #include <unistd.h>
23 
24 #include "event_handler_utils.h"
25 #include "file_descriptor_listener.h"
26 
27 DEFINE_HILOG_LABEL("EpollIoWaiter");
28 
29 namespace OHOS {
30 namespace AppExecFwk {
31 namespace {
32 const size_t MAX_EPOLL_EVENTS_SIZE = 8;
33 
EpollCtrl(int32_t epollFd,int32_t operation,int32_t fileDescriptor,uint32_t epollEvents)34 inline int32_t EpollCtrl(int32_t epollFd, int32_t operation, int32_t fileDescriptor, uint32_t epollEvents)
35 {
36     struct epoll_event epollEvent = {
37         .events = epollEvents,
38         .data = {.fd = fileDescriptor},
39     };
40 
41     return epoll_ctl(epollFd, operation, fileDescriptor, &epollEvent);
42 }
43 }  // unnamed namespace
44 
~EpollIoWaiter()45 EpollIoWaiter::~EpollIoWaiter()
46 {
47     // Close all valid file descriptors.
48     if (epollFd_ >= 0) {
49         close(epollFd_);
50         epollFd_ = -1;
51     }
52 
53     if (awakenFd_ >= 0) {
54         close(awakenFd_);
55         awakenFd_ = -1;
56     }
57 }
58 
Init()59 bool EpollIoWaiter::Init()
60 {
61     if (epollFd_ >= 0) {
62         HILOGE("Init: Already initialized");
63         return true;
64     }
65 
66     int32_t epollFd = -1;
67     int32_t awakenFd = -1;
68 
69     do {
70         epollFd = epoll_create(MAX_EPOLL_EVENTS_SIZE);
71         if (epollFd < 0) {
72             char errmsg[MAX_ERRORMSG_LEN] = {0};
73             GetLastErr(errmsg, MAX_ERRORMSG_LEN);
74             HILOGE("Init: Failed to create epoll, %{public}s", errmsg);
75             break;
76         }
77 
78         awakenFd = eventfd(0, EFD_CLOEXEC | EFD_NONBLOCK);
79         if (awakenFd < 0) {
80             char errmsg[MAX_ERRORMSG_LEN] = {0};
81             GetLastErr(errmsg, MAX_ERRORMSG_LEN);
82             HILOGE("Init: Failed to create event fd, %{public}s", errmsg);
83             break;
84         }
85 
86         // Add readable file descriptor of pipe, used to wake up blocked thread.
87         if (EpollCtrl(epollFd, EPOLL_CTL_ADD, awakenFd, EPOLLIN | EPOLLET) < 0) {
88             char errmsg[MAX_ERRORMSG_LEN] = {0};
89             GetLastErr(errmsg, MAX_ERRORMSG_LEN);
90             HILOGE("Init: Failed to add awaken file descriptor into epoll, %{public}s", errmsg);
91             break;
92         }
93 
94         // Prepare epoll successfully.
95         epollFd_ = epollFd;
96         awakenFd_ = awakenFd;
97 
98         return true;
99     } while (0);
100 
101     // If any error happened, close all valid file descriptors.
102     if (epollFd >= 0) {
103         close(epollFd);
104         epollFd = -1;
105     }
106 
107     if (awakenFd >= 0) {
108         close(awakenFd);
109         awakenFd = -1;
110     }
111 
112     return false;
113 }
114 
WaitFor(std::unique_lock<std::mutex> & lock,int64_t nanoseconds)115 bool EpollIoWaiter::WaitFor(std::unique_lock<std::mutex> &lock, int64_t nanoseconds)
116 {
117     if (epollFd_ < 0) {
118         HILOGE("WaitFor: MUST initialized before waiting");
119         return false;
120     }
121 
122     // Increasment of waiting count MUST be done before unlock.
123     ++waitingCount_;
124     lock.unlock();
125 
126     // Block on epoll_wait outside of the lock.
127     struct epoll_event epollEvents[MAX_EPOLL_EVENTS_SIZE];
128     int32_t retVal = epoll_wait(epollFd_, epollEvents, MAX_EPOLL_EVENTS_SIZE, NanosecondsToTimeout(nanoseconds));
129     // Decrease waiting count after block at once.
130     --waitingCount_;
131 
132     bool result = true;
133     if (retVal < 0) {
134         if (errno != EINTR && errno != EINVAL) {
135             char errmsg[MAX_ERRORMSG_LEN] = {0};
136             GetLastErr(errmsg, MAX_ERRORMSG_LEN);
137             HILOGE("WaitFor: Failed to wait epoll, %{public}s", errmsg);
138             result = false;
139         }
140     } else {
141         for (int32_t i = 0; i < retVal; ++i) {
142             if (epollEvents[i].data.fd == awakenFd_) {
143                 // Drain awaken pipe, if woken up by it.
144                 DrainAwakenPipe();
145                 continue;
146             }
147 
148             // Transform epoll events into file descriptor listener events.
149             uint32_t events = 0;
150             if ((epollEvents[i].events & EPOLLIN) != 0) {
151                 events |= FILE_DESCRIPTOR_INPUT_EVENT;
152             }
153 
154             if ((epollEvents[i].events & EPOLLOUT) != 0) {
155                 events |= FILE_DESCRIPTOR_OUTPUT_EVENT;
156             }
157 
158             if ((epollEvents[i].events & (EPOLLHUP)) != 0) {
159                 events |= FILE_DESCRIPTOR_SHUTDOWN_EVENT;
160             }
161 
162             if ((epollEvents[i].events & (EPOLLERR)) != 0) {
163                 events |= FILE_DESCRIPTOR_EXCEPTION_EVENT;
164             }
165 
166             if (callback_) {
167                 callback_(epollEvents[i].data.fd, events);
168             }
169         }
170     }
171 
172     lock.lock();
173     return result;
174 }
175 
NotifyOne()176 void EpollIoWaiter::NotifyOne()
177 {
178     // Epoll only support wake up all waiting thread.
179     NotifyAll();
180 }
181 
NotifyAll()182 void EpollIoWaiter::NotifyAll()
183 {
184     if (awakenFd_ < 0) {
185         HILOGE("NotifyAll: MUST initialized before notifying");
186         return;
187     }
188 
189     // Not waiting, so nothing to do.
190     if (waitingCount_.load() == 0) {
191         return;
192     }
193 
194     static const uint64_t increment = 1;
195     ssize_t retVal = write(awakenFd_, &increment, sizeof(increment));
196     if (retVal < 0) {
197         char errmsg[MAX_ERRORMSG_LEN] = {0};
198         GetLastErr(errmsg, MAX_ERRORMSG_LEN);
199         HILOGE("NotifyAll: Failed to write data into awaken pipe, %{public}s", errmsg);
200     }
201 }
202 
SupportListeningFileDescriptor() const203 bool EpollIoWaiter::SupportListeningFileDescriptor() const
204 {
205     return true;
206 }
207 
AddFileDescriptor(int32_t fileDescriptor,uint32_t events)208 bool EpollIoWaiter::AddFileDescriptor(int32_t fileDescriptor, uint32_t events)
209 {
210     if ((fileDescriptor < 0) || ((events & FILE_DESCRIPTOR_EVENTS_MASK) == 0)) {
211         HILOGE("AddFileDescriptor(%{public}d, %{public}u): Invalid parameter", fileDescriptor, events);
212         return false;
213     }
214 
215     if (epollFd_ < 0) {
216         HILOGE("AddFileDescriptor: MUST initialized before adding fds");
217         return false;
218     }
219 
220     // Transform file descriptor listener events into epoll events.
221     uint32_t epollEvents = 0;
222     if ((events & FILE_DESCRIPTOR_INPUT_EVENT) != 0) {
223         epollEvents |= EPOLLIN;
224     }
225 
226     if ((events & FILE_DESCRIPTOR_OUTPUT_EVENT) != 0) {
227         epollEvents |= EPOLLOUT;
228     }
229 
230     if (EpollCtrl(epollFd_, EPOLL_CTL_ADD, fileDescriptor, epollEvents) < 0) {
231         char errmsg[MAX_ERRORMSG_LEN] = {0};
232         GetLastErr(errmsg, MAX_ERRORMSG_LEN);
233         HILOGE("AddFileDescriptor: Failed to add file descriptor into epoll, %{public}s", errmsg);
234         return false;
235     }
236 
237     return true;
238 }
239 
RemoveFileDescriptor(int32_t fileDescriptor)240 void EpollIoWaiter::RemoveFileDescriptor(int32_t fileDescriptor)
241 {
242     if (fileDescriptor < 0) {
243         HILOGE("RemoveFileDescriptor: Invalid param while removing fd, fd = %{public}d", fileDescriptor);
244         return;
245     }
246 
247     if (epollFd_ < 0) {
248         HILOGE("RemoveFileDescriptor: MUST initialized before removing fds");
249         return;
250     }
251 
252     if (EpollCtrl(epollFd_, EPOLL_CTL_DEL, fileDescriptor, 0) < 0) {
253         char errmsg[MAX_ERRORMSG_LEN] = {0};
254         GetLastErr(errmsg, MAX_ERRORMSG_LEN);
255         HILOGE("RemoveFileDescriptor: Failed to remove file descriptor from epoll, %{public}s", errmsg);
256         return;
257     }
258 }
259 
DrainAwakenPipe() const260 void EpollIoWaiter::DrainAwakenPipe() const
261 {
262     uint64_t value = 0;
263     ssize_t retVal = read(awakenFd_, &value, sizeof(value));
264     if (retVal < 0) {
265         char errmsg[MAX_ERRORMSG_LEN] = {0};
266         GetLastErr(errmsg, MAX_ERRORMSG_LEN);
267         HILOGE("DrainAwakenPipe: Failed to read data from awaken pipe, %{public}s", errmsg);
268     }
269 }
270 
SetFileDescriptorEventCallback(const IoWaiter::FileDescriptorEventCallback & callback)271 void EpollIoWaiter::SetFileDescriptorEventCallback(const IoWaiter::FileDescriptorEventCallback &callback)
272 {
273     callback_ = callback;
274 }
275 }  // namespace AppExecFwk
276 }  // namespace OHOS
277