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