• 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 
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, &param) != 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