blob: 8f81c2c65e25068697ddcae1403f9c0dd1c347c3 [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
Kevin DuBoisecb1f0d2019-12-12 10:47:41 -080028#include "SchedulerUtils.h"
Kevin DuBoiscc27b502019-11-13 09:40:07 -080029#include "Timer.h"
30
31namespace android::scheduler {
32
33static constexpr size_t kReadPipe = 0;
34static constexpr size_t kWritePipe = 1;
35
Kevin DuBoiscc27b502019-11-13 09:40:07 -080036Timer::Timer()
37 : mTimerFd(timerfd_create(CLOCK_MONOTONIC, TFD_CLOEXEC | TFD_NONBLOCK)),
38 mEpollFd(epoll_create1(EPOLL_CLOEXEC)) {
39 if (pipe2(mPipes.data(), O_CLOEXEC | O_NONBLOCK)) {
40 ALOGE("could not create TimerDispatch mPipes");
41 };
42
43 mDispatchThread = std::thread(std::bind(&Timer::dispatch, this));
44}
45
46Timer::~Timer() {
47 endDispatch();
48 mDispatchThread.join();
49
50 close(mPipes[kWritePipe]);
51 close(mPipes[kReadPipe]);
52 close(mEpollFd);
53 close(mTimerFd);
54}
55
56void Timer::endDispatch() {
57 static constexpr unsigned char end = 'e';
58 write(mPipes[kWritePipe], &end, sizeof(end));
59}
60
61nsecs_t Timer::now() const {
62 return systemTime(SYSTEM_TIME_MONOTONIC);
63}
64
Kevin DuBoiscc27b502019-11-13 09:40:07 -080065void Timer::alarmIn(std::function<void()> const& cb, nsecs_t fireIn) {
66 std::lock_guard<decltype(mMutex)> lk(mMutex);
Kevin DuBoiscc27b502019-11-13 09:40:07 -080067 using namespace std::literals;
68 static constexpr int ns_per_s =
69 std::chrono::duration_cast<std::chrono::nanoseconds>(1s).count();
70
71 mCallback = cb;
72
73 struct itimerspec old_timer;
74 struct itimerspec new_timer {
75 .it_interval = {.tv_sec = 0, .tv_nsec = 0},
76 .it_value = {.tv_sec = static_cast<long>(fireIn / ns_per_s),
77 .tv_nsec = static_cast<long>(fireIn % ns_per_s)},
78 };
79
80 if (timerfd_settime(mTimerFd, 0, &new_timer, &old_timer)) {
Kevin DuBois127a2d92019-12-04 13:52:52 -080081 ALOGW("Failed to set timerfd %s (%i)", strerror(errno), errno);
Kevin DuBoiscc27b502019-11-13 09:40:07 -080082 }
83}
84
85void Timer::alarmCancel() {
86 std::lock_guard<decltype(mMutex)> lk(mMutex);
Kevin DuBoiscc27b502019-11-13 09:40:07 -080087
88 struct itimerspec old_timer;
89 struct itimerspec new_timer {
90 .it_interval = {.tv_sec = 0, .tv_nsec = 0},
91 .it_value = {
92 .tv_sec = 0,
93 .tv_nsec = 0,
94 },
95 };
96
97 if (timerfd_settime(mTimerFd, 0, &new_timer, &old_timer)) {
98 ALOGW("Failed to disarm timerfd");
99 }
100}
101
102void Timer::dispatch() {
103 struct sched_param param = {0};
104 param.sched_priority = 2;
105 if (pthread_setschedparam(pthread_self(), SCHED_FIFO, &param) != 0) {
106 ALOGW("Failed to set SCHED_FIFO on dispatch thread");
107 }
108
109 if (pthread_setname_np(pthread_self(), "TimerDispatch")) {
110 ALOGW("Failed to set thread name on dispatch thread");
111 }
112
113 enum DispatchType : uint32_t { TIMER, TERMINATE, MAX_DISPATCH_TYPE };
114 epoll_event timerEvent;
115 timerEvent.events = EPOLLIN;
116 timerEvent.data.u32 = DispatchType::TIMER;
117 if (epoll_ctl(mEpollFd, EPOLL_CTL_ADD, mTimerFd, &timerEvent) == -1) {
118 ALOGE("Error adding timer fd to epoll dispatch loop");
119 return;
120 }
121
122 epoll_event terminateEvent;
123 terminateEvent.events = EPOLLIN;
124 terminateEvent.data.u32 = DispatchType::TERMINATE;
125 if (epoll_ctl(mEpollFd, EPOLL_CTL_ADD, mPipes[kReadPipe], &terminateEvent) == -1) {
126 ALOGE("Error adding control fd to dispatch loop");
127 return;
128 }
129
130 uint64_t iteration = 0;
131 char const traceNamePrefix[] = "TimerIteration #";
Kevin DuBoiscc27b502019-11-13 09:40:07 -0800132 static constexpr size_t maxlen = arrayLen(traceNamePrefix) + max64print;
133 std::array<char, maxlen> str_buffer;
134 auto timing = true;
135 while (timing) {
136 epoll_event events[DispatchType::MAX_DISPATCH_TYPE];
137 int nfds = epoll_wait(mEpollFd, events, DispatchType::MAX_DISPATCH_TYPE, -1);
138
139 if (ATRACE_ENABLED()) {
140 snprintf(str_buffer.data(), str_buffer.size(), "%s%" PRIu64, traceNamePrefix,
141 iteration++);
142 ATRACE_NAME(str_buffer.data());
143 }
144
145 if (nfds == -1) {
146 if (errno != EINTR) {
147 timing = false;
148 continue;
149 }
150 }
151
152 for (auto i = 0; i < nfds; i++) {
153 if (events[i].data.u32 == DispatchType::TIMER) {
154 static uint64_t mIgnored = 0;
155 read(mTimerFd, &mIgnored, sizeof(mIgnored));
156 std::function<void()> cb;
157 {
158 std::lock_guard<decltype(mMutex)> lk(mMutex);
159 cb = mCallback;
160 }
161 if (cb) {
162 cb();
163 }
164 }
165 if (events[i].data.u32 == DispatchType::TERMINATE) {
166 timing = false;
167 }
168 }
169 }
170}
171
172} // namespace android::scheduler