SF: add VSyncDispatch timing objects
Add epoll and timerfd dispatch of events.
Fixes: 140303478
Bug: 140301853
Test: 3 new VSyncDispatchRealtimeTest
Change-Id: Ibc9bbaacfe3774247fb3d059dab3769bb17f3194
diff --git a/services/surfaceflinger/Scheduler/Timer.cpp b/services/surfaceflinger/Scheduler/Timer.cpp
new file mode 100644
index 0000000..2394ed2
--- /dev/null
+++ b/services/surfaceflinger/Scheduler/Timer.cpp
@@ -0,0 +1,181 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#undef LOG_TAG
+#define LOG_TAG "SchedulerTimer"
+#define ATRACE_TAG ATRACE_TAG_GRAPHICS
+#include <log/log.h>
+#include <sys/epoll.h>
+#include <sys/timerfd.h>
+#include <sys/unistd.h>
+#include <utils/Trace.h>
+#include <chrono>
+#include <cstdint>
+
+#include "Timer.h"
+
+namespace android::scheduler {
+
+static constexpr size_t kReadPipe = 0;
+static constexpr size_t kWritePipe = 1;
+
+template <class T, size_t N>
+constexpr size_t arrayLen(T (&)[N]) {
+ return N;
+}
+
+Timer::Timer()
+ : mTimerFd(timerfd_create(CLOCK_MONOTONIC, TFD_CLOEXEC | TFD_NONBLOCK)),
+ mEpollFd(epoll_create1(EPOLL_CLOEXEC)) {
+ if (pipe2(mPipes.data(), O_CLOEXEC | O_NONBLOCK)) {
+ ALOGE("could not create TimerDispatch mPipes");
+ };
+
+ mDispatchThread = std::thread(std::bind(&Timer::dispatch, this));
+}
+
+Timer::~Timer() {
+ endDispatch();
+ mDispatchThread.join();
+
+ close(mPipes[kWritePipe]);
+ close(mPipes[kReadPipe]);
+ close(mEpollFd);
+ close(mTimerFd);
+}
+
+void Timer::endDispatch() {
+ static constexpr unsigned char end = 'e';
+ write(mPipes[kWritePipe], &end, sizeof(end));
+}
+
+nsecs_t Timer::now() const {
+ return systemTime(SYSTEM_TIME_MONOTONIC);
+}
+
+constexpr char const* timerTraceTag = "AlarmInNs";
+void Timer::alarmIn(std::function<void()> const& cb, nsecs_t fireIn) {
+ std::lock_guard<decltype(mMutex)> lk(mMutex);
+ ATRACE_INT64(timerTraceTag, fireIn);
+
+ using namespace std::literals;
+ static constexpr int ns_per_s =
+ std::chrono::duration_cast<std::chrono::nanoseconds>(1s).count();
+
+ mCallback = cb;
+
+ struct itimerspec old_timer;
+ struct itimerspec new_timer {
+ .it_interval = {.tv_sec = 0, .tv_nsec = 0},
+ .it_value = {.tv_sec = static_cast<long>(fireIn / ns_per_s),
+ .tv_nsec = static_cast<long>(fireIn % ns_per_s)},
+ };
+
+ if (timerfd_settime(mTimerFd, 0, &new_timer, &old_timer)) {
+ ALOGW("Failed to set timerfd");
+ }
+}
+
+void Timer::alarmCancel() {
+ std::lock_guard<decltype(mMutex)> lk(mMutex);
+ ATRACE_INT64(timerTraceTag, 0);
+
+ struct itimerspec old_timer;
+ struct itimerspec new_timer {
+ .it_interval = {.tv_sec = 0, .tv_nsec = 0},
+ .it_value = {
+ .tv_sec = 0,
+ .tv_nsec = 0,
+ },
+ };
+
+ if (timerfd_settime(mTimerFd, 0, &new_timer, &old_timer)) {
+ ALOGW("Failed to disarm timerfd");
+ }
+}
+
+void Timer::dispatch() {
+ struct sched_param param = {0};
+ param.sched_priority = 2;
+ if (pthread_setschedparam(pthread_self(), SCHED_FIFO, ¶m) != 0) {
+ ALOGW("Failed to set SCHED_FIFO on dispatch thread");
+ }
+
+ if (pthread_setname_np(pthread_self(), "TimerDispatch")) {
+ ALOGW("Failed to set thread name on dispatch thread");
+ }
+
+ enum DispatchType : uint32_t { TIMER, TERMINATE, MAX_DISPATCH_TYPE };
+ epoll_event timerEvent;
+ timerEvent.events = EPOLLIN;
+ timerEvent.data.u32 = DispatchType::TIMER;
+ if (epoll_ctl(mEpollFd, EPOLL_CTL_ADD, mTimerFd, &timerEvent) == -1) {
+ ALOGE("Error adding timer fd to epoll dispatch loop");
+ return;
+ }
+
+ epoll_event terminateEvent;
+ terminateEvent.events = EPOLLIN;
+ terminateEvent.data.u32 = DispatchType::TERMINATE;
+ if (epoll_ctl(mEpollFd, EPOLL_CTL_ADD, mPipes[kReadPipe], &terminateEvent) == -1) {
+ ALOGE("Error adding control fd to dispatch loop");
+ return;
+ }
+
+ uint64_t iteration = 0;
+ char const traceNamePrefix[] = "TimerIteration #";
+ static constexpr size_t max64print = std::numeric_limits<decltype(iteration)>::digits10;
+ static constexpr size_t maxlen = arrayLen(traceNamePrefix) + max64print;
+ std::array<char, maxlen> str_buffer;
+ auto timing = true;
+ while (timing) {
+ epoll_event events[DispatchType::MAX_DISPATCH_TYPE];
+ int nfds = epoll_wait(mEpollFd, events, DispatchType::MAX_DISPATCH_TYPE, -1);
+
+ if (ATRACE_ENABLED()) {
+ snprintf(str_buffer.data(), str_buffer.size(), "%s%" PRIu64, traceNamePrefix,
+ iteration++);
+ ATRACE_NAME(str_buffer.data());
+ }
+
+ if (nfds == -1) {
+ if (errno != EINTR) {
+ timing = false;
+ continue;
+ }
+ }
+
+ for (auto i = 0; i < nfds; i++) {
+ if (events[i].data.u32 == DispatchType::TIMER) {
+ static uint64_t mIgnored = 0;
+ read(mTimerFd, &mIgnored, sizeof(mIgnored));
+ std::function<void()> cb;
+ {
+ std::lock_guard<decltype(mMutex)> lk(mMutex);
+ cb = mCallback;
+ }
+ if (cb) {
+ cb();
+ }
+ }
+ if (events[i].data.u32 == DispatchType::TERMINATE) {
+ timing = false;
+ }
+ }
+ }
+}
+
+} // namespace android::scheduler