ml: use mmap to load the icudata file

This should reduce the memory consumption and is necessary when
ml-service is of multiprocesses.

BUG=chromium:933017
TEST=unit test of mlservice passed.

Change-Id: I4aea241a54d27cf807bfd54d3409ad13489a0ff1
Reviewed-on: https://chromium-review.googlesource.com/c/chromiumos/platform2/+/2501085
Commit-Queue: Honglin Yu <honglinyu@chromium.org>
Tested-by: Honglin Yu <honglinyu@chromium.org>
Reviewed-by: Andrew Moylan <amoylan@chromium.org>
diff --git a/ml/machine_learning_service_impl.cc b/ml/machine_learning_service_impl.cc
index bf9778b..789dfbb 100644
--- a/ml/machine_learning_service_impl.cc
+++ b/ml/machine_learning_service_impl.cc
@@ -12,6 +12,7 @@
 #include <base/bind_helpers.h>
 #include <base/files/file.h>
 #include <base/files/file_util.h>
+#include <base/files/memory_mapped_file.h>
 #include <tensorflow/lite/model.h>
 #include <unicode/putil.h>
 #include <unicode/udata.h>
@@ -52,14 +53,38 @@
 
 constexpr char kIcuDataFilePath[] = "/opt/google/chrome/icudtl.dat";
 
+// Used to hold the mmap object of the icu data file. Each process should only
+// have one instance of it. Intentionally never close it.
+// We can not make it as a member of `MachineLearningServiceImpl` because it
+// will crash the unit test (because in that case, when the
+// `MachineLearningServiceImpl` object is destructed, the file will be
+// unmapped but the icu data can not be reset in the testing process).
+base::MemoryMappedFile* g_icu_data_mmap_file = nullptr;
+
+void InitIcuIfNeeded() {
+  if (!g_icu_data_mmap_file) {
+    g_icu_data_mmap_file = new base::MemoryMappedFile();
+    CHECK(g_icu_data_mmap_file->Initialize(
+        base::FilePath(kIcuDataFilePath),
+        base::MemoryMappedFile::Access::READ_ONLY));
+    // Init the Icu library.
+    UErrorCode err = U_ZERO_ERROR;
+    udata_setCommonData(const_cast<uint8_t*>(g_icu_data_mmap_file->data()),
+                        &err);
+    DCHECK(err == U_ZERO_ERROR);
+    // Never try to load Icu data from files.
+    udata_setFileAccess(UDATA_ONLY_PACKAGES, &err);
+    DCHECK(err == U_ZERO_ERROR);
+  }
+}
+
 }  // namespace
 
 MachineLearningServiceImpl::MachineLearningServiceImpl(
     mojo::ScopedMessagePipeHandle pipe,
     base::Closure disconnect_handler,
     const std::string& model_dir)
-    : icu_data_(nullptr),
-      builtin_model_metadata_(GetBuiltinModelMetadata()),
+    : builtin_model_metadata_(GetBuiltinModelMetadata()),
       model_dir_(model_dir),
       receiver_(this,
                 mojo::InterfaceRequest<
@@ -349,22 +374,4 @@
   request_metrics.RecordRequestEvent(LoadModelResult::OK);
 }
 
-void MachineLearningServiceImpl::InitIcuIfNeeded() {
-  if (icu_data_ == nullptr) {
-    // Need to load the data file again.
-    int64_t file_size;
-    const base::FilePath icu_data_file_path(kIcuDataFilePath);
-    CHECK(base::GetFileSize(icu_data_file_path, &file_size));
-    icu_data_ = new char[file_size];
-    CHECK(base::ReadFile(icu_data_file_path, icu_data_,
-                         static_cast<int>(file_size)) == file_size);
-    // Init the Icu library.
-    UErrorCode err = U_ZERO_ERROR;
-    udata_setCommonData(reinterpret_cast<void*>(icu_data_), &err);
-    DCHECK(err == U_ZERO_ERROR);
-    // Never try to load Icu data from files.
-    udata_setFileAccess(UDATA_ONLY_PACKAGES, &err);
-  }
-}
-
 }  // namespace ml