[Fuchsia] Implement thread priorities. (#40983)

Implement thread priorities using the Fuchsia media profile provider API
to enable system / product configuration of host threads.

Bug: b/275114261

This is a CP of https://github.com/flutter/engine/pull/40970 with a
couple of changes to .cmx files that no longer exist on master.
diff --git a/shell/platform/fuchsia/flutter/BUILD.gn b/shell/platform/fuchsia/flutter/BUILD.gn
index 13e5526..a192944 100644
--- a/shell/platform/fuchsia/flutter/BUILD.gn
+++ b/shell/platform/fuchsia/flutter/BUILD.gn
@@ -158,6 +158,7 @@
              "$fuchsia_sdk_root/fidl:fuchsia.images",
              "$fuchsia_sdk_root/fidl:fuchsia.intl",
              "$fuchsia_sdk_root/fidl:fuchsia.io",
+             "$fuchsia_sdk_root/fidl:fuchsia.media",
              "$fuchsia_sdk_root/fidl:fuchsia.memorypressure",
              "$fuchsia_sdk_root/fidl:fuchsia.sys",
              "$fuchsia_sdk_root/fidl:fuchsia.ui.app",
diff --git a/shell/platform/fuchsia/flutter/engine.cc b/shell/platform/fuchsia/flutter/engine.cc
index d623804..badb01e 100644
--- a/shell/platform/fuchsia/flutter/engine.cc
+++ b/shell/platform/fuchsia/flutter/engine.cc
@@ -5,8 +5,11 @@
 #include "engine.h"
 
 #include <fuchsia/accessibility/semantics/cpp/fidl.h>
+#include <fuchsia/media/cpp/fidl.h>
 #include <fuchsia/ui/scenic/cpp/fidl.h>
 #include <lib/async/cpp/task.h>
+#include <lib/zx/thread.h>
+#include <zircon/rights.h>
 #include <zircon/status.h>
 #include <zircon/types.h>
 #include <memory>
@@ -21,6 +24,7 @@
 #include "flutter/shell/common/rasterizer.h"
 #include "flutter/shell/common/run_configuration.h"
 #include "flutter/shell/common/serialization_callbacks.h"
+#include "flutter/shell/common/thread_host.h"
 #include "third_party/skia/include/core/SkPicture.h"
 #include "third_party/skia/include/core/SkSerialProcs.h"
 #include "third_party/skia/include/ports/SkFontMgr_fuchsia.h"
@@ -54,14 +58,145 @@
       nullptr);
 }
 
+//
+// Fuchsia scheduler role naming scheme employed here:
+//
+// Roles based on thread function:
+//   <prefix>.type.{platform,ui,raster,io,profiler}
+//
+// Roles based on fml::Thread::ThreadPriority:
+//   <prefix>.thread.{background,display,raster,normal}
+//
+
+void SetThreadRole(
+    const fuchsia::media::ProfileProviderSyncPtr& profile_provider,
+    const std::string& role) {
+  ZX_ASSERT(profile_provider);
+
+  zx::thread dup;
+  const zx_status_t dup_status =
+      zx::thread::self()->duplicate(ZX_RIGHT_SAME_RIGHTS, &dup);
+  if (dup_status != ZX_OK) {
+    FML_LOG(WARNING)
+        << "Failed to duplicate thread handle when setting thread config: "
+        << zx_status_get_string(dup_status)
+        << ". Thread will run at default priority.";
+    return;
+  }
+
+  int64_t unused_period;
+  int64_t unused_capacity;
+  const zx_status_t status = profile_provider->RegisterHandlerWithCapacity(
+      std::move(dup), role, 0, 0.f, &unused_period, &unused_capacity);
+  if (status != ZX_OK) {
+    FML_LOG(WARNING) << "Failed to set thread role to \"" << role
+                     << "\": " << zx_status_get_string(status)
+                     << ". Thread will run at default priority.";
+    return;
+  }
+}
+
+void SetThreadConfig(
+    const std::string& name_prefix,
+    const fuchsia::media::ProfileProviderSyncPtr& profile_provider,
+    const fml::Thread::ThreadConfig& config) {
+  ZX_ASSERT(profile_provider);
+
+  fml::Thread::SetCurrentThreadName(config);
+
+  // Derive the role name from the prefix and priority. See comment above about
+  // the role naming scheme.
+  std::string role;
+  switch (config.priority) {
+    case fml::Thread::ThreadPriority::BACKGROUND:
+      role = name_prefix + ".thread.background";
+      break;
+    case fml::Thread::ThreadPriority::DISPLAY:
+      role = name_prefix + ".thread.display";
+      break;
+    case fml::Thread::ThreadPriority::RASTER:
+      role = name_prefix + ".thread.raster";
+      break;
+    case fml::Thread::ThreadPriority::NORMAL:
+      role = name_prefix + ".thread.normal";
+      break;
+    default:
+      FML_LOG(WARNING) << "Unknown thread priority "
+                       << static_cast<int>(config.priority)
+                       << ". Thread will run at default priority.";
+      return;
+  }
+  ZX_ASSERT(!role.empty());
+
+  SetThreadRole(profile_provider, role);
+}
+
 }  // namespace
 
-flutter::ThreadHost Engine::CreateThreadHost(const std::string& name_prefix) {
+flutter::ThreadHost Engine::CreateThreadHost(
+    const std::string& name_prefix,
+    const std::shared_ptr<sys::ServiceDirectory>& services) {
   fml::Thread::SetCurrentThreadName(
       fml::Thread::ThreadConfig(name_prefix + ".platform"));
-  return flutter::ThreadHost(name_prefix, flutter::ThreadHost::Type::RASTER |
-                                              flutter::ThreadHost::Type::UI |
-                                              flutter::ThreadHost::Type::IO);
+
+  // Default the config setter to setup the thread name only.
+  flutter::ThreadConfigSetter config_setter = fml::Thread::SetCurrentThreadName;
+
+  // Override the config setter if the media profile provider is available.
+  if (services) {
+    // Connect to the media profile provider to assign thread priorities using
+    // Fuchsia's scheduler role API. Failure to connect will print a warning and
+    // proceed with engine initialization, leaving threads created by the engine
+    // at default priority.
+    //
+    // The use of std::shared_ptr here is to work around the unfortunate
+    // requirement for flutter::ThreadConfigSetter (i.e. std::function<>) that
+    // the target callable be copy-constructible. This awkwardly conflicts with
+    // fuchsia::media::ProfileProviderSyncPtr being move-only. std::shared_ptr
+    // provides copyable object that references the move-only SyncPtr.
+    std::shared_ptr<fuchsia::media::ProfileProviderSyncPtr>
+        media_profile_provider =
+            std::make_shared<fuchsia::media::ProfileProviderSyncPtr>();
+
+    const zx_status_t connect_status =
+        services->Connect(media_profile_provider->NewRequest());
+    if (connect_status != ZX_OK) {
+      FML_LOG(WARNING)
+          << "Failed to connect to " << fuchsia::media::ProfileProvider::Name_
+          << ": " << zx_status_get_string(connect_status)
+          << " This is not a fatal error, but threads created by the engine "
+             "will run at default priority, regardless of the requested "
+             "priority.";
+    } else {
+      // Set the role for (this) platform thread. See comment above about the
+      // role naming scheme.
+      SetThreadRole(*media_profile_provider, name_prefix + ".type.platform");
+
+      // This lambda must be copyable or the assignment fails to compile,
+      // necessitating the use of std::shared_ptr for the profile provider.
+      config_setter = [name_prefix, media_profile_provider](
+                          const fml::Thread::ThreadConfig& config) {
+        SetThreadConfig(name_prefix, *media_profile_provider, config);
+      };
+    }
+  }
+
+  flutter::ThreadHost::ThreadHostConfig thread_host_config{config_setter};
+
+  thread_host_config.SetRasterConfig(
+      {flutter::ThreadHost::ThreadHostConfig::MakeThreadName(
+           flutter::ThreadHost::Type::RASTER, name_prefix),
+       fml::Thread::ThreadPriority::RASTER});
+  thread_host_config.SetUIConfig(
+      {flutter::ThreadHost::ThreadHostConfig::MakeThreadName(
+           flutter::ThreadHost::Type::UI, name_prefix),
+       fml::Thread::ThreadPriority::DISPLAY});
+  thread_host_config.SetIOConfig(
+      {flutter::ThreadHost::ThreadHostConfig::MakeThreadName(
+           flutter::ThreadHost::Type::IO, name_prefix),
+       fml::Thread::ThreadPriority::NORMAL});
+
+  return flutter::ThreadHost(thread_host_config);
 }
 
 Engine::Engine(Delegate& delegate,
@@ -78,7 +213,7 @@
                bool for_v1_component)
     : delegate_(delegate),
       thread_label_(std::move(thread_label)),
-      thread_host_(CreateThreadHost(thread_label_)),
+      thread_host_(CreateThreadHost(thread_label_, runner_services)),
       view_token_(std::move(view_token)),
       memory_pressure_watcher_binding_(this),
       latest_memory_pressure_level_(fuchsia::memorypressure::Level::NORMAL),
@@ -104,7 +239,7 @@
                bool for_v1_component)
     : delegate_(delegate),
       thread_label_(std::move(thread_label)),
-      thread_host_(CreateThreadHost(thread_label_)),
+      thread_host_(CreateThreadHost(thread_label_, runner_services)),
       view_creation_token_(std::move(view_creation_token)),
       memory_pressure_watcher_binding_(this),
       latest_memory_pressure_level_(fuchsia::memorypressure::Level::NORMAL),
diff --git a/shell/platform/fuchsia/flutter/engine.h b/shell/platform/fuchsia/flutter/engine.h
index e11c41e..cfcbe1e 100644
--- a/shell/platform/fuchsia/flutter/engine.h
+++ b/shell/platform/fuchsia/flutter/engine.h
@@ -48,7 +48,9 @@
     virtual void OnEngineTerminate(const Engine* holder) = 0;
   };
 
-  static flutter::ThreadHost CreateThreadHost(const std::string& name_prefix);
+  static flutter::ThreadHost CreateThreadHost(
+      const std::string& name_prefix,
+      const std::shared_ptr<sys::ServiceDirectory>& runner_services = nullptr);
 
   // Gfx connection ctor.
   Engine(Delegate& delegate,
diff --git a/shell/platform/fuchsia/flutter/meta/common.shard.cml b/shell/platform/fuchsia/flutter/meta/common.shard.cml
index b43a3da..1807334 100644
--- a/shell/platform/fuchsia/flutter/meta/common.shard.cml
+++ b/shell/platform/fuchsia/flutter/meta/common.shard.cml
@@ -31,6 +31,7 @@
                 "fuchsia.fonts.Provider",
                 "fuchsia.intl.PropertyProvider",
                 "fuchsia.logger.LogSink", // Copied from syslog/client.shard.cml.
+                "fuchsia.media.ProfileProvider",
                 "fuchsia.memorypressure.Provider",
                 "fuchsia.net.name.Lookup",
                 "fuchsia.posix.socket.Provider",
diff --git a/shell/platform/fuchsia/flutter/meta/flutter_aot_product_runner.cmx b/shell/platform/fuchsia/flutter/meta/flutter_aot_product_runner.cmx
index ec1e197..97092cb 100644
--- a/shell/platform/fuchsia/flutter/meta/flutter_aot_product_runner.cmx
+++ b/shell/platform/fuchsia/flutter/meta/flutter_aot_product_runner.cmx
@@ -16,6 +16,7 @@
             "fuchsia.fonts.Provider",
             "fuchsia.intl.PropertyProvider",
             "fuchsia.logger.LogSink",
+            "fuchsia.media.ProfileProvider",
             "fuchsia.memorypressure.Provider",
             "fuchsia.net.name.Lookup",
             "fuchsia.posix.socket.Provider",
diff --git a/shell/platform/fuchsia/flutter/meta/flutter_aot_runner.cmx b/shell/platform/fuchsia/flutter/meta/flutter_aot_runner.cmx
index ec1e197..97092cb 100644
--- a/shell/platform/fuchsia/flutter/meta/flutter_aot_runner.cmx
+++ b/shell/platform/fuchsia/flutter/meta/flutter_aot_runner.cmx
@@ -16,6 +16,7 @@
             "fuchsia.fonts.Provider",
             "fuchsia.intl.PropertyProvider",
             "fuchsia.logger.LogSink",
+            "fuchsia.media.ProfileProvider",
             "fuchsia.memorypressure.Provider",
             "fuchsia.net.name.Lookup",
             "fuchsia.posix.socket.Provider",
diff --git a/shell/platform/fuchsia/flutter/meta/flutter_jit_product_runner.cmx b/shell/platform/fuchsia/flutter/meta/flutter_jit_product_runner.cmx
index b0a626c..8397c5b 100644
--- a/shell/platform/fuchsia/flutter/meta/flutter_jit_product_runner.cmx
+++ b/shell/platform/fuchsia/flutter/meta/flutter_jit_product_runner.cmx
@@ -17,6 +17,7 @@
             "fuchsia.fonts.Provider",
             "fuchsia.intl.PropertyProvider",
             "fuchsia.logger.LogSink",
+            "fuchsia.media.ProfileProvider",
             "fuchsia.memorypressure.Provider",
             "fuchsia.net.name.Lookup",
             "fuchsia.posix.socket.Provider",
diff --git a/shell/platform/fuchsia/flutter/meta/flutter_jit_runner.cmx b/shell/platform/fuchsia/flutter/meta/flutter_jit_runner.cmx
index b0a626c..8397c5b 100644
--- a/shell/platform/fuchsia/flutter/meta/flutter_jit_runner.cmx
+++ b/shell/platform/fuchsia/flutter/meta/flutter_jit_runner.cmx
@@ -17,6 +17,7 @@
             "fuchsia.fonts.Provider",
             "fuchsia.intl.PropertyProvider",
             "fuchsia.logger.LogSink",
+            "fuchsia.media.ProfileProvider",
             "fuchsia.memorypressure.Provider",
             "fuchsia.net.name.Lookup",
             "fuchsia.posix.socket.Provider",