Evict buffers from idle bufferpool

Evict cached buffers from idle bufferpool in order to reduce memory
consumption.

Bug: 146679370
Test: atest CtsMediaTestCases -- --module-arg CtsMediaTestCases:size:small
Change-Id: Ic23bd36c88e4bed2a3a8f8aa277ae4c8ca89a41c
diff --git a/media/bufferpool/2.0/AccessorImpl.cpp b/media/bufferpool/2.0/AccessorImpl.cpp
index 1947656..cc1b3bd 100644
--- a/media/bufferpool/2.0/AccessorImpl.cpp
+++ b/media/bufferpool/2.0/AccessorImpl.cpp
@@ -39,6 +39,9 @@
 
     static constexpr size_t kMinAllocBytesForEviction = 1024*1024*15;
     static constexpr size_t kMinBufferCountForEviction = 25;
+
+    static constexpr nsecs_t kEvictGranularityNs = 1000000000; // 1 sec
+    static constexpr nsecs_t kEvictDurationNs = 5000000000; // 5 secs
 }
 
 // Buffer structure in bufferpool process
@@ -139,7 +142,7 @@
 
 Accessor::Impl::Impl(
         const std::shared_ptr<BufferPoolAllocator> &allocator)
-        : mAllocator(allocator) {}
+        : mAllocator(allocator), mScheduleEvictTs(0) {}
 
 Accessor::Impl::~Impl() {
 }
@@ -177,6 +180,7 @@
         }
         mBufferPool.processStatusMessages();
         mBufferPool.cleanUp();
+        scheduleEvictIfNeeded();
     }
     return status;
 }
@@ -191,6 +195,7 @@
     // Since close# will be called after all works are finished, it is OK to
     // evict unused buffers.
     mBufferPool.cleanUp(true);
+    scheduleEvictIfNeeded();
     return ResultStatus::OK;
 }
 
@@ -217,6 +222,7 @@
         mBufferPool.handleOwnBuffer(connectionId, *bufferId);
     }
     mBufferPool.cleanUp();
+    scheduleEvictIfNeeded();
     return status;
 }
 
@@ -242,6 +248,7 @@
         }
     }
     mBufferPool.cleanUp();
+    scheduleEvictIfNeeded();
     return ResultStatus::CRITICAL_ERROR;
 }
 
@@ -884,6 +891,88 @@
     }
 }
 
+void Accessor::Impl::evictorThread(
+        std::map<const std::weak_ptr<Accessor::Impl>, nsecs_t, std::owner_less<>> &accessors,
+        std::mutex &mutex,
+        std::condition_variable &cv) {
+    std::list<const std::weak_ptr<Accessor::Impl>> evictList;
+    while (true) {
+        int expired = 0;
+        int evicted = 0;
+        {
+            nsecs_t now = systemTime();
+            std::unique_lock<std::mutex> lock(mutex);
+            if (accessors.size() == 0) {
+                cv.wait(lock);
+            }
+            auto it = accessors.begin();
+            while (it != accessors.end()) {
+                if (now > (it->second + kEvictDurationNs)) {
+                    ++expired;
+                    evictList.push_back(it->first);
+                    it = accessors.erase(it);
+                } else {
+                    ++it;
+                }
+            }
+        }
+        // evict idle accessors;
+        for (auto it = evictList.begin(); it != evictList.end(); ++it) {
+            const std::shared_ptr<Accessor::Impl> accessor = it->lock();
+            if (accessor) {
+                accessor->cleanUp(true);
+                ++evicted;
+            }
+        }
+        if (expired > 0) {
+            ALOGD("evictor expired: %d, evicted: %d", expired, evicted);
+        }
+        evictList.clear();
+        ::usleep(kEvictGranularityNs / 1000);
+    }
+}
+
+Accessor::Impl::AccessorEvictor::AccessorEvictor() {
+    std::thread evictor(
+            evictorThread,
+            std::ref(mAccessors),
+            std::ref(mMutex),
+            std::ref(mCv));
+    evictor.detach();
+}
+
+void Accessor::Impl::AccessorEvictor::addAccessor(
+        const std::weak_ptr<Accessor::Impl> &impl, nsecs_t ts) {
+    std::lock_guard<std::mutex> lock(mMutex);
+    bool notify = mAccessors.empty();
+    auto it = mAccessors.find(impl);
+    if (it == mAccessors.end()) {
+        mAccessors.emplace(impl, ts);
+    } else {
+        it->second = ts;
+    }
+    if (notify) {
+        mCv.notify_one();
+    }
+}
+
+std::unique_ptr<Accessor::Impl::AccessorEvictor> Accessor::Impl::sEvictor;
+
+void Accessor::Impl::createEvictor() {
+    if (!sEvictor) {
+        sEvictor = std::make_unique<Accessor::Impl::AccessorEvictor>();
+    }
+}
+
+void Accessor::Impl::scheduleEvictIfNeeded() {
+    nsecs_t now = systemTime();
+
+    if (now > (mScheduleEvictTs + kEvictGranularityNs)) {
+        mScheduleEvictTs = now;
+        sEvictor->addAccessor(shared_from_this(), now);
+    }
+}
+
 }  // namespace implementation
 }  // namespace V2_0
 }  // namespace bufferpool