EGL: Close Multifile Blobcache files after mapping

When loading entries from disk, we don't need to keep the
files open after mapping their contents.

Per mmap documentation:

  After the mmap() call has returned, the file descriptor, fd, can
  be closed immediately without invalidating the mapping.

  https://man7.org/linux/man-pages/man2/mmap.2.html

This will prevent consuming excessive file descriptors, which
are a limited resource.

Added new test that ensures file descriptors do not remain open.

Test: libEGL_test, EGL_test, restricted_trace_perf.py
Bug: b/286809755
Change-Id: I6317fdbce340a8e7cbf3020ad41386cf9915dd2d
diff --git a/opengl/libs/EGL/MultifileBlobCache_test.cpp b/opengl/libs/EGL/MultifileBlobCache_test.cpp
index dbee13b..1639be6 100644
--- a/opengl/libs/EGL/MultifileBlobCache_test.cpp
+++ b/opengl/libs/EGL/MultifileBlobCache_test.cpp
@@ -42,6 +42,8 @@
 
     virtual void TearDown() { mMBC.reset(); }
 
+    int getFileDescriptorCount();
+
     std::unique_ptr<TemporaryFile> mTempFile;
     std::unique_ptr<MultifileBlobCache> mMBC;
 };
@@ -216,4 +218,56 @@
     ASSERT_EQ('y', buf[0]);
 }
 
+int MultifileBlobCacheTest::getFileDescriptorCount() {
+    DIR* directory = opendir("/proc/self/fd");
+
+    int fileCount = 0;
+    struct dirent* entry;
+    while ((entry = readdir(directory)) != NULL) {
+        fileCount++;
+        // printf("File: %s\n", entry->d_name);
+    }
+
+    closedir(directory);
+    return fileCount;
+}
+
+TEST_F(MultifileBlobCacheTest, EnsureFileDescriptorsClosed) {
+    // Populate the cache with a bunch of entries
+    size_t kLargeNumberOfEntries = 1024;
+    for (int i = 0; i < kLargeNumberOfEntries; i++) {
+        // printf("Caching: %i", i);
+
+        // Use the index as the key and value
+        mMBC->set(&i, sizeof(i), &i, sizeof(i));
+
+        int result = 0;
+        ASSERT_EQ(sizeof(i), mMBC->get(&i, sizeof(i), &result, sizeof(result)));
+        ASSERT_EQ(i, result);
+    }
+
+    // Ensure we don't have a bunch of open fds
+    ASSERT_LT(getFileDescriptorCount(), kLargeNumberOfEntries / 2);
+
+    // Close the cache so everything writes out
+    mMBC->finish();
+    mMBC.reset();
+
+    // Now open it again and ensure we still don't have a bunch of open fds
+    mMBC.reset(
+            new MultifileBlobCache(kMaxKeySize, kMaxValueSize, kMaxTotalSize, &mTempFile->path[0]));
+
+    // Check after initialization
+    ASSERT_LT(getFileDescriptorCount(), kLargeNumberOfEntries / 2);
+
+    for (int i = 0; i < kLargeNumberOfEntries; i++) {
+        int result = 0;
+        ASSERT_EQ(sizeof(i), mMBC->get(&i, sizeof(i), &result, sizeof(result)));
+        ASSERT_EQ(i, result);
+    }
+
+    // And again after we've actually used it
+    ASSERT_LT(getFileDescriptorCount(), kLargeNumberOfEntries / 2);
+}
+
 } // namespace android