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 "event_queue.h"
17
18 #include <algorithm>
19 #include <iterator>
20 #include <mutex>
21
22 #include "deamon_io_waiter.h"
23 #include "epoll_io_waiter.h"
24 #include "event_handler.h"
25 #include "event_handler_utils.h"
26 #include "event_logger.h"
27 #include "none_io_waiter.h"
28
29
30 namespace OHOS {
31 namespace AppExecFwk {
32 namespace {
33
34 DEFINE_EH_HILOG_LABEL("EventQueue");
35
36 // Help to remove file descriptor listeners.
37 template<typename T>
RemoveFileDescriptorListenerLocked(std::map<int32_t,std::shared_ptr<FileDescriptorListener>> & listeners,const std::shared_ptr<IoWaiter> & ioWaiter,const T & filter,bool useDeamonIoWaiter_)38 void RemoveFileDescriptorListenerLocked(std::map<int32_t, std::shared_ptr<FileDescriptorListener>> &listeners,
39 const std::shared_ptr<IoWaiter>& ioWaiter, const T &filter, bool useDeamonIoWaiter_)
40 {
41 if (!useDeamonIoWaiter_ && !ioWaiter) {
42 return;
43 }
44
45 for (auto it = listeners.begin(); it != listeners.end();) {
46 if (filter(it->second)) {
47 if (useDeamonIoWaiter_) {
48 DeamonIoWaiter::GetInstance().RemoveFileDescriptor(it->first);
49 } else {
50 ioWaiter->RemoveFileDescriptor(it->first);
51 }
52 it = listeners.erase(it);
53 } else {
54 ++it;
55 }
56 }
57 }
58
59 } // unnamed namespace
60
EventQueue()61 EventQueue::EventQueue() : ioWaiter_(std::make_shared<NoneIoWaiter>())
62 {
63 HILOGD("enter");
64 }
65
EventQueue(const std::shared_ptr<IoWaiter> & ioWaiter)66 EventQueue::EventQueue(const std::shared_ptr<IoWaiter> &ioWaiter)
67 : ioWaiter_(ioWaiter ? ioWaiter : std::make_shared<NoneIoWaiter>())
68 {
69 HILOGD("enter");
70 if (ioWaiter_->SupportListeningFileDescriptor()) {
71 // Set callback to handle events from file descriptors.
72 ioWaiter_->SetFileDescriptorEventCallback(
73 std::bind(&EventQueue::HandleFileDescriptorEvent, this, std::placeholders::_1, std::placeholders::_2,
74 std::placeholders::_3, std::placeholders::_4));
75 }
76 }
77
~EventQueue()78 EventQueue::~EventQueue()
79 {
80 std::lock_guard<std::mutex> lock(queueLock_);
81 usable_.store(false);
82 ioWaiter_ = nullptr;
83 EH_LOGI_LIMIT("EventQueue is unavailable hence");
84 }
85
GetEvent()86 InnerEvent::Pointer EventQueue::GetEvent()
87 {
88 return InnerEvent::Pointer(nullptr, nullptr);
89 }
90
GetExpiredEvent(InnerEvent::TimePoint & nextExpiredTime)91 InnerEvent::Pointer EventQueue::GetExpiredEvent(InnerEvent::TimePoint &nextExpiredTime)
92 {
93 return InnerEvent::Pointer(nullptr, nullptr);
94 }
95
AddFileDescriptorByFd(int32_t fileDescriptor,uint32_t events,const std::string & taskName,const std::shared_ptr<FileDescriptorListener> & listener,EventQueue::Priority priority)96 bool EventQueue::AddFileDescriptorByFd(int32_t fileDescriptor, uint32_t events, const std::string &taskName,
97 const std::shared_ptr<FileDescriptorListener>& listener, EventQueue::Priority priority)
98 {
99 if (useDeamonIoWaiter_) {
100 return DeamonIoWaiter::GetInstance().AddFileDescriptor(fileDescriptor, events, taskName,
101 listener, priority);
102 }
103 if (ioWaiter_) {
104 return ioWaiter_->AddFileDescriptor(fileDescriptor, events, taskName, listener, priority);
105 }
106 return false;
107 }
108
AddFileDescriptorListener(int32_t fileDescriptor,uint32_t events,const std::shared_ptr<FileDescriptorListener> & listener,const std::string & taskName,Priority priority)109 ErrCode EventQueue::AddFileDescriptorListener(int32_t fileDescriptor, uint32_t events,
110 const std::shared_ptr<FileDescriptorListener> &listener, const std::string &taskName,
111 Priority priority)
112 {
113 if ((fileDescriptor < 0) || ((events & FILE_DESCRIPTOR_EVENTS_MASK) == 0) || (!listener)) {
114 HILOGE("%{public}d, %{public}u, %{public}s: Invalid parameter",
115 fileDescriptor, events, listener ? "valid" : "null");
116 return EVENT_HANDLER_ERR_INVALID_PARAM;
117 }
118
119 std::lock_guard<std::mutex> lock(queueLock_);
120 if (!usable_.load()) {
121 HILOGW("EventQueue is unavailable.");
122 return EVENT_HANDLER_ERR_NO_EVENT_RUNNER;
123 }
124 auto it = listeners_.find(fileDescriptor);
125 if (it != listeners_.end()) {
126 HILOGE("File descriptor %{public}d is already in listening", fileDescriptor);
127 return EVENT_HANDLER_ERR_FD_ALREADY;
128 }
129
130 HILOGD("Add file descriptor %{public}d to io waiter %{public}d", fileDescriptor, useDeamonIoWaiter_);
131 if (!EnsureIoWaiterSupportListerningFileDescriptorLocked()) {
132 return EVENT_HANDLER_ERR_FD_NOT_SUPPORT;
133 }
134
135 if (!AddFileDescriptorByFd(fileDescriptor, events, taskName, listener, priority)) {
136 HILOGE("Failed to add file descriptor into IO waiter");
137 return EVENT_HANDLER_ERR_FD_FAILED;
138 }
139
140 listeners_.emplace(fileDescriptor, listener);
141 return ERR_OK;
142 }
143
RemoveFileDescriptorListener(const std::shared_ptr<EventHandler> & owner)144 void EventQueue::RemoveFileDescriptorListener(const std::shared_ptr<EventHandler> &owner)
145 {
146 HILOGD("enter");
147 if (!owner) {
148 HILOGE("Invalid owner");
149 return;
150 }
151
152 auto listenerFilter = [&owner](const std::shared_ptr<FileDescriptorListener> &listener) {
153 if (!listener) {
154 return false;
155 }
156 return listener->GetOwner() == owner;
157 };
158
159 std::lock_guard<std::mutex> lock(queueLock_);
160 if (!usable_.load()) {
161 HILOGW("EventQueue is unavailable.");
162 return;
163 }
164 RemoveFileDescriptorListenerLocked(listeners_, ioWaiter_, listenerFilter, useDeamonIoWaiter_);
165 }
166
RemoveFileDescriptorListener(int32_t fileDescriptor)167 void EventQueue::RemoveFileDescriptorListener(int32_t fileDescriptor)
168 {
169 HILOGD("enter");
170 if (fileDescriptor < 0) {
171 HILOGE("%{public}d: Invalid file descriptor", fileDescriptor);
172 return;
173 }
174
175 std::lock_guard<std::mutex> lock(queueLock_);
176 if (!usable_.load()) {
177 HILOGW("EventQueue is unavailable.");
178 return;
179 }
180 if (listeners_.erase(fileDescriptor) > 0) {
181 if (useDeamonIoWaiter_) {
182 DeamonIoWaiter::GetInstance().RemoveFileDescriptor(fileDescriptor);
183 return;
184 }
185 if (ioWaiter_) {
186 ioWaiter_->RemoveFileDescriptor(fileDescriptor);
187 }
188 }
189 }
190
Prepare()191 void EventQueue::Prepare()
192 {
193 HILOGD("enter");
194 std::lock_guard<std::mutex> lock(queueLock_);
195 if (!usable_.load()) {
196 HILOGW("EventQueue is unavailable.");
197 return;
198 }
199 finished_ = false;
200 }
201
Finish()202 void EventQueue::Finish()
203 {
204 HILOGD("enter");
205 std::lock_guard<std::mutex> lock(queueLock_);
206 if (!usable_.load()) {
207 HILOGW("EventQueue is unavailable.");
208 return;
209 }
210 finished_ = true;
211 ioWaiter_->NotifyAll();
212 }
213
WaitUntilLocked(const InnerEvent::TimePoint & when,std::unique_lock<std::mutex> & lock)214 void EventQueue::WaitUntilLocked(const InnerEvent::TimePoint &when, std::unique_lock<std::mutex> &lock)
215 {
216 // Get a temp reference of IO waiter, otherwise it maybe released while waiting.
217 auto ioWaiterHolder = ioWaiter_;
218 if (!ioWaiterHolder->WaitFor(lock, TimePointToTimeOut(when))) {
219 HILOGE("Failed to call wait, reset IO waiter");
220 ioWaiter_ = std::make_shared<NoneIoWaiter>();
221 listeners_.clear();
222 }
223 }
224
HandleFileDescriptorEvent(int32_t fileDescriptor,uint32_t events,const std::string & taskName,Priority priority)225 void EventQueue::HandleFileDescriptorEvent(int32_t fileDescriptor, uint32_t events,
226 const std::string &taskName, Priority priority) __attribute__((no_sanitize("cfi")))
227 {
228 std::shared_ptr<FileDescriptorListener> listener;
229 {
230 std::lock_guard<std::mutex> lock(queueLock_);
231 if (!usable_.load()) {
232 HILOGW("EventQueue is unavailable.");
233 return;
234 }
235 auto it = listeners_.find(fileDescriptor);
236 if (it == listeners_.end()) {
237 HILOGW("Can not found listener, maybe it is removed");
238 return;
239 }
240 // Hold instance of listener.
241 listener = it->second;
242 if (!listener) {
243 return;
244 }
245 }
246
247 auto handler = listener->GetOwner();
248 if (!handler) {
249 HILOGW("Owner of listener is released");
250 return;
251 }
252
253 std::weak_ptr<FileDescriptorListener> wp = listener;
254 auto f = [fileDescriptor, events, wp]() {
255 auto listener = wp.lock();
256 if (!listener) {
257 HILOGW("Listener is released");
258 return;
259 }
260
261 if ((events & FILE_DESCRIPTOR_INPUT_EVENT) != 0) {
262 listener->OnReadable(fileDescriptor);
263 }
264
265 if ((events & FILE_DESCRIPTOR_OUTPUT_EVENT) != 0) {
266 listener->OnWritable(fileDescriptor);
267 }
268
269 if ((events & FILE_DESCRIPTOR_SHUTDOWN_EVENT) != 0) {
270 listener->OnShutdown(fileDescriptor);
271 }
272
273 if ((events & FILE_DESCRIPTOR_EXCEPTION_EVENT) != 0) {
274 listener->OnException(fileDescriptor);
275 }
276 };
277
278 HILOGD("Post fd %{public}d, task %{public}s, priority %{public}d.", fileDescriptor,
279 taskName.c_str(), priority);
280 // Post a high priority task to handle file descriptor events.
281 handler->PostTask(f, taskName, 0, priority);
282 }
283
CheckFileDescriptorEvent()284 void EventQueue::CheckFileDescriptorEvent()
285 {
286 InnerEvent::TimePoint now = InnerEvent::Clock::now();
287 std::unique_lock<std::mutex> lock(queueLock_);
288 WaitUntilLocked(now, lock);
289 }
290
EnsureIoWaiterSupportListerningFileDescriptorLocked()291 bool EventQueue::EnsureIoWaiterSupportListerningFileDescriptorLocked()
292 {
293 HILOGD("enter");
294 if (useDeamonIoWaiter_) {
295 if (!DeamonIoWaiter::GetInstance().Init()) {
296 HILOGE("Failed to initialize deamon waiter");
297 return false;
298 }
299 DeamonIoWaiter::GetInstance().StartEpollIoWaiter();
300 return true;
301 }
302
303 if (ioWaiter_->SupportListeningFileDescriptor()) {
304 return true;
305 }
306
307 auto newIoWaiter = std::make_shared<EpollIoWaiter>();
308 if (!newIoWaiter->Init()) {
309 HILOGE("Failed to initialize epoll");
310 return false;
311 }
312
313 // Set callback to handle events from file descriptors.
314 newIoWaiter->SetFileDescriptorEventCallback(
315 std::bind(&EventQueue::HandleFileDescriptorEvent, this, std::placeholders::_1, std::placeholders::_2,
316 std::placeholders::_3, std::placeholders::_4));
317
318 ioWaiter_->NotifyAll();
319 ioWaiter_ = newIoWaiter;
320 return true;
321 }
322
RemoveInvalidFileDescriptor()323 void EventQueue::RemoveInvalidFileDescriptor()
324 {
325 // Remove all listeners which lost its owner.
326 auto listenerFilter = [](const std::shared_ptr<FileDescriptorListener> &listener) {
327 if (!listener) {
328 return true;
329 }
330 HILOGD("Start get to GetOwner");
331 return !listener->GetOwner();
332 };
333
334 RemoveFileDescriptorListenerLocked(listeners_, ioWaiter_, listenerFilter, useDeamonIoWaiter_);
335 }
336
337 } // namespace AppExecFwk
338 } // namespace OHOS
339