| Kevin DuBois | cc27b50 | 2019-11-13 09:40:07 -0800 | [diff] [blame] | 1 | /* | 
|  | 2 | * Copyright 2019 The Android Open Source Project | 
|  | 3 | * | 
|  | 4 | * Licensed under the Apache License, Version 2.0 (the "License"); | 
|  | 5 | * you may not use this file except in compliance with the License. | 
|  | 6 | * You may obtain a copy of the License at | 
|  | 7 | * | 
|  | 8 | *      http://www.apache.org/licenses/LICENSE-2.0 | 
|  | 9 | * | 
|  | 10 | * Unless required by applicable law or agreed to in writing, software | 
|  | 11 | * distributed under the License is distributed on an "AS IS" BASIS, | 
|  | 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | 
|  | 13 | * See the License for the specific language governing permissions and | 
|  | 14 | * limitations under the License. | 
|  | 15 | */ | 
|  | 16 |  | 
|  | 17 | #undef LOG_TAG | 
|  | 18 | #define LOG_TAG "SchedulerTimer" | 
|  | 19 | #define ATRACE_TAG ATRACE_TAG_GRAPHICS | 
|  | 20 | #include <log/log.h> | 
|  | 21 | #include <sys/epoll.h> | 
|  | 22 | #include <sys/timerfd.h> | 
|  | 23 | #include <sys/unistd.h> | 
|  | 24 | #include <utils/Trace.h> | 
|  | 25 | #include <chrono> | 
|  | 26 | #include <cstdint> | 
|  | 27 |  | 
|  | 28 | #include "Timer.h" | 
|  | 29 |  | 
|  | 30 | namespace android::scheduler { | 
|  | 31 |  | 
|  | 32 | static constexpr size_t kReadPipe = 0; | 
|  | 33 | static constexpr size_t kWritePipe = 1; | 
|  | 34 |  | 
|  | 35 | template <class T, size_t N> | 
|  | 36 | constexpr size_t arrayLen(T (&)[N]) { | 
|  | 37 | return N; | 
|  | 38 | } | 
|  | 39 |  | 
|  | 40 | Timer::Timer() | 
|  | 41 | : mTimerFd(timerfd_create(CLOCK_MONOTONIC, TFD_CLOEXEC | TFD_NONBLOCK)), | 
|  | 42 | mEpollFd(epoll_create1(EPOLL_CLOEXEC)) { | 
|  | 43 | if (pipe2(mPipes.data(), O_CLOEXEC | O_NONBLOCK)) { | 
|  | 44 | ALOGE("could not create TimerDispatch mPipes"); | 
|  | 45 | }; | 
|  | 46 |  | 
|  | 47 | mDispatchThread = std::thread(std::bind(&Timer::dispatch, this)); | 
|  | 48 | } | 
|  | 49 |  | 
|  | 50 | Timer::~Timer() { | 
|  | 51 | endDispatch(); | 
|  | 52 | mDispatchThread.join(); | 
|  | 53 |  | 
|  | 54 | close(mPipes[kWritePipe]); | 
|  | 55 | close(mPipes[kReadPipe]); | 
|  | 56 | close(mEpollFd); | 
|  | 57 | close(mTimerFd); | 
|  | 58 | } | 
|  | 59 |  | 
|  | 60 | void Timer::endDispatch() { | 
|  | 61 | static constexpr unsigned char end = 'e'; | 
|  | 62 | write(mPipes[kWritePipe], &end, sizeof(end)); | 
|  | 63 | } | 
|  | 64 |  | 
|  | 65 | nsecs_t Timer::now() const { | 
|  | 66 | return systemTime(SYSTEM_TIME_MONOTONIC); | 
|  | 67 | } | 
|  | 68 |  | 
|  | 69 | constexpr char const* timerTraceTag = "AlarmInNs"; | 
|  | 70 | void Timer::alarmIn(std::function<void()> const& cb, nsecs_t fireIn) { | 
|  | 71 | std::lock_guard<decltype(mMutex)> lk(mMutex); | 
|  | 72 | ATRACE_INT64(timerTraceTag, fireIn); | 
|  | 73 |  | 
|  | 74 | using namespace std::literals; | 
|  | 75 | static constexpr int ns_per_s = | 
|  | 76 | std::chrono::duration_cast<std::chrono::nanoseconds>(1s).count(); | 
|  | 77 |  | 
|  | 78 | mCallback = cb; | 
|  | 79 |  | 
|  | 80 | struct itimerspec old_timer; | 
|  | 81 | struct itimerspec new_timer { | 
|  | 82 | .it_interval = {.tv_sec = 0, .tv_nsec = 0}, | 
|  | 83 | .it_value = {.tv_sec = static_cast<long>(fireIn / ns_per_s), | 
|  | 84 | .tv_nsec = static_cast<long>(fireIn % ns_per_s)}, | 
|  | 85 | }; | 
|  | 86 |  | 
|  | 87 | if (timerfd_settime(mTimerFd, 0, &new_timer, &old_timer)) { | 
| Kevin DuBois | 127a2d9 | 2019-12-04 13:52:52 -0800 | [diff] [blame] | 88 | ALOGW("Failed to set timerfd %s (%i)", strerror(errno), errno); | 
| Kevin DuBois | cc27b50 | 2019-11-13 09:40:07 -0800 | [diff] [blame] | 89 | } | 
|  | 90 | } | 
|  | 91 |  | 
|  | 92 | void Timer::alarmCancel() { | 
|  | 93 | std::lock_guard<decltype(mMutex)> lk(mMutex); | 
|  | 94 | ATRACE_INT64(timerTraceTag, 0); | 
|  | 95 |  | 
|  | 96 | struct itimerspec old_timer; | 
|  | 97 | struct itimerspec new_timer { | 
|  | 98 | .it_interval = {.tv_sec = 0, .tv_nsec = 0}, | 
|  | 99 | .it_value = { | 
|  | 100 | .tv_sec = 0, | 
|  | 101 | .tv_nsec = 0, | 
|  | 102 | }, | 
|  | 103 | }; | 
|  | 104 |  | 
|  | 105 | if (timerfd_settime(mTimerFd, 0, &new_timer, &old_timer)) { | 
|  | 106 | ALOGW("Failed to disarm timerfd"); | 
|  | 107 | } | 
|  | 108 | } | 
|  | 109 |  | 
|  | 110 | void Timer::dispatch() { | 
|  | 111 | struct sched_param param = {0}; | 
|  | 112 | param.sched_priority = 2; | 
|  | 113 | if (pthread_setschedparam(pthread_self(), SCHED_FIFO, ¶m) != 0) { | 
|  | 114 | ALOGW("Failed to set SCHED_FIFO on dispatch thread"); | 
|  | 115 | } | 
|  | 116 |  | 
|  | 117 | if (pthread_setname_np(pthread_self(), "TimerDispatch")) { | 
|  | 118 | ALOGW("Failed to set thread name on dispatch thread"); | 
|  | 119 | } | 
|  | 120 |  | 
|  | 121 | enum DispatchType : uint32_t { TIMER, TERMINATE, MAX_DISPATCH_TYPE }; | 
|  | 122 | epoll_event timerEvent; | 
|  | 123 | timerEvent.events = EPOLLIN; | 
|  | 124 | timerEvent.data.u32 = DispatchType::TIMER; | 
|  | 125 | if (epoll_ctl(mEpollFd, EPOLL_CTL_ADD, mTimerFd, &timerEvent) == -1) { | 
|  | 126 | ALOGE("Error adding timer fd to epoll dispatch loop"); | 
|  | 127 | return; | 
|  | 128 | } | 
|  | 129 |  | 
|  | 130 | epoll_event terminateEvent; | 
|  | 131 | terminateEvent.events = EPOLLIN; | 
|  | 132 | terminateEvent.data.u32 = DispatchType::TERMINATE; | 
|  | 133 | if (epoll_ctl(mEpollFd, EPOLL_CTL_ADD, mPipes[kReadPipe], &terminateEvent) == -1) { | 
|  | 134 | ALOGE("Error adding control fd to dispatch loop"); | 
|  | 135 | return; | 
|  | 136 | } | 
|  | 137 |  | 
|  | 138 | uint64_t iteration = 0; | 
|  | 139 | char const traceNamePrefix[] = "TimerIteration #"; | 
|  | 140 | static constexpr size_t max64print = std::numeric_limits<decltype(iteration)>::digits10; | 
|  | 141 | static constexpr size_t maxlen = arrayLen(traceNamePrefix) + max64print; | 
|  | 142 | std::array<char, maxlen> str_buffer; | 
|  | 143 | auto timing = true; | 
|  | 144 | while (timing) { | 
|  | 145 | epoll_event events[DispatchType::MAX_DISPATCH_TYPE]; | 
|  | 146 | int nfds = epoll_wait(mEpollFd, events, DispatchType::MAX_DISPATCH_TYPE, -1); | 
|  | 147 |  | 
|  | 148 | if (ATRACE_ENABLED()) { | 
|  | 149 | snprintf(str_buffer.data(), str_buffer.size(), "%s%" PRIu64, traceNamePrefix, | 
|  | 150 | iteration++); | 
|  | 151 | ATRACE_NAME(str_buffer.data()); | 
|  | 152 | } | 
|  | 153 |  | 
|  | 154 | if (nfds == -1) { | 
|  | 155 | if (errno != EINTR) { | 
|  | 156 | timing = false; | 
|  | 157 | continue; | 
|  | 158 | } | 
|  | 159 | } | 
|  | 160 |  | 
|  | 161 | for (auto i = 0; i < nfds; i++) { | 
|  | 162 | if (events[i].data.u32 == DispatchType::TIMER) { | 
|  | 163 | static uint64_t mIgnored = 0; | 
|  | 164 | read(mTimerFd, &mIgnored, sizeof(mIgnored)); | 
|  | 165 | std::function<void()> cb; | 
|  | 166 | { | 
|  | 167 | std::lock_guard<decltype(mMutex)> lk(mMutex); | 
|  | 168 | cb = mCallback; | 
|  | 169 | } | 
|  | 170 | if (cb) { | 
|  | 171 | cb(); | 
|  | 172 | } | 
|  | 173 | } | 
|  | 174 | if (events[i].data.u32 == DispatchType::TERMINATE) { | 
|  | 175 | timing = false; | 
|  | 176 | } | 
|  | 177 | } | 
|  | 178 | } | 
|  | 179 | } | 
|  | 180 |  | 
|  | 181 | } // namespace android::scheduler |