• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
alarmAt(std::function<void ()> const & cb,nsecs_t time)91 void Timer::alarmAt(std::function<void()> const& cb, nsecs_t time) {
92     std::lock_guard lock(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>(time / ns_per_s),
103                      .tv_nsec = static_cast<long>(time % ns_per_s)},
104     };
105 
106     if (timerfd_settime(mTimerFd, TFD_TIMER_ABSTIME, &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 lock(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, &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");
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 lock(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 lock(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 lock(mMutex);
237     StringAppendF(&result, "\t\tDebugState: %s\n", strDebugState(mDebugState));
238 }
239 
240 } // namespace android::scheduler
241