Use smart Refs for IOKit and CoreFoundation objects.

Bug: dawn:89
Change-Id: Idea634bcc65ab4ec017f4e4c2431e95915f533df
Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/32661
Commit-Queue: Corentin Wallez <cwallez@chromium.org>
Reviewed-by: Austin Eng <enga@chromium.org>
Reviewed-by: Stephen White <senorblanco@chromium.org>
diff --git a/src/common/BUILD.gn b/src/common/BUILD.gn
index 1e31801..698b26c 100644
--- a/src/common/BUILD.gn
+++ b/src/common/BUILD.gn
@@ -157,11 +157,13 @@
       "BitSetIterator.h",
       "Compiler.h",
       "Constants.h",
+      "CoreFoundationRef.h",
       "DynamicLib.cpp",
       "DynamicLib.h",
       "GPUInfo.cpp",
       "GPUInfo.h",
       "HashUtils.h",
+      "IOKitRef.h",
       "LinkedList.h",
       "Log.cpp",
       "Log.h",
diff --git a/src/common/CMakeLists.txt b/src/common/CMakeLists.txt
index 8ea48e0..86812af 100644
--- a/src/common/CMakeLists.txt
+++ b/src/common/CMakeLists.txt
@@ -19,11 +19,13 @@
     "BitSetIterator.h"
     "Compiler.h"
     "Constants.h"
+    "CoreFoundationRef.h"
     "DynamicLib.cpp"
     "DynamicLib.h"
     "GPUInfo.cpp"
     "GPUInfo.h"
     "HashUtils.h"
+    "IOKitRef.h"
     "LinkedList.h"
     "Log.cpp"
     "Log.h"
diff --git a/src/common/CoreFoundationRef.h b/src/common/CoreFoundationRef.h
new file mode 100644
index 0000000..a4a637b
--- /dev/null
+++ b/src/common/CoreFoundationRef.h
@@ -0,0 +1,46 @@
+// Copyright 2020 The Dawn Authors
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef COMMON_COREFOUNDATIONREF_H_
+#define COMMON_COREFOUNDATIONREF_H_
+
+#include "common/RefBase.h"
+
+#include <CoreFoundation/CoreFoundation.h>
+
+template <typename T>
+struct CoreFoundationRefTraits {
+    static constexpr T kNullValue = nullptr;
+    static void Reference(T value) {
+        CFRetain(value);
+    }
+    static void Release(T value) {
+        CFRelease(value);
+    }
+};
+
+template <typename T>
+class CFRef : public RefBase<T, CoreFoundationRefTraits<T>> {
+  public:
+    using RefBase<T, CoreFoundationRefTraits<T>>::RefBase;
+};
+
+template <typename T>
+CFRef<T> AcquireCFRef(T pointee) {
+    CFRef<T> ref;
+    ref.Acquire(pointee);
+    return ref;
+}
+
+#endif  // COMMON_COREFOUNDATIONREF_H_
diff --git a/src/common/IOKitRef.h b/src/common/IOKitRef.h
new file mode 100644
index 0000000..cba0376
--- /dev/null
+++ b/src/common/IOKitRef.h
@@ -0,0 +1,46 @@
+// Copyright 2020 The Dawn Authors
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef COMMON_IOKITREF_H_
+#define COMMON_IOKITREF_H_
+
+#include "common/RefBase.h"
+
+#include <IOKit/IOKitLib.h>
+
+template <typename T>
+struct IOKitRefTraits {
+    static constexpr T kNullValue = IO_OBJECT_NULL;
+    static void Reference(T value) {
+        IOObjectRetain(value);
+    }
+    static void Release(T value) {
+        IOObjectRelease(value);
+    }
+};
+
+template <typename T>
+class IORef : public RefBase<T, IOKitRefTraits<T>> {
+  public:
+    using RefBase<T, IOKitRefTraits<T>>::RefBase;
+};
+
+template <typename T>
+IORef<T> AcquireIORef(T pointee) {
+    IORef<T> ref;
+    ref.Acquire(pointee);
+    return ref;
+}
+
+#endif  // COMMON_IOKITREF_H_
diff --git a/src/common/RefBase.h b/src/common/RefBase.h
index 508b424..a1db6a9 100644
--- a/src/common/RefBase.h
+++ b/src/common/RefBase.h
@@ -15,6 +15,7 @@
 #ifndef COMMON_REFBASE_H_
 #define COMMON_REFBASE_H_
 
+#include "common/Assert.h"
 #include "common/Compiler.h"
 
 #include <type_traits>
@@ -163,6 +164,11 @@
         mValue = value;
     }
 
+    T* InitializeInto() DAWN_NO_DISCARD {
+        ASSERT(mValue == kNullValue);
+        return &mValue;
+    }
+
   private:
     // Friend is needed so that instances of RefBase<U> can call Reference and Release on
     // RefBase<T>.
diff --git a/src/dawn_native/metal/BackendMTL.mm b/src/dawn_native/metal/BackendMTL.mm
index f7b1742..ada2c6d 100644
--- a/src/dawn_native/metal/BackendMTL.mm
+++ b/src/dawn_native/metal/BackendMTL.mm
@@ -14,6 +14,7 @@
 
 #include "dawn_native/metal/BackendMTL.h"
 
+#include "common/CoreFoundationRef.h"
 #include "common/GPUInfo.h"
 #include "common/NSRef.h"
 #include "common/Platform.h"
@@ -23,6 +24,7 @@
 
 #if defined(DAWN_PLATFORM_MACOS)
 #    import <IOKit/IOKitLib.h>
+#    include "common/IOKitRef.h"
 #endif
 
 namespace dawn_native { namespace metal {
@@ -72,18 +74,17 @@
 
             // Recursively search registry entry and its parents for property name
             // The data should release with CFRelease
-            CFDataRef data = static_cast<CFDataRef>(IORegistryEntrySearchCFProperty(
-                entry, kIOServicePlane, name, kCFAllocatorDefault,
-                kIORegistryIterateRecursively | kIORegistryIterateParents));
+            CFRef<CFDataRef> data =
+                AcquireCFRef(static_cast<CFDataRef>(IORegistryEntrySearchCFProperty(
+                    entry, kIOServicePlane, name, kCFAllocatorDefault,
+                    kIORegistryIterateRecursively | kIORegistryIterateParents)));
 
             if (data == nullptr) {
                 return value;
             }
 
             // CFDataGetBytePtr() is guaranteed to return a read-only pointer
-            value = *reinterpret_cast<const uint32_t*>(CFDataGetBytePtr(data));
-
-            CFRelease(data);
+            value = *reinterpret_cast<const uint32_t*>(CFDataGetBytePtr(data.Get()));
             return value;
         }
 
@@ -107,38 +108,35 @@
         MaybeError API_AVAILABLE(macos(10.13))
             GetDeviceIORegistryPCIInfo(id<MTLDevice> device, PCIIDs* ids) {
             // Get a matching dictionary for the IOGraphicsAccelerator2
-            CFMutableDictionaryRef matchingDict = IORegistryEntryIDMatching([device registryID]);
+            CFRef<CFMutableDictionaryRef> matchingDict =
+                AcquireCFRef(IORegistryEntryIDMatching([device registryID]));
             if (matchingDict == nullptr) {
                 return DAWN_INTERNAL_ERROR("Failed to create the matching dict for the device");
             }
 
             // IOServiceGetMatchingService will consume the reference on the matching dictionary,
             // so we don't need to release the dictionary.
-            io_registry_entry_t acceleratorEntry =
-                IOServiceGetMatchingService(kIOMasterPortDefault, matchingDict);
+            IORef<io_registry_entry_t> acceleratorEntry = AcquireIORef(
+                IOServiceGetMatchingService(kIOMasterPortDefault, matchingDict.Detach()));
             if (acceleratorEntry == IO_OBJECT_NULL) {
                 return DAWN_INTERNAL_ERROR(
                     "Failed to get the IO registry entry for the accelerator");
             }
 
             // Get the parent entry that will be the IOPCIDevice
-            io_registry_entry_t deviceEntry = IO_OBJECT_NULL;
-            if (IORegistryEntryGetParentEntry(acceleratorEntry, kIOServicePlane, &deviceEntry) !=
-                kIOReturnSuccess) {
-                IOObjectRelease(acceleratorEntry);
+            IORef<io_registry_entry_t> deviceEntry;
+            if (IORegistryEntryGetParentEntry(acceleratorEntry.Get(), kIOServicePlane,
+                                              deviceEntry.InitializeInto()) != kIOReturnSuccess) {
                 return DAWN_INTERNAL_ERROR("Failed to get the IO registry entry for the device");
             }
 
             ASSERT(deviceEntry != IO_OBJECT_NULL);
-            IOObjectRelease(acceleratorEntry);
 
-            uint32_t vendorId = GetEntryProperty(deviceEntry, CFSTR("vendor-id"));
-            uint32_t deviceId = GetEntryProperty(deviceEntry, CFSTR("device-id"));
+            uint32_t vendorId = GetEntryProperty(deviceEntry.Get(), CFSTR("vendor-id"));
+            uint32_t deviceId = GetEntryProperty(deviceEntry.Get(), CFSTR("device-id"));
 
             *ids = PCIIDs{vendorId, deviceId};
 
-            IOObjectRelease(deviceEntry);
-
             return {};
         }
 
diff --git a/src/tests/unittests/RefCountedTests.cpp b/src/tests/unittests/RefCountedTests.cpp
index 5da3118..236dd16 100644
--- a/src/tests/unittests/RefCountedTests.cpp
+++ b/src/tests/unittests/RefCountedTests.cpp
@@ -402,3 +402,17 @@
     destination = nullptr;
     EXPECT_TRUE(deleted);
 }
+
+// Test Ref's InitializeInto.
+TEST(Ref, InitializeInto) {
+    bool deleted = false;
+    RCTest* original = new RCTest(&deleted);
+
+    // InitializeInto acquires the ref.
+    Ref<RCTest> ref;
+    *ref.InitializeInto() = original;
+    EXPECT_EQ(original->GetRefCountForTesting(), 1u);
+
+    ref = nullptr;
+    EXPECT_TRUE(deleted);
+}