diff options
-rw-r--r-- | include/ui/Fence.h | 9 | ||||
-rw-r--r-- | include/ui/FenceTime.h | 161 | ||||
-rw-r--r-- | libs/ui/Android.bp | 1 | ||||
-rw-r--r-- | libs/ui/Fence.cpp | 6 | ||||
-rw-r--r-- | libs/ui/FenceTime.cpp | 282 |
5 files changed, 454 insertions, 5 deletions
diff --git a/include/ui/Fence.h b/include/ui/Fence.h index 1df15f897c..99b39d8423 100644 --- a/include/ui/Fence.h +++ b/include/ui/Fence.h @@ -42,6 +42,11 @@ class Fence { public: static const sp<Fence> NO_FENCE; + static constexpr nsecs_t SIGNAL_TIME_PENDING = INT64_MAX; + static constexpr nsecs_t SIGNAL_TIME_INVALID = -1; + static inline bool isValidTimestamp(nsecs_t time) { + return time >= 0 && time < INT64_MAX; + } // TIMEOUT_NEVER may be passed to the wait method to indicate that it // should wait indefinitely for the fence to signal. @@ -94,8 +99,8 @@ public: // getSignalTime returns the system monotonic clock time at which the // fence transitioned to the signaled state. If the fence is not signaled - // then INT64_MAX is returned. If the fence is invalid or if an error - // occurs then -1 is returned. + // then SIGNAL_TIME_PENDING is returned. If the fence is invalid or if an + // error occurs then SIGNAL_TIME_INVALID is returned. nsecs_t getSignalTime() const; #if __cplusplus > 201103L diff --git a/include/ui/FenceTime.h b/include/ui/FenceTime.h new file mode 100644 index 0000000000..27cc720028 --- /dev/null +++ b/include/ui/FenceTime.h @@ -0,0 +1,161 @@ +/* + * Copyright (C) 2016 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. + */ + +#ifndef ANDROID_FENCE_TIME_H +#define ANDROID_FENCE_TIME_H + +#include <ui/Fence.h> +#include <utils/Flattenable.h> +#include <utils/Timers.h> + +#include <atomic> +#include <mutex> +#include <queue> + +namespace android { + +// A wrapper around fence that only implements isValid and getSignalTime. +// It automatically closes the fence in a thread-safe manner once the signal +// time is known. +class FenceTime { +public: + // An atomic snapshot of the FenceTime that is flattenable. + // + // This class is needed because the FenceTime class may not stay + // consistent for all steps of the flattening process. + // + // Not thread safe. + struct Snapshot : public Flattenable<Snapshot> { + enum class State { + EMPTY, + FENCE, + SIGNAL_TIME, + }; + + Snapshot() = default; // Creates an empty snapshot. + explicit Snapshot(const sp<Fence>& fence); + explicit Snapshot(nsecs_t signalTime); + + // Movable. + Snapshot(Snapshot&& src) = default; + Snapshot& operator=(Snapshot&& src) = default; + // Not copyable. + Snapshot(const Snapshot& src) = delete; + Snapshot& operator=(const Snapshot&& src) = delete; + + // Flattenable implementation. + size_t getFlattenedSize() const; + size_t getFdCount() const; + status_t flatten(void*& buffer, size_t& size, int*& fds, + size_t& count) const; + status_t unflatten(void const*& buffer, size_t& size, int const*& fds, + size_t& count); + + State state{State::EMPTY}; + sp<Fence> fence{Fence::NO_FENCE}; + nsecs_t signalTime{Fence::SIGNAL_TIME_INVALID}; + }; + + static const std::shared_ptr<FenceTime> NO_FENCE; + + explicit FenceTime(const sp<Fence>& fence); + explicit FenceTime(sp<Fence>&& fence); + + // Passing in Fence::SIGNAL_TIME_PENDING is not allowed. + // Doing so will convert the signalTime to Fence::SIGNAL_TIME_INVALID. + explicit FenceTime(nsecs_t signalTime); + + // Do not allow default construction. Share NO_FENCE or explicitly construct + // with Fence::SIGNAL_TIME_INVALID instead. + FenceTime() = delete; + + // Do not allow copy, assign, or move. Use a shared_ptr to share the + // signalTime result. Or use getSnapshot() if a thread-safe copy is really + // needed. + FenceTime(const FenceTime&) = delete; + FenceTime(FenceTime&&) = delete; + FenceTime& operator=(const FenceTime&) = delete; + FenceTime& operator=(FenceTime&&) = delete; + + // This method should only be called when replacing the fence with + // a signalTime. Since this is an indirect way of setting the signal time + // of a fence, the snapshot should come from a trusted source. + void applyTrustedSnapshot(const Snapshot& src); + + bool isValid() const; + + // Attempts to get the timestamp from the Fence if the timestamp isn't + // already cached. Otherwise, it returns the cached value. + nsecs_t getSignalTime(); + + // Gets the cached timestamp without attempting to query the Fence. + nsecs_t getCachedSignalTime() const; + + // Returns a snapshot of the FenceTime in its current state. + Snapshot getSnapshot() const; + + // Override new and delete since this needs 8-byte alignment, which + // is not guaranteed on x86. + static void* operator new(size_t nbytes) noexcept; + static void operator delete(void *p); + +private: + enum class State { + VALID, + INVALID, + }; + + const State mState{State::INVALID}; + + // mMutex guards mFence and mSignalTime. + // mSignalTime is also atomic since it is sometimes read outside the lock + // for quick checks. + mutable std::mutex mMutex; + sp<Fence> mFence{Fence::NO_FENCE}; + std::atomic<nsecs_t> mSignalTime{Fence::SIGNAL_TIME_INVALID}; +}; + +// A queue of FenceTimes that are expected to signal in FIFO order. +// Only maintains a queue of weak pointers so it doesn't keep references +// to Fences on its own. +// +// Can be used to get the signal time of a fence and close its file descriptor +// without making a syscall for every fence later in the timeline. +// Additionally, since the FenceTime caches the timestamp internally, +// other timelines that reference the same FenceTime can avoid the syscall. +// +// FenceTimeline only keeps track of a limited number of entries to avoid +// growing unbounded. Users of FenceTime must make sure they can work even +// if FenceTimeline did nothing. i.e. they should eventually call +// Fence::getSignalTime(), not only Fence::getCachedSignalTime(). +// +// push() and updateSignalTimes() are safe to call simultaneously from +// different threads. +class FenceTimeline { +public: + static constexpr size_t MAX_ENTRIES = 64; + + void push(const std::shared_ptr<FenceTime>& fence); + void updateSignalTimes(); + +private: + mutable std::mutex mMutex; + std::queue<std::weak_ptr<FenceTime>> mQueue; +}; + +}; // namespace android + +#endif // ANDROID_FENCE_TIME_H diff --git a/libs/ui/Android.bp b/libs/ui/Android.bp index 087e877263..661d1c802a 100644 --- a/libs/ui/Android.bp +++ b/libs/ui/Android.bp @@ -43,6 +43,7 @@ cc_library_shared { srcs: [ "ColorSpace.cpp", "Fence.cpp", + "FenceTime.cpp", "FrameStats.cpp", "Gralloc1.cpp", "Gralloc1On0Adapter.cpp", diff --git a/libs/ui/Fence.cpp b/libs/ui/Fence.cpp index 7cf8233820..a1dda3a475 100644 --- a/libs/ui/Fence.cpp +++ b/libs/ui/Fence.cpp @@ -109,17 +109,17 @@ int Fence::dup() const { nsecs_t Fence::getSignalTime() const { if (mFenceFd == -1) { - return -1; + return SIGNAL_TIME_INVALID; } struct sync_fence_info_data* finfo = sync_fence_info(mFenceFd); if (finfo == NULL) { ALOGE("sync_fence_info returned NULL for fd %d", mFenceFd); - return -1; + return SIGNAL_TIME_INVALID; } if (finfo->status != 1) { sync_fence_info_free(finfo); - return INT64_MAX; + return SIGNAL_TIME_PENDING; } struct sync_pt_info* pinfo = NULL; diff --git a/libs/ui/FenceTime.cpp b/libs/ui/FenceTime.cpp new file mode 100644 index 0000000000..c0245ebb89 --- /dev/null +++ b/libs/ui/FenceTime.cpp @@ -0,0 +1,282 @@ +/* +* Copyright 2016 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. +*/ + +#include <ui/FenceTime.h> + +#include <cutils/compiler.h> // For CC_[UN]LIKELY +#include <inttypes.h> +#include <stdlib.h> + +#include <memory> + +namespace android { + +// ============================================================================ +// FenceTime +// ============================================================================ + +const auto FenceTime::NO_FENCE = std::make_shared<FenceTime>(Fence::NO_FENCE); + +void* FenceTime::operator new(size_t byteCount) noexcept { + void *p = nullptr; + if (posix_memalign(&p, alignof(FenceTime), byteCount)) { + return nullptr; + } + return p; +} + +void FenceTime::operator delete(void *p) { + free(p); +} + +FenceTime::FenceTime(const sp<Fence>& fence) + : mState(((fence.get() != nullptr) && fence->isValid()) ? + State::VALID : State::INVALID), + mFence(fence), + mSignalTime(mState == State::INVALID ? + Fence::SIGNAL_TIME_INVALID : Fence::SIGNAL_TIME_PENDING) { +} + +FenceTime::FenceTime(sp<Fence>&& fence) + : mState(((fence.get() != nullptr) && fence->isValid()) ? + State::VALID : State::INVALID), + mFence(std::move(fence)), + mSignalTime(mState == State::INVALID ? + Fence::SIGNAL_TIME_INVALID : Fence::SIGNAL_TIME_PENDING) { +} + +FenceTime::FenceTime(nsecs_t signalTime) + : mState(Fence::isValidTimestamp(signalTime) ? State::VALID : State::INVALID), + mFence(nullptr), + mSignalTime(signalTime == Fence::SIGNAL_TIME_PENDING ? + Fence::SIGNAL_TIME_INVALID : signalTime) { +} + +void FenceTime::applyTrustedSnapshot(const Snapshot& src) { + if (CC_UNLIKELY(src.state != Snapshot::State::SIGNAL_TIME)) { + // Applying Snapshot::State::FENCE, could change the valid state of the + // FenceTime, which is not allowed. Callers should create a new + // FenceTime from the snapshot instead. + ALOGE("FenceTime::applyTrustedSnapshot: Unexpected fence."); + return; + } + + if (src.state == Snapshot::State::EMPTY) { + return; + } + + nsecs_t signalTime = mSignalTime.load(std::memory_order_relaxed); + if (signalTime != Fence::SIGNAL_TIME_PENDING) { + // We should always get the same signalTime here that we did in + // getSignalTime(). This check races with getSignalTime(), but it is + // only a sanity check so that's okay. + if (CC_UNLIKELY(signalTime != src.signalTime)) { + ALOGE("FenceTime::applyTrustedSnapshot: signalTime mismatch. " + "(%" PRId64 " (old) != %" PRId64 " (new))", + signalTime, src.signalTime); + } + return; + } + + std::lock_guard<std::mutex> lock(mMutex); + mFence.clear(); + mSignalTime.store(src.signalTime, std::memory_order_relaxed); +} + +bool FenceTime::isValid() const { + // We store the valid state in the constructors and return it here. + // This lets release code remember the valid state even after the + // underlying fence is destroyed. + return mState != State::INVALID; +} + +nsecs_t FenceTime::getSignalTime() { + // See if we already have a cached value we can return. + nsecs_t signalTime = mSignalTime.load(std::memory_order_relaxed); + if (signalTime != Fence::SIGNAL_TIME_PENDING) { + return signalTime; + } + + // Hold a reference to the fence on the stack in case the class' + // reference is removed by another thread. This prevents the + // fence from being destroyed until the end of this method, where + // we conveniently do not have the lock held. + sp<Fence> fence; + { + // With the lock acquired this time, see if we have the cached + // value or if we need to poll the fence. + std::lock_guard<std::mutex> lock(mMutex); + if (!mFence.get()) { + // Another thread set the signal time just before we added the + // reference to mFence. + return mSignalTime.load(std::memory_order_relaxed); + } + fence = mFence; + } + + // Make the system call without the lock held. + signalTime = fence->getSignalTime(); + + // Make the signal time visible to everyone if it is no longer pending + // and remove the class' reference to the fence. + if (signalTime != Fence::SIGNAL_TIME_PENDING) { + std::lock_guard<std::mutex> lock(mMutex); + mFence.clear(); + mSignalTime.store(signalTime, std::memory_order_relaxed); + } + + return signalTime; +} + +nsecs_t FenceTime::getCachedSignalTime() const { + // memory_order_acquire since we don't have a lock fallback path + // that will do an acquire. + return mSignalTime.load(std::memory_order_acquire); +} + +FenceTime::Snapshot FenceTime::getSnapshot() const { + // Quick check without the lock. + nsecs_t signalTime = mSignalTime.load(std::memory_order_relaxed); + if (signalTime != Fence::SIGNAL_TIME_PENDING) { + return Snapshot(signalTime); + } + + // Do the full check with the lock. + std::lock_guard<std::mutex> lock(mMutex); + signalTime = mSignalTime.load(std::memory_order_relaxed); + if (signalTime != Fence::SIGNAL_TIME_PENDING) { + return Snapshot(signalTime); + } + return Snapshot(mFence); +} + +// ============================================================================ +// FenceTime::Snapshot +// ============================================================================ + +FenceTime::Snapshot::Snapshot(const sp<Fence>& srcFence) + : state(State::FENCE), fence(srcFence) { +} + +FenceTime::Snapshot::Snapshot(nsecs_t srcSignalTime) + : state(State::SIGNAL_TIME), signalTime(srcSignalTime) { +} + +size_t FenceTime::Snapshot::getFlattenedSize() const { + constexpr size_t min = sizeof(state); + switch (state) { + case State::EMPTY: + return min; + case State::FENCE: + return min + fence->getFlattenedSize(); + case State::SIGNAL_TIME: + return min + sizeof(signalTime); + } + return 0; +} + +size_t FenceTime::Snapshot::getFdCount() const { + return state == State::FENCE ? fence->getFdCount() : 0u; +} + +status_t FenceTime::Snapshot::flatten( + void*& buffer, size_t& size, int*& fds, size_t& count) const { + if (size < getFlattenedSize()) { + return NO_MEMORY; + } + + FlattenableUtils::write(buffer, size, state); + switch (state) { + case State::EMPTY: + return NO_ERROR; + case State::FENCE: + return fence->flatten(buffer, size, fds, count); + case State::SIGNAL_TIME: + FlattenableUtils::write(buffer, size, signalTime); + return NO_ERROR; + } + + return NO_ERROR; +} + +status_t FenceTime::Snapshot::unflatten( + void const*& buffer, size_t& size, int const*& fds, size_t& count) { + if (size < sizeof(state)) { + return NO_MEMORY; + } + + FlattenableUtils::read(buffer, size, state); + switch (state) { + case State::EMPTY: + return NO_ERROR; + case State::FENCE: + fence = new Fence; + return fence->unflatten(buffer, size, fds, count); + case State::SIGNAL_TIME: + if (size < sizeof(signalTime)) { + return NO_MEMORY; + } + FlattenableUtils::read(buffer, size, signalTime); + return NO_ERROR; + } + + return NO_ERROR; +} + +// ============================================================================ +// FenceTimeline +// ============================================================================ +void FenceTimeline::push(const std::shared_ptr<FenceTime>& fence) { + std::lock_guard<std::mutex> lock(mMutex); + while (mQueue.size() >= MAX_ENTRIES) { + // This is a sanity check to make sure the queue doesn't grow unbounded. + // MAX_ENTRIES should be big enough not to trigger this path. + // In case this path is taken though, users of FenceTime must make sure + // not to rely solely on FenceTimeline to get the final timestamp and + // should eventually call Fence::getSignalTime on their own. + std::shared_ptr<FenceTime> front = mQueue.front().lock(); + if (front) { + // Make a last ditch effort to get the signalTime here since + // we are removing it from the timeline. + front->getSignalTime(); + } + mQueue.pop(); + } + mQueue.push(fence); +} + +void FenceTimeline::updateSignalTimes() { + while (!mQueue.empty()) { + std::lock_guard<std::mutex> lock(mMutex); + std::shared_ptr<FenceTime> fence = mQueue.front().lock(); + if (!fence) { + // The shared_ptr no longer exists and no one cares about the + // timestamp anymore. + mQueue.pop(); + continue; + } else if (fence->getSignalTime() != Fence::SIGNAL_TIME_PENDING) { + // The fence has signaled and we've removed the sp<Fence> ref. + mQueue.pop(); + continue; + } else { + // The fence didn't signal yet. Break since the later ones + // shouldn't have signaled either. + break; + } + } +} + +} // namespace android |