blob: fb4f315aae783dad52b7b211782729c6ef6b7e15 [file] [log] [blame]
Kevin DuBoiscc27b502019-11-13 09:40:07 -08001/*
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
30namespace android::scheduler {
31
32static constexpr size_t kReadPipe = 0;
33static constexpr size_t kWritePipe = 1;
34
35template <class T, size_t N>
36constexpr size_t arrayLen(T (&)[N]) {
37 return N;
38}
39
40Timer::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
50Timer::~Timer() {
51 endDispatch();
52 mDispatchThread.join();
53
54 close(mPipes[kWritePipe]);
55 close(mPipes[kReadPipe]);
56 close(mEpollFd);
57 close(mTimerFd);
58}
59
60void Timer::endDispatch() {
61 static constexpr unsigned char end = 'e';
62 write(mPipes[kWritePipe], &end, sizeof(end));
63}
64
65nsecs_t Timer::now() const {
66 return systemTime(SYSTEM_TIME_MONOTONIC);
67}
68
69constexpr char const* timerTraceTag = "AlarmInNs";
70void 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 DuBois127a2d92019-12-04 13:52:52 -080088 ALOGW("Failed to set timerfd %s (%i)", strerror(errno), errno);
Kevin DuBoiscc27b502019-11-13 09:40:07 -080089 }
90}
91
92void 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
110void Timer::dispatch() {
111 struct sched_param param = {0};
112 param.sched_priority = 2;
113 if (pthread_setschedparam(pthread_self(), SCHED_FIFO, &param) != 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