diff --git a/src/common/BUILD.gn b/src/common/BUILD.gn index 1e318012d1..698b26c9ed 100644 --- a/src/common/BUILD.gn +++ b/src/common/BUILD.gn @@ -157,11 +157,13 @@ if (is_win || is_linux || is_chromeos || is_mac || is_fuchsia || is_android) { "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 8ea48e0bdf..86812af420 100644 --- a/src/common/CMakeLists.txt +++ b/src/common/CMakeLists.txt @@ -19,11 +19,13 @@ target_sources(dawn_common PRIVATE "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 0000000000..a4a637b689 --- /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 + +template +struct CoreFoundationRefTraits { + static constexpr T kNullValue = nullptr; + static void Reference(T value) { + CFRetain(value); + } + static void Release(T value) { + CFRelease(value); + } +}; + +template +class CFRef : public RefBase> { + public: + using RefBase>::RefBase; +}; + +template +CFRef AcquireCFRef(T pointee) { + CFRef 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 0000000000..cba037665a --- /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 + +template +struct IOKitRefTraits { + static constexpr T kNullValue = IO_OBJECT_NULL; + static void Reference(T value) { + IOObjectRetain(value); + } + static void Release(T value) { + IOObjectRelease(value); + } +}; + +template +class IORef : public RefBase> { + public: + using RefBase>::RefBase; +}; + +template +IORef AcquireIORef(T pointee) { + IORef ref; + ref.Acquire(pointee); + return ref; +} + +#endif // COMMON_IOKITREF_H_ diff --git a/src/common/RefBase.h b/src/common/RefBase.h index 508b424269..a1db6a973e 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 @@ -163,6 +164,11 @@ class RefBase { mValue = value; } + T* InitializeInto() DAWN_NO_DISCARD { + ASSERT(mValue == kNullValue); + return &mValue; + } + private: // Friend is needed so that instances of RefBase can call Reference and Release on // RefBase. diff --git a/src/dawn_native/metal/BackendMTL.mm b/src/dawn_native/metal/BackendMTL.mm index f7b17423f9..ada2c6d681 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 +# include "common/IOKitRef.h" #endif namespace dawn_native { namespace metal { @@ -72,18 +74,17 @@ namespace dawn_native { namespace metal { // Recursively search registry entry and its parents for property name // The data should release with CFRelease - CFDataRef data = static_cast(IORegistryEntrySearchCFProperty( - entry, kIOServicePlane, name, kCFAllocatorDefault, - kIORegistryIterateRecursively | kIORegistryIterateParents)); + CFRef data = + AcquireCFRef(static_cast(IORegistryEntrySearchCFProperty( + entry, kIOServicePlane, name, kCFAllocatorDefault, + kIORegistryIterateRecursively | kIORegistryIterateParents))); if (data == nullptr) { return value; } // CFDataGetBytePtr() is guaranteed to return a read-only pointer - value = *reinterpret_cast(CFDataGetBytePtr(data)); - - CFRelease(data); + value = *reinterpret_cast(CFDataGetBytePtr(data.Get())); return value; } @@ -107,38 +108,35 @@ namespace dawn_native { namespace metal { MaybeError API_AVAILABLE(macos(10.13)) GetDeviceIORegistryPCIInfo(id device, PCIIDs* ids) { // Get a matching dictionary for the IOGraphicsAccelerator2 - CFMutableDictionaryRef matchingDict = IORegistryEntryIDMatching([device registryID]); + CFRef 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 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 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 5da311860f..236dd160fc 100644 --- a/src/tests/unittests/RefCountedTests.cpp +++ b/src/tests/unittests/RefCountedTests.cpp @@ -402,3 +402,17 @@ TEST(Ref, MoveAssignmentDerived) { 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 ref; + *ref.InitializeInto() = original; + EXPECT_EQ(original->GetRefCountForTesting(), 1u); + + ref = nullptr; + EXPECT_TRUE(deleted); +}