| /* |
| * Copyright (C) 2024 The Android Open Source Project |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| #include "TimedEventLooper.h" |
| |
| #include <log/log.h> |
| #include <sched.h> |
| |
| namespace android::hwc_xrr { |
| namespace { |
| |
| using ::android::base::ScopedLockAssertion; |
| |
| } // namespace |
| |
| TimedEventLooper::TimedEventLooper() { |
| loop_thread_ = std::thread(&TimedEventLooper::LoopBody, this); |
| } |
| |
| TimedEventLooper::~TimedEventLooper() { |
| StopLoop(true); |
| loop_thread_.join(); |
| } |
| |
| void TimedEventLooper::DropEvent(const std::string& name) { |
| std::lock_guard<std::mutex> lock(mutex_); |
| DropEventLocked(name); |
| } |
| |
| void TimedEventLooper::DropEventLocked(const std::string& name) { |
| TimedEventQueue q; |
| while (!event_queue_.empty()) { |
| auto& it = event_queue_.top(); |
| if ((it.event_name_) != name) { |
| q.emplace(std::move(it)); |
| } |
| event_queue_.pop(); |
| } |
| event_queue_ = std::move(q); |
| condition_.notify_all(); |
| } |
| |
| void TimedEventLooper::DropEvent(int64_t flag) { |
| std::lock_guard<std::mutex> lock(mutex_); |
| DropEventLocked(flag); |
| } |
| |
| void TimedEventLooper::DropEventLocked(int64_t flag) { |
| TimedEventQueue q; |
| while (!event_queue_.empty()) { |
| auto& it = event_queue_.top(); |
| if (it.flag_ != flag) { |
| q.emplace(std::move(it)); |
| } |
| event_queue_.pop(); |
| } |
| event_queue_ = std::move(q); |
| condition_.notify_all(); |
| } |
| |
| void TimedEventLooper::DropEvents(int64_t mask) { |
| std::lock_guard<std::mutex> lock(mutex_); |
| DropEventsLocked(mask); |
| } |
| |
| void TimedEventLooper::DropEventsLocked(int64_t mask) { |
| TimedEventQueue q; |
| while (!event_queue_.empty()) { |
| auto& it = event_queue_.top(); |
| if ((it.flag_ & mask) != mask) { |
| q.emplace(std::move(it)); |
| } |
| event_queue_.pop(); |
| } |
| event_queue_ = std::move(q); |
| condition_.notify_all(); |
| } |
| |
| std::string TimedEventLooper::Dump() { |
| std::lock_guard<std::mutex> lock(mutex_); |
| |
| return DumpLocked(); |
| } |
| |
| void TimedEventLooper::PostEvent(TimedEvent& event) { |
| std::lock_guard<std::mutex> lock(mutex_); |
| PostEventLocked(event); |
| } |
| |
| void TimedEventLooper::PostEvent(TimedEvent&& event) { |
| std::lock_guard<std::mutex> lock(mutex_); |
| PostEventLocked(event); |
| } |
| |
| void TimedEventLooper::PostEventLocked(TimedEvent& event) { |
| event.timed_event_looper_ = this; |
| event_queue_.push(event); |
| condition_.notify_all(); |
| }; |
| |
| void TimedEventLooper::PostEventLocked(TimedEvent&& event) { |
| event.timed_event_looper_ = this; |
| event_queue_.emplace(std::move(event)); |
| condition_.notify_all(); |
| }; |
| |
| void TimedEventLooper::HandleEvent([[maybe_unused]] TimedEvent& event) { |
| } |
| |
| void TimedEventLooper::Reset() { |
| { |
| std::lock_guard<std::mutex> lock(mutex_); |
| event_queue_ = TimedEventQueue(); |
| } |
| condition_.notify_all(); |
| } |
| |
| void TimedEventLooper::StartLoop() { |
| { |
| const std::lock_guard<std::mutex> lock(mutex_); |
| enabled_ = true; |
| } |
| condition_.notify_all(); |
| } |
| |
| void TimedEventLooper::StopLoop(bool exit) { |
| { |
| const std::lock_guard<std::mutex> lock(mutex_); |
| is_thread_exit_ = exit; |
| enabled_ = false; |
| } |
| condition_.notify_all(); |
| } |
| |
| std::string TimedEventLooper::DumpLocked() { |
| TimedEventQueue q; |
| std::string res; |
| while (!event_queue_.empty()) { |
| auto& event = event_queue_.top(); |
| res += event.ToString(); |
| q.emplace(std::move(event)); |
| event_queue_.pop(); |
| } |
| event_queue_ = std::move(q); |
| return res; |
| } |
| |
| void TimedEventLooper::LoopBody() { |
| struct sched_param param = {.sched_priority = sched_get_priority_min(SCHED_FIFO)}; |
| if (sched_setscheduler(0, SCHED_FIFO, ¶m) != 0) { |
| ALOGE("TimedEventLooper %s: fail to set scheduler to SCHED_FIFO.", __func__); |
| } |
| for (;;) { |
| { |
| std::unique_lock<std::mutex> lock(mutex_); |
| if (is_thread_exit_) |
| break; |
| if (!enabled_) { |
| condition_.wait(lock); |
| } |
| if (!enabled_) |
| continue; |
| if (event_queue_.empty()) { |
| condition_.wait(lock); |
| } |
| if (event_queue_.empty()) { |
| continue; |
| } |
| int64_t whenNs = event_queue_.top().when_ns_; |
| int64_t nowNs = systemTime(SYSTEM_TIME_MONOTONIC); |
| if (whenNs > nowNs) { |
| int64_t delayNs = whenNs - nowNs; |
| auto res = condition_.wait_for(lock, std::chrono::nanoseconds(delayNs)); |
| if (res != std::cv_status::timeout || event_queue_.empty()) { |
| continue; |
| } |
| } |
| pending_event_ = event_queue_.top(); |
| event_queue_.pop(); |
| } |
| // Run on looper's thread without mutex. |
| if (pending_event_) { |
| HandleEvent(*pending_event_); |
| pending_event_ = std::nullopt; |
| } |
| } |
| } |
| |
| bool TimedEventLooper::RunOnLooperThread() const { |
| auto id = std::this_thread::get_id(); |
| return (id == loop_thread_.get_id()); |
| } |
| |
| } // namespace android::hwc_xrr |