blob: 7c5058e43177ec2cbdafa14f27f7904fdd7e64d0 [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
Ady Abraham75398722020-04-07 14:08:45 -070020#include <android-base/stringprintf.h>
Kevin DuBoiscc27b502019-11-13 09:40:07 -080021#include <log/log.h>
22#include <sys/epoll.h>
23#include <sys/timerfd.h>
24#include <sys/unistd.h>
25#include <utils/Trace.h>
26#include <chrono>
27#include <cstdint>
28
Kevin DuBoisecb1f0d2019-12-12 10:47:41 -080029#include "SchedulerUtils.h"
Kevin DuBoiscc27b502019-11-13 09:40:07 -080030#include "Timer.h"
31
32namespace android::scheduler {
Ady Abraham75398722020-04-07 14:08:45 -070033using base::StringAppendF;
Kevin DuBoiscc27b502019-11-13 09:40:07 -080034
35static constexpr size_t kReadPipe = 0;
36static constexpr size_t kWritePipe = 1;
37
Ady Abraham75398722020-04-07 14:08:45 -070038Timer::Timer() {
39 reset();
40 mDispatchThread = std::thread([this]() { threadMain(); });
Kevin DuBoiscc27b502019-11-13 09:40:07 -080041}
42
43Timer::~Timer() {
44 endDispatch();
45 mDispatchThread.join();
Ady Abraham75398722020-04-07 14:08:45 -070046 cleanup();
47}
Kevin DuBoiscc27b502019-11-13 09:40:07 -080048
Ady Abraham75398722020-04-07 14:08:45 -070049void Timer::reset() {
50 cleanup();
51 mTimerFd = timerfd_create(CLOCK_MONOTONIC, TFD_CLOEXEC | TFD_NONBLOCK);
52 mEpollFd = epoll_create1(EPOLL_CLOEXEC);
53 if (pipe2(mPipes.data(), O_CLOEXEC | O_NONBLOCK)) {
54 ALOGE("could not create TimerDispatch mPipes");
55 return;
56 };
57 setDebugState(DebugState::Reset);
58}
59
60void Timer::cleanup() {
61 if (mTimerFd != -1) {
62 close(mTimerFd);
63 mTimerFd = -1;
64 }
65
66 if (mEpollFd != -1) {
67 close(mEpollFd);
68 mEpollFd = -1;
69 }
70
71 if (mPipes[kReadPipe] != -1) {
72 close(mPipes[kReadPipe]);
73 mPipes[kReadPipe] = -1;
74 }
75
76 if (mPipes[kWritePipe] != -1) {
77 close(mPipes[kWritePipe]);
78 mPipes[kWritePipe] = -1;
79 }
Kevin DuBoiscc27b502019-11-13 09:40:07 -080080}
81
82void Timer::endDispatch() {
83 static constexpr unsigned char end = 'e';
84 write(mPipes[kWritePipe], &end, sizeof(end));
85}
86
87nsecs_t Timer::now() const {
88 return systemTime(SYSTEM_TIME_MONOTONIC);
89}
90
Kevin DuBoiscc27b502019-11-13 09:40:07 -080091void Timer::alarmIn(std::function<void()> const& cb, nsecs_t fireIn) {
92 std::lock_guard<decltype(mMutex)> lk(mMutex);
Kevin DuBoiscc27b502019-11-13 09:40:07 -080093 using namespace std::literals;
94 static constexpr int ns_per_s =
95 std::chrono::duration_cast<std::chrono::nanoseconds>(1s).count();
96
97 mCallback = cb;
98
99 struct itimerspec old_timer;
100 struct itimerspec new_timer {
101 .it_interval = {.tv_sec = 0, .tv_nsec = 0},
102 .it_value = {.tv_sec = static_cast<long>(fireIn / ns_per_s),
103 .tv_nsec = static_cast<long>(fireIn % ns_per_s)},
104 };
105
106 if (timerfd_settime(mTimerFd, 0, &new_timer, &old_timer)) {
Kevin DuBois127a2d92019-12-04 13:52:52 -0800107 ALOGW("Failed to set timerfd %s (%i)", strerror(errno), errno);
Kevin DuBoiscc27b502019-11-13 09:40:07 -0800108 }
109}
110
111void Timer::alarmCancel() {
112 std::lock_guard<decltype(mMutex)> lk(mMutex);
Kevin DuBoiscc27b502019-11-13 09:40:07 -0800113
114 struct itimerspec old_timer;
115 struct itimerspec new_timer {
116 .it_interval = {.tv_sec = 0, .tv_nsec = 0},
117 .it_value = {
118 .tv_sec = 0,
119 .tv_nsec = 0,
120 },
121 };
122
123 if (timerfd_settime(mTimerFd, 0, &new_timer, &old_timer)) {
124 ALOGW("Failed to disarm timerfd");
125 }
126}
127
Ady Abraham75398722020-04-07 14:08:45 -0700128void Timer::threadMain() {
129 while (dispatch()) {
130 reset();
131 }
132}
133
134bool Timer::dispatch() {
135 setDebugState(DebugState::Running);
Kevin DuBoiscc27b502019-11-13 09:40:07 -0800136 struct sched_param param = {0};
137 param.sched_priority = 2;
138 if (pthread_setschedparam(pthread_self(), SCHED_FIFO, &param) != 0) {
139 ALOGW("Failed to set SCHED_FIFO on dispatch thread");
140 }
141
142 if (pthread_setname_np(pthread_self(), "TimerDispatch")) {
143 ALOGW("Failed to set thread name on dispatch thread");
144 }
145
146 enum DispatchType : uint32_t { TIMER, TERMINATE, MAX_DISPATCH_TYPE };
147 epoll_event timerEvent;
148 timerEvent.events = EPOLLIN;
149 timerEvent.data.u32 = DispatchType::TIMER;
150 if (epoll_ctl(mEpollFd, EPOLL_CTL_ADD, mTimerFd, &timerEvent) == -1) {
151 ALOGE("Error adding timer fd to epoll dispatch loop");
Ady Abraham75398722020-04-07 14:08:45 -0700152 return true;
Kevin DuBoiscc27b502019-11-13 09:40:07 -0800153 }
154
155 epoll_event terminateEvent;
156 terminateEvent.events = EPOLLIN;
157 terminateEvent.data.u32 = DispatchType::TERMINATE;
158 if (epoll_ctl(mEpollFd, EPOLL_CTL_ADD, mPipes[kReadPipe], &terminateEvent) == -1) {
159 ALOGE("Error adding control fd to dispatch loop");
Ady Abraham75398722020-04-07 14:08:45 -0700160 return true;
Kevin DuBoiscc27b502019-11-13 09:40:07 -0800161 }
162
163 uint64_t iteration = 0;
164 char const traceNamePrefix[] = "TimerIteration #";
Kevin DuBoiscc27b502019-11-13 09:40:07 -0800165 static constexpr size_t maxlen = arrayLen(traceNamePrefix) + max64print;
166 std::array<char, maxlen> str_buffer;
Ady Abraham75398722020-04-07 14:08:45 -0700167
168 while (true) {
169 setDebugState(DebugState::Waiting);
Kevin DuBoiscc27b502019-11-13 09:40:07 -0800170 epoll_event events[DispatchType::MAX_DISPATCH_TYPE];
171 int nfds = epoll_wait(mEpollFd, events, DispatchType::MAX_DISPATCH_TYPE, -1);
172
Ady Abraham75398722020-04-07 14:08:45 -0700173 setDebugState(DebugState::Running);
Kevin DuBoiscc27b502019-11-13 09:40:07 -0800174 if (ATRACE_ENABLED()) {
175 snprintf(str_buffer.data(), str_buffer.size(), "%s%" PRIu64, traceNamePrefix,
176 iteration++);
177 ATRACE_NAME(str_buffer.data());
178 }
179
180 if (nfds == -1) {
181 if (errno != EINTR) {
Ady Abraham75398722020-04-07 14:08:45 -0700182 ALOGE("Error waiting on epoll: %s", strerror(errno));
183 return true;
Kevin DuBoiscc27b502019-11-13 09:40:07 -0800184 }
185 }
186
187 for (auto i = 0; i < nfds; i++) {
188 if (events[i].data.u32 == DispatchType::TIMER) {
189 static uint64_t mIgnored = 0;
Ady Abraham75398722020-04-07 14:08:45 -0700190 setDebugState(DebugState::Reading);
Kevin DuBoiscc27b502019-11-13 09:40:07 -0800191 read(mTimerFd, &mIgnored, sizeof(mIgnored));
Ady Abraham75398722020-04-07 14:08:45 -0700192 setDebugState(DebugState::Running);
Kevin DuBoiscc27b502019-11-13 09:40:07 -0800193 std::function<void()> cb;
194 {
195 std::lock_guard<decltype(mMutex)> lk(mMutex);
196 cb = mCallback;
197 }
198 if (cb) {
Ady Abraham75398722020-04-07 14:08:45 -0700199 setDebugState(DebugState::InCallback);
Kevin DuBoiscc27b502019-11-13 09:40:07 -0800200 cb();
Ady Abraham75398722020-04-07 14:08:45 -0700201 setDebugState(DebugState::Running);
Kevin DuBoiscc27b502019-11-13 09:40:07 -0800202 }
203 }
204 if (events[i].data.u32 == DispatchType::TERMINATE) {
Ady Abraham75398722020-04-07 14:08:45 -0700205 ALOGE("Terminated");
206 setDebugState(DebugState::Running);
207 return false;
Kevin DuBoiscc27b502019-11-13 09:40:07 -0800208 }
209 }
210 }
211}
212
Ady Abraham75398722020-04-07 14:08:45 -0700213void Timer::setDebugState(DebugState state) {
214 std::lock_guard lk(mMutex);
215 mDebugState = state;
216}
217
218const char* Timer::strDebugState(DebugState state) const {
219 switch (state) {
220 case DebugState::Reset:
221 return "Reset";
222 case DebugState::Running:
223 return "Running";
224 case DebugState::Waiting:
225 return "Waiting";
226 case DebugState::Reading:
227 return "Reading";
228 case DebugState::InCallback:
229 return "InCallback";
230 case DebugState::Terminated:
231 return "Terminated";
232 }
233}
234
235void Timer::dump(std::string& result) const {
236 std::lock_guard lk(mMutex);
237 StringAppendF(&result, "\t\tDebugState: %s\n", strDebugState(mDebugState));
238}
239
Kevin DuBoiscc27b502019-11-13 09:40:07 -0800240} // namespace android::scheduler