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 <android-base/stringprintf.h>
21 #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
29 #include "SchedulerUtils.h"
30 #include "Timer.h"
31
32 namespace android::scheduler {
33 using base::StringAppendF;
34
35 static constexpr size_t kReadPipe = 0;
36 static constexpr size_t kWritePipe = 1;
37
Timer()38 Timer::Timer() {
39 reset();
40 mDispatchThread = std::thread([this]() { threadMain(); });
41 }
42
~Timer()43 Timer::~Timer() {
44 endDispatch();
45 mDispatchThread.join();
46 cleanup();
47 }
48
reset()49 void 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
cleanup()60 void 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 }
80 }
81
endDispatch()82 void Timer::endDispatch() {
83 static constexpr unsigned char end = 'e';
84 write(mPipes[kWritePipe], &end, sizeof(end));
85 }
86
now() const87 nsecs_t Timer::now() const {
88 return systemTime(SYSTEM_TIME_MONOTONIC);
89 }
90
alarmIn(std::function<void ()> const & cb,nsecs_t fireIn)91 void Timer::alarmIn(std::function<void()> const& cb, nsecs_t fireIn) {
92 std::lock_guard<decltype(mMutex)> lk(mMutex);
93 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)) {
107 ALOGW("Failed to set timerfd %s (%i)", strerror(errno), errno);
108 }
109 }
110
alarmCancel()111 void Timer::alarmCancel() {
112 std::lock_guard<decltype(mMutex)> lk(mMutex);
113
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
threadMain()128 void Timer::threadMain() {
129 while (dispatch()) {
130 reset();
131 }
132 }
133
dispatch()134 bool Timer::dispatch() {
135 setDebugState(DebugState::Running);
136 struct sched_param param = {0};
137 param.sched_priority = 2;
138 if (pthread_setschedparam(pthread_self(), SCHED_FIFO, ¶m) != 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");
152 return true;
153 }
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");
160 return true;
161 }
162
163 uint64_t iteration = 0;
164 char const traceNamePrefix[] = "TimerIteration #";
165 static constexpr size_t maxlen = arrayLen(traceNamePrefix) + max64print;
166 std::array<char, maxlen> str_buffer;
167
168 while (true) {
169 setDebugState(DebugState::Waiting);
170 epoll_event events[DispatchType::MAX_DISPATCH_TYPE];
171 int nfds = epoll_wait(mEpollFd, events, DispatchType::MAX_DISPATCH_TYPE, -1);
172
173 setDebugState(DebugState::Running);
174 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) {
182 ALOGE("Error waiting on epoll: %s", strerror(errno));
183 return true;
184 }
185 }
186
187 for (auto i = 0; i < nfds; i++) {
188 if (events[i].data.u32 == DispatchType::TIMER) {
189 static uint64_t mIgnored = 0;
190 setDebugState(DebugState::Reading);
191 read(mTimerFd, &mIgnored, sizeof(mIgnored));
192 setDebugState(DebugState::Running);
193 std::function<void()> cb;
194 {
195 std::lock_guard<decltype(mMutex)> lk(mMutex);
196 cb = mCallback;
197 }
198 if (cb) {
199 setDebugState(DebugState::InCallback);
200 cb();
201 setDebugState(DebugState::Running);
202 }
203 }
204 if (events[i].data.u32 == DispatchType::TERMINATE) {
205 ALOGE("Terminated");
206 setDebugState(DebugState::Running);
207 return false;
208 }
209 }
210 }
211 }
212
setDebugState(DebugState state)213 void Timer::setDebugState(DebugState state) {
214 std::lock_guard lk(mMutex);
215 mDebugState = state;
216 }
217
strDebugState(DebugState state) const218 const 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
dump(std::string & result) const235 void Timer::dump(std::string& result) const {
236 std::lock_guard lk(mMutex);
237 StringAppendF(&result, "\t\tDebugState: %s\n", strDebugState(mDebugState));
238 }
239
240 } // namespace android::scheduler
241