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