/* * Copyright 2023 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 "TimerProvider.h" #include #include #include #include namespace android { namespace { nsecs_t stimeToNsecs(stime_t time) { return std::chrono::duration_cast( std::chrono::duration(time)) .count(); } stime_t nsecsToStime(nsecs_t time) { return std::chrono::duration_cast>( std::chrono::nanoseconds(time)) .count(); } GesturesTimer* createTimer(void* data) { return static_cast(data)->createTimer(); } void setDeadline(void* data, GesturesTimer* timer, stime_t delay, GesturesTimerCallback callback, void* callbackData) { static_cast(data)->setDeadline(timer, stimeToNsecs(delay), callback, callbackData); }; void cancelTimer(void* data, GesturesTimer* timer) { static_cast(data)->cancelTimer(timer); } void freeTimer(void* data, GesturesTimer* timer) { static_cast(data)->freeTimer(timer); } } // namespace const GesturesTimerProvider kGestureTimerProvider = { .create_fn = createTimer, .set_fn = setDeadline, .cancel_fn = cancelTimer, .free_fn = freeTimer, }; TimerProvider::TimerProvider(InputReaderContext& context) : mReaderContext(context) {} std::string TimerProvider::dump() { std::string dump; auto timerPtrToString = [](const std::unique_ptr& timer) { return std::to_string(timer->id); }; dump += "Timer IDs: " + dumpVector>(mTimers, timerPtrToString) + "\n"; dump += "Deadlines and corresponding timer IDs:\n"; dump += addLinePrefix(dumpMap(mDeadlines, constToString, [](const Deadline& deadline) { return std::to_string(deadline.timerId); }), " ") + "\n"; return dump; } void TimerProvider::triggerCallbacks(nsecs_t when) { while (!mDeadlines.empty() && when >= mDeadlines.begin()->first) { const auto& deadlinePair = mDeadlines.begin(); deadlinePair->second.callback(when); mDeadlines.erase(deadlinePair); } requestTimeout(); } GesturesTimer* TimerProvider::createTimer() { mTimers.push_back(std::make_unique()); mTimers.back()->id = mNextTimerId; mNextTimerId++; return mTimers.back().get(); } void TimerProvider::setDeadline(GesturesTimer* timer, nsecs_t delay, GesturesTimerCallback callback, void* callbackData) { setDeadlineWithoutRequestingTimeout(timer, delay, callback, callbackData); requestTimeout(); } void TimerProvider::setDeadlineWithoutRequestingTimeout(GesturesTimer* timer, nsecs_t delay, GesturesTimerCallback callback, void* callbackData) { const nsecs_t now = getCurrentTime(); const nsecs_t time = now + delay; std::function wrappedCallback = [=, this](nsecs_t triggerTime) { stime_t nextDelay = callback(nsecsToStime(triggerTime), callbackData); if (nextDelay >= 0.0) { // When rescheduling a deadline, we know that we're running inside a call to // triggerCallbacks, at the end of which requestTimeout will be called. This means that // we don't want to call the public setDeadline, as that will request a timeout before // triggerCallbacks has removed this current deadline, resulting in a request for a // timeout that has already passed. setDeadlineWithoutRequestingTimeout(timer, stimeToNsecs(nextDelay), callback, callbackData); } }; mDeadlines.insert({time, Deadline(wrappedCallback, timer->id)}); } void TimerProvider::cancelTimer(GesturesTimer* timer) { int id = timer->id; std::erase_if(mDeadlines, [id](const auto& item) { return item.second.timerId == id; }); requestTimeout(); } void TimerProvider::freeTimer(GesturesTimer* timer) { cancelTimer(timer); std::erase_if(mTimers, [timer](std::unique_ptr& t) { return t.get() == timer; }); } void TimerProvider::requestTimeout() { if (!mDeadlines.empty()) { // Because a std::multimap is sorted by key, we simply use the time for the first entry. mReaderContext.requestTimeoutAtTime(mDeadlines.begin()->first); } } nsecs_t TimerProvider::getCurrentTime() { return systemTime(SYSTEM_TIME_MONOTONIC); } } // namespace android