|  | /* | 
|  | * Copyright 2019 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. | 
|  | */ | 
|  |  | 
|  | #undef LOG_TAG | 
|  | #define LOG_TAG "SchedulerTimer" | 
|  | #define ATRACE_TAG ATRACE_TAG_GRAPHICS | 
|  | #include <log/log.h> | 
|  | #include <sys/epoll.h> | 
|  | #include <sys/timerfd.h> | 
|  | #include <sys/unistd.h> | 
|  | #include <utils/Trace.h> | 
|  | #include <chrono> | 
|  | #include <cstdint> | 
|  |  | 
|  | #include "Timer.h" | 
|  |  | 
|  | namespace android::scheduler { | 
|  |  | 
|  | static constexpr size_t kReadPipe = 0; | 
|  | static constexpr size_t kWritePipe = 1; | 
|  |  | 
|  | template <class T, size_t N> | 
|  | constexpr size_t arrayLen(T (&)[N]) { | 
|  | return N; | 
|  | } | 
|  |  | 
|  | Timer::Timer() | 
|  | : mTimerFd(timerfd_create(CLOCK_MONOTONIC, TFD_CLOEXEC | TFD_NONBLOCK)), | 
|  | mEpollFd(epoll_create1(EPOLL_CLOEXEC)) { | 
|  | if (pipe2(mPipes.data(), O_CLOEXEC | O_NONBLOCK)) { | 
|  | ALOGE("could not create TimerDispatch mPipes"); | 
|  | }; | 
|  |  | 
|  | mDispatchThread = std::thread(std::bind(&Timer::dispatch, this)); | 
|  | } | 
|  |  | 
|  | Timer::~Timer() { | 
|  | endDispatch(); | 
|  | mDispatchThread.join(); | 
|  |  | 
|  | close(mPipes[kWritePipe]); | 
|  | close(mPipes[kReadPipe]); | 
|  | close(mEpollFd); | 
|  | close(mTimerFd); | 
|  | } | 
|  |  | 
|  | void Timer::endDispatch() { | 
|  | static constexpr unsigned char end = 'e'; | 
|  | write(mPipes[kWritePipe], &end, sizeof(end)); | 
|  | } | 
|  |  | 
|  | nsecs_t Timer::now() const { | 
|  | return systemTime(SYSTEM_TIME_MONOTONIC); | 
|  | } | 
|  |  | 
|  | constexpr char const* timerTraceTag = "AlarmInNs"; | 
|  | void Timer::alarmIn(std::function<void()> const& cb, nsecs_t fireIn) { | 
|  | std::lock_guard<decltype(mMutex)> lk(mMutex); | 
|  | ATRACE_INT64(timerTraceTag, fireIn); | 
|  |  | 
|  | using namespace std::literals; | 
|  | static constexpr int ns_per_s = | 
|  | std::chrono::duration_cast<std::chrono::nanoseconds>(1s).count(); | 
|  |  | 
|  | mCallback = cb; | 
|  |  | 
|  | struct itimerspec old_timer; | 
|  | struct itimerspec new_timer { | 
|  | .it_interval = {.tv_sec = 0, .tv_nsec = 0}, | 
|  | .it_value = {.tv_sec = static_cast<long>(fireIn / ns_per_s), | 
|  | .tv_nsec = static_cast<long>(fireIn % ns_per_s)}, | 
|  | }; | 
|  |  | 
|  | if (timerfd_settime(mTimerFd, 0, &new_timer, &old_timer)) { | 
|  | ALOGW("Failed to set timerfd %s (%i)", strerror(errno), errno); | 
|  | } | 
|  | } | 
|  |  | 
|  | void Timer::alarmCancel() { | 
|  | std::lock_guard<decltype(mMutex)> lk(mMutex); | 
|  | ATRACE_INT64(timerTraceTag, 0); | 
|  |  | 
|  | struct itimerspec old_timer; | 
|  | struct itimerspec new_timer { | 
|  | .it_interval = {.tv_sec = 0, .tv_nsec = 0}, | 
|  | .it_value = { | 
|  | .tv_sec = 0, | 
|  | .tv_nsec = 0, | 
|  | }, | 
|  | }; | 
|  |  | 
|  | if (timerfd_settime(mTimerFd, 0, &new_timer, &old_timer)) { | 
|  | ALOGW("Failed to disarm timerfd"); | 
|  | } | 
|  | } | 
|  |  | 
|  | void Timer::dispatch() { | 
|  | struct sched_param param = {0}; | 
|  | param.sched_priority = 2; | 
|  | if (pthread_setschedparam(pthread_self(), SCHED_FIFO, ¶m) != 0) { | 
|  | ALOGW("Failed to set SCHED_FIFO on dispatch thread"); | 
|  | } | 
|  |  | 
|  | if (pthread_setname_np(pthread_self(), "TimerDispatch")) { | 
|  | ALOGW("Failed to set thread name on dispatch thread"); | 
|  | } | 
|  |  | 
|  | enum DispatchType : uint32_t { TIMER, TERMINATE, MAX_DISPATCH_TYPE }; | 
|  | epoll_event timerEvent; | 
|  | timerEvent.events = EPOLLIN; | 
|  | timerEvent.data.u32 = DispatchType::TIMER; | 
|  | if (epoll_ctl(mEpollFd, EPOLL_CTL_ADD, mTimerFd, &timerEvent) == -1) { | 
|  | ALOGE("Error adding timer fd to epoll dispatch loop"); | 
|  | return; | 
|  | } | 
|  |  | 
|  | epoll_event terminateEvent; | 
|  | terminateEvent.events = EPOLLIN; | 
|  | terminateEvent.data.u32 = DispatchType::TERMINATE; | 
|  | if (epoll_ctl(mEpollFd, EPOLL_CTL_ADD, mPipes[kReadPipe], &terminateEvent) == -1) { | 
|  | ALOGE("Error adding control fd to dispatch loop"); | 
|  | return; | 
|  | } | 
|  |  | 
|  | uint64_t iteration = 0; | 
|  | char const traceNamePrefix[] = "TimerIteration #"; | 
|  | static constexpr size_t max64print = std::numeric_limits<decltype(iteration)>::digits10; | 
|  | static constexpr size_t maxlen = arrayLen(traceNamePrefix) + max64print; | 
|  | std::array<char, maxlen> str_buffer; | 
|  | auto timing = true; | 
|  | while (timing) { | 
|  | epoll_event events[DispatchType::MAX_DISPATCH_TYPE]; | 
|  | int nfds = epoll_wait(mEpollFd, events, DispatchType::MAX_DISPATCH_TYPE, -1); | 
|  |  | 
|  | if (ATRACE_ENABLED()) { | 
|  | snprintf(str_buffer.data(), str_buffer.size(), "%s%" PRIu64, traceNamePrefix, | 
|  | iteration++); | 
|  | ATRACE_NAME(str_buffer.data()); | 
|  | } | 
|  |  | 
|  | if (nfds == -1) { | 
|  | if (errno != EINTR) { | 
|  | timing = false; | 
|  | continue; | 
|  | } | 
|  | } | 
|  |  | 
|  | for (auto i = 0; i < nfds; i++) { | 
|  | if (events[i].data.u32 == DispatchType::TIMER) { | 
|  | static uint64_t mIgnored = 0; | 
|  | read(mTimerFd, &mIgnored, sizeof(mIgnored)); | 
|  | std::function<void()> cb; | 
|  | { | 
|  | std::lock_guard<decltype(mMutex)> lk(mMutex); | 
|  | cb = mCallback; | 
|  | } | 
|  | if (cb) { | 
|  | cb(); | 
|  | } | 
|  | } | 
|  | if (events[i].data.u32 == DispatchType::TERMINATE) { | 
|  | timing = false; | 
|  | } | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | } // namespace android::scheduler |