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
20 #include <chrono>
21 #include <cstdint>
22
23 #include <sys/epoll.h>
24 #include <sys/timerfd.h>
25 #include <sys/unistd.h>
26
27 #include <common/FlagManager.h>
28 #include <common/trace.h>
29 #include <ftl/concat.h>
30 #include <ftl/enum.h>
31 #include <log/log.h>
32
33 #include <scheduler/Timer.h>
34
35 namespace android::scheduler {
36
37 constexpr size_t kReadPipe = 0;
38 constexpr size_t kWritePipe = 1;
39
40 Clock::~Clock() = default;
41 TimeKeeper::~TimeKeeper() = default;
42
Timer()43 Timer::Timer() {
44 reset();
45 mDispatchThread = std::thread([this]() { threadMain(); });
46 }
47
~Timer()48 Timer::~Timer() {
49 endDispatch();
50 mDispatchThread.join();
51 cleanup();
52 }
53
reset()54 void Timer::reset() {
55 std::function<void()> cb;
56 {
57 std::lock_guard lock(mMutex);
58 if (mExpectingCallback && mCallback) {
59 cb = mCallback;
60 }
61
62 cleanup();
63 mTimerFd = timerfd_create(CLOCK_MONOTONIC, TFD_CLOEXEC | TFD_NONBLOCK);
64 mEpollFd = epoll_create1(EPOLL_CLOEXEC);
65 if (pipe2(mPipes.data(), O_CLOEXEC | O_NONBLOCK)) {
66 ALOGE("could not create TimerDispatch mPipes");
67 }
68 }
69 if (cb) {
70 setDebugState(DebugState::InCallback);
71 cb();
72 setDebugState(DebugState::Running);
73 }
74 setDebugState(DebugState::Reset);
75 }
76
cleanup()77 void Timer::cleanup() {
78 if (mTimerFd != -1) {
79 close(mTimerFd);
80 mTimerFd = -1;
81 }
82
83 if (mEpollFd != -1) {
84 close(mEpollFd);
85 mEpollFd = -1;
86 }
87
88 if (mPipes[kReadPipe] != -1) {
89 close(mPipes[kReadPipe]);
90 mPipes[kReadPipe] = -1;
91 }
92
93 if (mPipes[kWritePipe] != -1) {
94 close(mPipes[kWritePipe]);
95 mPipes[kWritePipe] = -1;
96 }
97
98 setCallback({});
99 }
100
endDispatch()101 void Timer::endDispatch() {
102 static constexpr unsigned char end = 'e';
103 write(mPipes[kWritePipe], &end, sizeof(end));
104 }
105
now() const106 nsecs_t Timer::now() const {
107 return systemTime(SYSTEM_TIME_MONOTONIC);
108 }
109
alarmAt(std::function<void ()> callback,nsecs_t time)110 void Timer::alarmAt(std::function<void()> callback, nsecs_t time) {
111 std::lock_guard lock(mMutex);
112 using namespace std::literals;
113 static constexpr int ns_per_s =
114 std::chrono::duration_cast<std::chrono::nanoseconds>(1s).count();
115
116 setCallback(std::move(callback));
117
118 struct itimerspec old_timer;
119 struct itimerspec new_timer {
120 .it_interval = {.tv_sec = 0, .tv_nsec = 0},
121 .it_value = {.tv_sec = static_cast<long>(time / ns_per_s),
122 .tv_nsec = static_cast<long>(time % ns_per_s)},
123 };
124
125 if (timerfd_settime(mTimerFd, TFD_TIMER_ABSTIME, &new_timer, &old_timer)) {
126 ALOGW("Failed to set timerfd %s (%i)", strerror(errno), errno);
127 }
128 }
129
alarmCancel()130 void Timer::alarmCancel() {
131 std::lock_guard lock(mMutex);
132
133 struct itimerspec old_timer;
134 struct itimerspec new_timer {
135 .it_interval = {.tv_sec = 0, .tv_nsec = 0},
136 .it_value = {
137 .tv_sec = 0,
138 .tv_nsec = 0,
139 },
140 };
141
142 if (timerfd_settime(mTimerFd, 0, &new_timer, &old_timer)) {
143 ALOGW("Failed to disarm timerfd");
144 }
145
146 setCallback({});
147 }
148
threadMain()149 void Timer::threadMain() {
150 while (dispatch()) {
151 reset();
152 }
153 }
154
dispatch()155 bool Timer::dispatch() {
156 setDebugState(DebugState::Running);
157 struct sched_param param = {0};
158 param.sched_priority = 2;
159 if (!FlagManager::getInstance().disable_sched_fifo_sf_sched()) {
160 if (pthread_setschedparam(pthread_self(), SCHED_FIFO, ¶m) != 0) {
161 ALOGW("Failed to set SCHED_FIFO on dispatch thread");
162 }
163 }
164
165 if (pthread_setname_np(pthread_self(), "TimerDispatch") != 0) {
166 ALOGW("Failed to set thread name on dispatch thread");
167 }
168
169 enum DispatchType : uint32_t { TIMER, TERMINATE, MAX_DISPATCH_TYPE };
170 epoll_event timerEvent;
171 timerEvent.events = EPOLLIN;
172 timerEvent.data.u32 = DispatchType::TIMER;
173 if (epoll_ctl(mEpollFd, EPOLL_CTL_ADD, mTimerFd, &timerEvent) == -1) {
174 ALOGE("Error adding timer fd to epoll dispatch loop");
175 return true;
176 }
177
178 epoll_event terminateEvent;
179 terminateEvent.events = EPOLLIN;
180 terminateEvent.data.u32 = DispatchType::TERMINATE;
181 if (epoll_ctl(mEpollFd, EPOLL_CTL_ADD, mPipes[kReadPipe], &terminateEvent) == -1) {
182 ALOGE("Error adding control fd to dispatch loop");
183 return true;
184 }
185
186 uint64_t iteration = 0;
187
188 while (true) {
189 setDebugState(DebugState::Waiting);
190 epoll_event events[DispatchType::MAX_DISPATCH_TYPE];
191 int nfds = epoll_wait(mEpollFd, events, DispatchType::MAX_DISPATCH_TYPE, -1);
192
193 setDebugState(DebugState::Running);
194 if (SFTRACE_ENABLED()) {
195 ftl::Concat trace("TimerIteration #", iteration++);
196 SFTRACE_NAME(trace.c_str());
197 }
198
199 if (nfds == -1) {
200 if (errno != EINTR) {
201 ALOGE("Error waiting on epoll: %s", strerror(errno));
202 return true;
203 }
204 }
205
206 for (auto i = 0; i < nfds; i++) {
207 if (events[i].data.u32 == DispatchType::TIMER) {
208 static uint64_t mIgnored = 0;
209 setDebugState(DebugState::Reading);
210 read(mTimerFd, &mIgnored, sizeof(mIgnored));
211 setDebugState(DebugState::Running);
212 std::function<void()> cb;
213 {
214 std::lock_guard lock(mMutex);
215 cb = mCallback;
216 mExpectingCallback = false;
217 }
218 if (cb) {
219 setDebugState(DebugState::InCallback);
220 cb();
221 setDebugState(DebugState::Running);
222 }
223 }
224 if (events[i].data.u32 == DispatchType::TERMINATE) {
225 ALOGE("Terminated");
226 setDebugState(DebugState::Running);
227 return false;
228 }
229 }
230 }
231 }
232
setDebugState(DebugState state)233 void Timer::setDebugState(DebugState state) {
234 std::lock_guard lock(mMutex);
235 mDebugState = state;
236 }
237
setCallback(std::function<void ()> && callback)238 void Timer::setCallback(std::function<void()>&& callback) {
239 mExpectingCallback = bool(callback);
240 mCallback = std::move(callback);
241 }
242
dump(std::string & result) const243 void Timer::dump(std::string& result) const {
244 std::lock_guard lock(mMutex);
245 result.append("\t\tDebugState: ");
246 result.append(ftl::enum_string(mDebugState));
247 result.push_back('\n');
248 }
249
250 } // namespace android::scheduler
251