Codec2 vndk: clear pending lock by a dead client process
Bug: 312707275
Bug: 309898317
Bug: 304183768
Change-Id: I5c6c2bbdd4859220a3f41f83998d88d0a7b9ddcb
diff --git a/media/codec2/vndk/include/C2SurfaceSyncObj.h b/media/codec2/vndk/include/C2SurfaceSyncObj.h
index 0005049..7c1a405 100644
--- a/media/codec2/vndk/include/C2SurfaceSyncObj.h
+++ b/media/codec2/vndk/include/C2SurfaceSyncObj.h
@@ -122,6 +122,11 @@
*/
void invalidate();
+ /**
+ * If a dead process holds the lock, clear the lock.
+ */
+ void clearLockIfNecessary();
+
C2SyncVariables() {}
private:
@@ -140,6 +145,11 @@
*/
int wait();
+ /**
+ * try lock for the specified duration.
+ */
+ bool tryLockFor(size_t ms);
+
std::atomic<uint32_t> mLock;
std::atomic<uint32_t> mCond;
int32_t mMaxDequeueCount;
diff --git a/media/codec2/vndk/platform/C2BqBuffer.cpp b/media/codec2/vndk/platform/C2BqBuffer.cpp
index 43a3eb2..48157c8 100644
--- a/media/codec2/vndk/platform/C2BqBuffer.cpp
+++ b/media/codec2/vndk/platform/C2BqBuffer.cpp
@@ -802,6 +802,13 @@
if (oldVar) {
oldVar->invalidate();
}
+ // invalidate pending lock from a dead process if any
+ if (syncVar) {
+ syncVar->clearLockIfNecessary();
+ }
+ if (oldVar) {
+ oldVar->clearLockIfNecessary();
+ }
}
private:
diff --git a/media/codec2/vndk/platform/C2SurfaceSyncObj.cpp b/media/codec2/vndk/platform/C2SurfaceSyncObj.cpp
index 0d8ed0b..41d16b5 100644
--- a/media/codec2/vndk/platform/C2SurfaceSyncObj.cpp
+++ b/media/codec2/vndk/platform/C2SurfaceSyncObj.cpp
@@ -26,6 +26,33 @@
#include <chrono>
#include <C2SurfaceSyncObj.h>
+namespace {
+static inline void timespec_add_ms(timespec& ts, size_t ms) {
+ constexpr int kNanoSecondsPerSec = 1000000000;
+ ts.tv_sec += ms / 1000;
+ ts.tv_nsec += (ms % 1000) * 1000000;
+ if (ts.tv_nsec >= kNanoSecondsPerSec) {
+ ts.tv_sec++;
+ ts.tv_nsec -= kNanoSecondsPerSec;
+ }
+}
+
+/*
+ * lhs < rhs: return <0
+ * lhs == rhs: return 0
+ * lhs > rhs: return >0
+ */
+static inline int timespec_compare(const timespec& lhs, const timespec& rhs) {
+ if (lhs.tv_sec < rhs.tv_sec) {
+ return -1;
+ }
+ if (lhs.tv_sec > rhs.tv_sec) {
+ return 1;
+ }
+ return lhs.tv_nsec - rhs.tv_nsec;
+}
+}
+
const native_handle_t C2SurfaceSyncMemory::HandleSyncMem::cHeader = {
C2SurfaceSyncMemory::HandleSyncMem::version,
C2SurfaceSyncMemory::HandleSyncMem::numFds,
@@ -289,6 +316,21 @@
(void) syscall(__NR_futex, &mCond, FUTEX_REQUEUE, INT_MAX, (void *)INT_MAX, &mLock, 0);
}
+void C2SyncVariables::clearLockIfNecessary() {
+ // Note: After waiting for 30ms without acquiring the lock,
+ // we will consider the lock is dangling.
+ // Since the lock duration is very brief to manage the counter,
+ // waiting for 30ms should be more than enough.
+ constexpr size_t kTestLockDurationMs = 30;
+
+ bool locked = tryLockFor(kTestLockDurationMs);
+ unlock();
+
+ if (!locked) {
+ ALOGW("A dead process might be holding the lock");
+ }
+}
+
int C2SyncVariables::signal() {
mCond++;
@@ -313,3 +355,35 @@
}
return 0;
}
+
+bool C2SyncVariables::tryLockFor(size_t ms) {
+ uint32_t old = FUTEX_UNLOCKED;
+
+ if (mLock.compare_exchange_strong(old, FUTEX_LOCKED_UNCONTENDED)) {
+ return true;
+ }
+
+ if (old == FUTEX_LOCKED_UNCONTENDED) {
+ old = mLock.exchange(FUTEX_LOCKED_CONTENDED);
+ }
+
+ struct timespec wait{
+ static_cast<time_t>(ms / 1000),
+ static_cast<long>((ms % 1000) * 1000000)};
+ struct timespec end;
+ clock_gettime(CLOCK_REALTIME, &end);
+ timespec_add_ms(end, ms);
+
+ while (old != FUTEX_UNLOCKED) { // case of EINTR being returned;
+ (void)syscall(__NR_futex, &mLock, FUTEX_WAIT, FUTEX_LOCKED_CONTENDED, &wait, NULL, 0);
+ old = mLock.exchange(FUTEX_LOCKED_CONTENDED);
+
+ struct timespec now;
+ clock_gettime(CLOCK_REALTIME, &now);
+ if (timespec_compare(now, end) >= 0) {
+ break;
+ }
+ }
+
+ return old == FUTEX_UNLOCKED;
+}