diff --git a/fml/platform/darwin/cf_utils.h b/fml/platform/darwin/cf_utils.h index 8a1773d3d4875..71bbb2bb8d35f 100644 --- a/fml/platform/darwin/cf_utils.h +++ b/fml/platform/darwin/cf_utils.h @@ -14,6 +14,7 @@ namespace fml { /// Default retain and release implementations for CFRef. template struct CFRefTraits { + static constexpr T kNullValue = nullptr; static void Retain(T instance) { CFRetain(instance); } static void Release(T instance) { CFRelease(instance); } }; @@ -26,7 +27,7 @@ template class CFRef { public: /// Creates a new null CFRef. - CFRef() : instance_(nullptr) {} + CFRef() : instance_(CFRefTraits::kNullValue) {} /// Takes over ownership of `instance`, which is expected to be already /// retained. @@ -43,7 +44,7 @@ class CFRef { /// Move ctor: Takes over ownership of the CoreFoundation object owned /// by `other`. The object owned by `other` is set to null. CFRef(CFRef&& other) : instance_(other.instance_) { - other.instance_ = nullptr; + other.instance_ = CFRefTraits::kNullValue; } /// Takes over ownership of the CoreFoundation object owned by `other`. @@ -57,14 +58,14 @@ class CFRef { if (instance_) { CFRefTraits::Release(instance_); } - instance_ = nullptr; + instance_ = CFRefTraits::kNullValue; } /// Takes over ownership of `instance`, null by default. The object is /// expected to be already retained if non-null. /// /// Releases the previous object, if non-null. - void Reset(T instance = nullptr) { + void Reset(T instance = CFRefTraits::kNullValue) { if (instance_) { CFRefTraits::Release(instance_); } @@ -73,7 +74,7 @@ class CFRef { /// Retains a shared copy of `instance`. The previous object is released if /// non-null. Has no effect if `instance` is the currently-held object. - void Retain(T instance = nullptr) { + void Retain(T instance = CFRefTraits::kNullValue) { if (instance_ == instance) { return; } @@ -88,7 +89,7 @@ class CFRef { /// with the object. [[nodiscard]] T Release() { auto instance = instance_; - instance_ = nullptr; + instance_ = CFRefTraits::kNullValue; return instance; } @@ -108,7 +109,9 @@ class CFRef { operator T() const { return instance_; } /// Returns true if the underlying CoreFoundation object is non-null. - explicit operator bool() const { return instance_ != nullptr; } + explicit operator bool() const { + return instance_ != CFRefTraits::kNullValue; + } private: T instance_; diff --git a/fml/platform/darwin/cf_utils_unittests.mm b/fml/platform/darwin/cf_utils_unittests.mm index 2128d72554d63..dba51e1c2a694 100644 --- a/fml/platform/darwin/cf_utils_unittests.mm +++ b/fml/platform/darwin/cf_utils_unittests.mm @@ -7,6 +7,20 @@ #include "flutter/testing/testing.h" namespace fml { + +// Template specialization used in CFTest.SupportsCustomRetainRelease. +template <> +struct CFRefTraits { + static bool retain_called; + static bool release_called; + + static constexpr int64_t kNullValue = 0; + static void Retain(int64_t instance) { retain_called = true; } + static void Release(int64_t instance) { release_called = true; } +}; +bool CFRefTraits::retain_called = false; +bool CFRefTraits::release_called = false; + namespace testing { TEST(CFTest, CanCreateRefs) { @@ -83,5 +97,20 @@ EXPECT_EQ(ref_count_before + 1u, ref_count_after); } +TEST(CFTest, SupportsCustomRetainRelease) { + CFRef ref(1); + ASSERT_EQ(ref.Get(), 1); + ASSERT_FALSE(CFRefTraits::retain_called); + ASSERT_FALSE(CFRefTraits::release_called); + ref.Reset(); + ASSERT_EQ(ref.Get(), 0); + ASSERT_TRUE(CFRefTraits::release_called); + ref.Retain(2); + ASSERT_EQ(ref.Get(), 2); + ASSERT_TRUE(CFRefTraits::retain_called); + CFRefTraits::retain_called = false; + CFRefTraits::release_called = false; +} + } // namespace testing } // namespace fml diff --git a/impeller/golden_tests/metal_screenshot.h b/impeller/golden_tests/metal_screenshot.h index 3e0cbb213522d..739c7691d238e 100644 --- a/impeller/golden_tests/metal_screenshot.h +++ b/impeller/golden_tests/metal_screenshot.h @@ -15,9 +15,10 @@ namespace fml { -/// Default retain and release implementations for CGImageRef. +/// fml::CFRef retain and release implementations for CGImageRef. template <> struct CFRefTraits { + static constexpr CGImageRef kNullValue = nullptr; static void Retain(CGImageRef instance) { CGImageRetain(instance); } static void Release(CGImageRef instance) { CGImageRelease(instance); } }; diff --git a/shell/platform/darwin/ios/framework/Source/profiler_metrics_ios.mm b/shell/platform/darwin/ios/framework/Source/profiler_metrics_ios.mm index ad97326d1116b..0a4ba99a0dd1d 100644 --- a/shell/platform/darwin/ios/framework/Source/profiler_metrics_ios.mm +++ b/shell/platform/darwin/ios/framework/Source/profiler_metrics_ios.mm @@ -6,6 +6,7 @@ #import +#import "flutter/fml/platform/darwin/cf_utils.h" #import "flutter/shell/platform/darwin/common/framework/Headers/FlutterMacros.h" #import "flutter/shell/platform/darwin/ios/framework/Source/IOKit.h" @@ -34,75 +35,42 @@ } // namespace -namespace flutter { -namespace { - #if FLUTTER_RUNTIME_MODE == FLUTTER_RUNTIME_MODE_DEBUG || \ FLUTTER_RUNTIME_MODE == FLUTTER_RUNTIME_MODE_PROFILE -template -T ClearValue() { - return nullptr; -} +namespace fml { +/// fml::CFRef retain and release implementations for io_object_t and related types. template <> -io_object_t ClearValue() { - return 0; -} +struct CFRefTraits { + static constexpr io_object_t kNullValue = 0; + static void Retain(io_object_t instance) { IOObjectRetain(instance); } + static void Release(io_object_t instance) { IOObjectRelease(instance); } +}; -template -/// Generic RAII wrapper like unique_ptr but gives access to its handle. -class Scoped { - public: - typedef void (*Deleter)(T); - explicit Scoped(Deleter deleter) : object_(ClearValue()), deleter_(deleter) {} - Scoped(T object, Deleter deleter) : object_(object), deleter_(deleter) {} - ~Scoped() { - if (object_) { - deleter_(object_); - } - } - T* handle() { - if (object_) { - deleter_(object_); - object_ = ClearValue(); - } - return &object_; - } - T get() { return object_; } - void reset(T new_value) { - if (object_) { - deleter_(object_); - } - object_ = new_value; - } +} // namespace fml - private: - FML_DISALLOW_COPY_ASSIGN_AND_MOVE(Scoped); - T object_; - Deleter deleter_; -}; +#endif -void DeleteCF(CFMutableDictionaryRef value) { - CFRelease(value); -} +namespace flutter { +namespace { -void DeleteIO(io_object_t value) { - IOObjectRelease(value); -} +#if FLUTTER_RUNTIME_MODE == FLUTTER_RUNTIME_MODE_DEBUG || \ + FLUTTER_RUNTIME_MODE == FLUTTER_RUNTIME_MODE_PROFILE std::optional FindGpuUsageInfo(io_iterator_t iterator) { - for (Scoped regEntry(IOIteratorNext(iterator), DeleteIO); regEntry.get(); - regEntry.reset(IOIteratorNext(iterator))) { - Scoped serviceDictionary(DeleteCF); - if (IORegistryEntryCreateCFProperties(regEntry.get(), serviceDictionary.handle(), + for (fml::CFRef reg_entry(IOIteratorNext(iterator)); reg_entry.Get(); + reg_entry.Reset(IOIteratorNext(iterator))) { + CFMutableDictionaryRef cf_service_dictionary; + if (IORegistryEntryCreateCFProperties(reg_entry.Get(), &cf_service_dictionary, kCFAllocatorDefault, kNilOptions) != kIOReturnSuccess) { continue; } - - NSDictionary* dictionary = - ((__bridge NSDictionary*)serviceDictionary.get())[@"PerformanceStatistics"]; - NSNumber* utilization = dictionary[@"Device Utilization %"]; + // Transfer ownership to ARC-managed pointer. + NSDictionary* service_dictionary = (__bridge_transfer NSDictionary*)cf_service_dictionary; + cf_service_dictionary = nullptr; + NSDictionary* performanceStatistics = service_dictionary[@"PerformanceStatistics"]; + NSNumber* utilization = performanceStatistics[@"Device Utilization %"]; if (utilization) { return (GpuUsageInfo){.percent_usage = [utilization doubleValue]}; } @@ -111,24 +79,27 @@ void DeleteIO(io_object_t value) { } [[maybe_unused]] std::optional FindSimulatorGpuUsageInfo() { - Scoped iterator(DeleteIO); + io_iterator_t io_iterator; if (IOServiceGetMatchingServices(kIOMasterPortDefault, IOServiceNameMatching("IntelAccelerator"), - iterator.handle()) == kIOReturnSuccess) { - return FindGpuUsageInfo(iterator.get()); + &io_iterator) == kIOReturnSuccess) { + fml::CFRef iterator(io_iterator); + return FindGpuUsageInfo(iterator.Get()); } return std::nullopt; } [[maybe_unused]] std::optional FindDeviceGpuUsageInfo() { - Scoped iterator(DeleteIO); + io_iterator_t io_iterator; if (IOServiceGetMatchingServices(kIOMasterPortDefault, IOServiceNameMatching("sgx"), - iterator.handle()) == kIOReturnSuccess) { - for (Scoped regEntry(IOIteratorNext(iterator.get()), DeleteIO); - regEntry.get(); regEntry.reset(IOIteratorNext(iterator.get()))) { - Scoped innerIterator(DeleteIO); - if (IORegistryEntryGetChildIterator(regEntry.get(), kIOServicePlane, - innerIterator.handle()) == kIOReturnSuccess) { - std::optional result = FindGpuUsageInfo(innerIterator.get()); + &io_iterator) == kIOReturnSuccess) { + fml::CFRef iterator(io_iterator); + for (fml::CFRef reg_entry(IOIteratorNext(iterator.Get())); reg_entry.Get(); + reg_entry.Reset(IOIteratorNext(iterator.Get()))) { + io_iterator_t io_inner_iterator; + if (IORegistryEntryGetChildIterator(reg_entry.Get(), kIOServicePlane, &io_inner_iterator) == + kIOReturnSuccess) { + fml::CFRef inner_iterator(io_inner_iterator); + std::optional result = FindGpuUsageInfo(inner_iterator.Get()); if (result.has_value()) { return result; }