• 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     }
105 
106     if (awakenFd >= 0) {
107         close(awakenFd);
108     }
109 
110     return false;
111 }
112 
WaitFor(std::unique_lock<std::mutex> & lock,int64_t nanoseconds)113 bool EpollIoWaiter::WaitFor(std::unique_lock<std::mutex> &lock, int64_t nanoseconds)
114 {
115     if (epollFd_ < 0) {
116         HILOGE("WaitFor: MUST initialized before waiting");
117         return false;
118     }
119 
120     // Increasment of waiting count MUST be done before unlock.
121     ++waitingCount_;
122     lock.unlock();
123 
124     // Block on epoll_wait outside of the lock.
125     struct epoll_event epollEvents[MAX_EPOLL_EVENTS_SIZE];
126     int32_t retVal = epoll_wait(epollFd_, epollEvents, MAX_EPOLL_EVENTS_SIZE, NanosecondsToTimeout(nanoseconds));
127     // Decrease waiting count after block at once.
128     --waitingCount_;
129 
130     bool result = true;
131     if (retVal < 0) {
132         if (errno != EINTR && errno != EINVAL) {
133             char errmsg[MAX_ERRORMSG_LEN] = {0};
134             GetLastErr(errmsg, MAX_ERRORMSG_LEN);
135             HILOGE("WaitFor: Failed to wait epoll, %{public}s", errmsg);
136             result = false;
137         }
138     } else {
139         for (int32_t i = 0; i < retVal; ++i) {
140             if (epollEvents[i].data.fd == awakenFd_) {
141                 // Drain awaken pipe, if woken up by it.
142                 DrainAwakenPipe();
143                 continue;
144             }
145 
146             // Transform epoll events into file descriptor listener events.
147             uint32_t events = 0;
148             if ((epollEvents[i].events & EPOLLIN) != 0) {
149                 events |= FILE_DESCRIPTOR_INPUT_EVENT;
150             }
151 
152             if ((epollEvents[i].events & EPOLLOUT) != 0) {
153                 events |= FILE_DESCRIPTOR_OUTPUT_EVENT;
154             }
155 
156             if ((epollEvents[i].events & (EPOLLHUP)) != 0) {
157                 events |= FILE_DESCRIPTOR_SHUTDOWN_EVENT;
158             }
159 
160             if ((epollEvents[i].events & (EPOLLERR)) != 0) {
161                 events |= FILE_DESCRIPTOR_EXCEPTION_EVENT;
162             }
163 
164             if (callback_) {
165                 callback_(epollEvents[i].data.fd, events);
166             }
167         }
168     }
169 
170     lock.lock();
171     return result;
172 }
173 
NotifyOne()174 void EpollIoWaiter::NotifyOne()
175 {
176     // Epoll only support wake up all waiting thread.
177     NotifyAll();
178 }
179 
NotifyAll()180 void EpollIoWaiter::NotifyAll()
181 {
182     if (awakenFd_ < 0) {
183         HILOGE("NotifyAll: MUST initialized before notifying");
184         return;
185     }
186 
187     // Not waiting, so nothing to do.
188     if (waitingCount_.load() == 0) {
189         return;
190     }
191 
192     static const uint64_t increment = 1;
193     ssize_t retVal = write(awakenFd_, &increment, sizeof(increment));
194     if (retVal < 0) {
195         char errmsg[MAX_ERRORMSG_LEN] = {0};
196         GetLastErr(errmsg, MAX_ERRORMSG_LEN);
197         HILOGE("NotifyAll: Failed to write data into awaken pipe, %{public}s", errmsg);
198     }
199 }
200 
SupportListeningFileDescriptor() const201 bool EpollIoWaiter::SupportListeningFileDescriptor() const
202 {
203     return true;
204 }
205 
AddFileDescriptor(int32_t fileDescriptor,uint32_t events)206 bool EpollIoWaiter::AddFileDescriptor(int32_t fileDescriptor, uint32_t events)
207 {
208     if ((fileDescriptor < 0) || ((events & FILE_DESCRIPTOR_EVENTS_MASK) == 0)) {
209         HILOGE("AddFileDescriptor(%{public}d, %{public}u): Invalid parameter", fileDescriptor, events);
210         return false;
211     }
212 
213     if (epollFd_ < 0) {
214         HILOGE("AddFileDescriptor: MUST initialized before adding fds");
215         return false;
216     }
217 
218     // Transform file descriptor listener events into epoll events.
219     uint32_t epollEvents = 0;
220     if ((events & FILE_DESCRIPTOR_INPUT_EVENT) != 0) {
221         epollEvents |= EPOLLIN;
222     }
223 
224     if ((events & FILE_DESCRIPTOR_OUTPUT_EVENT) != 0) {
225         epollEvents |= EPOLLOUT;
226     }
227 
228     if (EpollCtrl(epollFd_, EPOLL_CTL_ADD, fileDescriptor, epollEvents) < 0) {
229         char errmsg[MAX_ERRORMSG_LEN] = {0};
230         GetLastErr(errmsg, MAX_ERRORMSG_LEN);
231         HILOGE("AddFileDescriptor: Failed to add file descriptor into epoll, %{public}s", errmsg);
232         return false;
233     }
234 
235     return true;
236 }
237 
RemoveFileDescriptor(int32_t fileDescriptor)238 void EpollIoWaiter::RemoveFileDescriptor(int32_t fileDescriptor)
239 {
240     if (fileDescriptor < 0) {
241         HILOGE("RemoveFileDescriptor: Invalid param while removing fd, fd = %{public}d", fileDescriptor);
242         return;
243     }
244 
245     if (epollFd_ < 0) {
246         HILOGE("RemoveFileDescriptor: MUST initialized before removing fds");
247         return;
248     }
249 
250     if (EpollCtrl(epollFd_, EPOLL_CTL_DEL, fileDescriptor, 0) < 0) {
251         char errmsg[MAX_ERRORMSG_LEN] = {0};
252         GetLastErr(errmsg, MAX_ERRORMSG_LEN);
253         HILOGE("RemoveFileDescriptor: Failed to remove file descriptor from epoll, %{public}s", errmsg);
254         return;
255     }
256 }
257 
DrainAwakenPipe() const258 void EpollIoWaiter::DrainAwakenPipe() const
259 {
260     uint64_t value = 0;
261     ssize_t retVal = read(awakenFd_, &value, sizeof(value));
262     if (retVal < 0) {
263         char errmsg[MAX_ERRORMSG_LEN] = {0};
264         GetLastErr(errmsg, MAX_ERRORMSG_LEN);
265         HILOGE("DrainAwakenPipe: Failed to read data from awaken pipe, %{public}s", errmsg);
266     }
267 }
268 
SetFileDescriptorEventCallback(const IoWaiter::FileDescriptorEventCallback & callback)269 void EpollIoWaiter::SetFileDescriptorEventCallback(const IoWaiter::FileDescriptorEventCallback &callback)
270 {
271     callback_ = callback;
272 }
273 }  // namespace AppExecFwk
274 }  // namespace OHOS
275