Factor SystemHandle out of SystemEventPrimitive

An OS-independent handle abstraction is useful for
importing/exporting fence/texture payloads as well.

Bug: dawn:1745
Change-Id: I40a3130f1034a7fa6ae15b1d6bd2845c09354416
Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/157764
Reviewed-by: Kai Ninomiya <kainino@chromium.org>
Commit-Queue: Austin Eng <enga@chromium.org>
Reviewed-by: Corentin Wallez <cwallez@chromium.org>
Kokoro: Kokoro <noreply+kokoro@google.com>
diff --git a/src/dawn/native/BUILD.gn b/src/dawn/native/BUILD.gn
index 86986a5..5485604 100644
--- a/src/dawn/native/BUILD.gn
+++ b/src/dawn/native/BUILD.gn
@@ -367,6 +367,8 @@
     "SwapChain.h",
     "SystemEvent.cpp",
     "SystemEvent.h",
+    "SystemHandle.cpp",
+    "SystemHandle.h",
     "Texture.cpp",
     "Texture.h",
     "TintUtils.cpp",
diff --git a/src/dawn/native/CMakeLists.txt b/src/dawn/native/CMakeLists.txt
index 190c1ee..3b5f1d8 100644
--- a/src/dawn/native/CMakeLists.txt
+++ b/src/dawn/native/CMakeLists.txt
@@ -157,6 +157,8 @@
     "Limits.h"
     "SystemEvent.cpp"
     "SystemEvent.h"
+    "SystemHandle.cpp"
+    "SystemHandle.h"
     "ObjectBase.cpp"
     "ObjectBase.h"
     "PassResourceUsage.cpp"
diff --git a/src/dawn/native/Error.h b/src/dawn/native/Error.h
index fe48ea9..3e95a13 100644
--- a/src/dawn/native/Error.h
+++ b/src/dawn/native/Error.h
@@ -135,6 +135,13 @@
 #define DAWN_FORMAT_INTERNAL_ERROR(...) \
     DAWN_MAKE_ERROR(InternalErrorType::Internal, absl::StrFormat(__VA_ARGS__))
 
+#define DAWN_INTERNAL_ERROR_IF(EXPR, ...)                                                  \
+    if (DAWN_UNLIKELY(EXPR)) {                                                             \
+        return DAWN_MAKE_ERROR(InternalErrorType::Internal, absl::StrFormat(__VA_ARGS__)); \
+    }                                                                                      \
+    for (;;)                                                                               \
+    break
+
 #define DAWN_UNIMPLEMENTED_ERROR(MESSAGE) \
     DAWN_MAKE_ERROR(InternalErrorType::Internal, std::string("Unimplemented: ") + MESSAGE)
 
diff --git a/src/dawn/native/SystemEvent.cpp b/src/dawn/native/SystemEvent.cpp
index 1113fc6..44ac664 100644
--- a/src/dawn/native/SystemEvent.cpp
+++ b/src/dawn/native/SystemEvent.cpp
@@ -65,78 +65,8 @@
 #define ToMilliseconds ToMillisecondsGeneric<int, -1>
 #endif
 
-#if DAWN_PLATFORM_IS(WINDOWS)
-HANDLE AsHANDLE(const SystemEventPrimitive& p) {
-    return reinterpret_cast<HANDLE>(p.value);
-}
-#elif DAWN_PLATFORM_IS(POSIX)
-int AsFD(const SystemEventPrimitive& p) {
-    DAWN_ASSERT(p.value <= std::numeric_limits<int>::max());
-    return p.value;
-}
-#endif
-
 }  // namespace
 
-// SystemEventPrimitive
-
-SystemEventPrimitive::SystemEventPrimitive(void* win32Handle)
-    : value(reinterpret_cast<uintptr_t>(win32Handle)) {
-#if DAWN_PLATFORM_IS(WINDOWS)
-    static_assert(std::is_same_v<void*, HANDLE>);
-    DAWN_ASSERT(win32Handle != nullptr);
-#else
-    DAWN_ASSERT(false);  // Wrong platform.
-#endif
-}
-
-SystemEventPrimitive::SystemEventPrimitive(int posixFd) : value(posixFd) {
-#if DAWN_PLATFORM_IS(POSIX)
-    static_assert(sizeof(uintptr_t) >= sizeof(int));
-    DAWN_ASSERT(posixFd > 0);
-#else
-    DAWN_ASSERT(false);  // Wrong platform.
-#endif
-}
-
-SystemEventPrimitive::~SystemEventPrimitive() {
-    if (IsValid()) {
-        Close();
-    }
-}
-
-SystemEventPrimitive::SystemEventPrimitive(SystemEventPrimitive&& rhs) {
-    *this = std::move(rhs);
-}
-
-SystemEventPrimitive& SystemEventPrimitive::operator=(SystemEventPrimitive&& rhs) {
-    if (this != &rhs) {
-        if (IsValid()) {
-            Close();
-        }
-        std::swap(value, rhs.value);
-    }
-    return *this;
-}
-
-bool SystemEventPrimitive::IsValid() const {
-    return value != kInvalid;
-}
-
-void SystemEventPrimitive::Close() {
-    DAWN_ASSERT(IsValid());
-
-#if DAWN_PLATFORM_IS(WINDOWS)
-    CloseHandle(AsHANDLE(*this));
-#elif DAWN_PLATFORM_IS(POSIX)
-    close(AsFD(*this));
-#else
-    DAWN_CHECK(false);  // Not implemented.
-#endif
-
-    value = kInvalid;
-}
-
 // SystemEventReceiver
 
 SystemEventReceiver SystemEventReceiver::CreateAlreadySignaled() {
@@ -163,7 +93,7 @@
 #elif DAWN_PLATFORM_IS(POSIX)
     // Send one byte to signal the receiver
     char zero[1] = {0};
-    int status = write(AsFD(mPrimitive), zero, 1);
+    int status = write(mPrimitive.Get(), zero, 1);
     DAWN_CHECK(status >= 0);
 #else
     // Not implemented for this platform.
@@ -182,7 +112,7 @@
 #elif DAWN_PLATFORM_IS(POSIX)
     std::vector<pollfd> pollfds(count);
     for (size_t i = 0; i < count; ++i) {
-        int fd = AsFD(futures[i].event->GetReceiver().mPrimitive);
+        int fd = futures[i].event->GetReceiver().mPrimitive.Get();
         pollfds[i] = pollfd{fd, POLLIN, 0};
     }
 
@@ -220,10 +150,10 @@
     DAWN_CHECK(status >= 0);
 
     SystemEventReceiver receiver;
-    receiver.mPrimitive = SystemEventPrimitive{pipeFds[0]};
+    receiver.mPrimitive = SystemHandle::Acquire(pipeFds[0]);
 
     SystemEventPipeSender sender;
-    sender.mPrimitive = SystemEventPrimitive{pipeFds[1]};
+    sender.mPrimitive = SystemHandle::Acquire(pipeFds[1]);
 
     return std::make_pair(std::move(sender), std::move(receiver));
 #else
diff --git a/src/dawn/native/SystemEvent.h b/src/dawn/native/SystemEvent.h
index 57bda22..cbd9be9 100644
--- a/src/dawn/native/SystemEvent.h
+++ b/src/dawn/native/SystemEvent.h
@@ -33,34 +33,13 @@
 #include "dawn/common/NonCopyable.h"
 #include "dawn/common/Platform.h"
 #include "dawn/native/IntegerTypes.h"
+#include "dawn/native/SystemHandle.h"
 
 namespace dawn::native {
 
 struct TrackedFutureWaitInfo;
 class SystemEventPipeSender;
 
-// Either a Win32 HANDLE or a POSIX fd (int) depending on OS, represented as a uintptr_t with
-// necessary conversions.
-class SystemEventPrimitive : NonCopyable {
-  public:
-    SystemEventPrimitive() = default;
-    // void* is the typedef of HANDLE in Win32.
-    explicit SystemEventPrimitive(void* win32Handle);
-    explicit SystemEventPrimitive(int posixFd);
-    ~SystemEventPrimitive();
-
-    SystemEventPrimitive(SystemEventPrimitive&&);
-    SystemEventPrimitive& operator=(SystemEventPrimitive&&);
-
-    bool IsValid() const;
-    void Close();
-
-    static constexpr uintptr_t kInvalid = 0;
-    // The underlying primitive, either a Win32 HANDLE (void*) or a POSIX fd (int), cast to
-    // uintptr_t. We treat 0 as the "invalid" value, even for POSIX.
-    uintptr_t value = kInvalid;
-};
-
 // SystemEventReceiver holds an OS event primitive (Win32 Event Object or POSIX file descriptor (fd)
 // that will be signalled by some other thing: either an OS integration like SetEventOnCompletion(),
 // or our own code like SystemEventPipeSender.
@@ -80,7 +59,7 @@
   private:
     friend bool WaitAnySystemEvent(size_t, TrackedFutureWaitInfo*, Nanoseconds);
     friend std::pair<SystemEventPipeSender, SystemEventReceiver> CreateSystemEventPipe();
-    SystemEventPrimitive mPrimitive;
+    SystemHandle mPrimitive;
 };
 
 // See CreateSystemEventPipe.
@@ -95,7 +74,7 @@
 
   private:
     friend std::pair<SystemEventPipeSender, SystemEventReceiver> CreateSystemEventPipe();
-    SystemEventPrimitive mPrimitive;
+    SystemHandle mPrimitive;
 };
 
 // Implementation of WaitAny when backed by SystemEventReceiver.
diff --git a/src/dawn/native/SystemHandle.cpp b/src/dawn/native/SystemHandle.cpp
new file mode 100644
index 0000000..9347c51
--- /dev/null
+++ b/src/dawn/native/SystemHandle.cpp
@@ -0,0 +1,164 @@
+// Copyright 2023 The Dawn & Tint Authors
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are met:
+//
+// 1. Redistributions of source code must retain the above copyright notice, this
+//    list of conditions and the following disclaimer.
+//
+// 2. Redistributions in binary form must reproduce the above copyright notice,
+//    this list of conditions and the following disclaimer in the documentation
+//    and/or other materials provided with the distribution.
+//
+// 3. Neither the name of the copyright holder nor the names of its
+//    contributors may be used to endorse or promote products derived from
+//    this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#include "src/dawn/native/SystemHandle.h"
+
+#include <utility>
+
+#include "dawn/common/Log.h"
+
+#if DAWN_PLATFORM_IS(WINDOWS)
+#include "dawn/common/windows_with_undefs.h"
+#elif DAWN_PLATFORM_IS(FUCHSIA)
+#include <zircon/syscalls.h>
+#elif DAWN_PLATFORM_IS(POSIX)
+#include <unistd.h>
+#endif
+
+namespace dawn::native {
+
+namespace {
+
+#if DAWN_PLATFORM_IS(WINDOWS)
+
+constexpr inline HANDLE kInvalidHandle = nullptr;
+
+inline bool IsHandleValid(HANDLE handle) {
+    return handle != nullptr;
+}
+
+inline ResultOrError<HANDLE> DuplicateHandle(HANDLE handle) {
+    HANDLE currentProcess = ::GetCurrentProcess();
+    HANDLE outHandle;
+    DAWN_INTERNAL_ERROR_IF(!::DuplicateHandle(currentProcess, handle, currentProcess, &outHandle, 0,
+                                              FALSE, DUPLICATE_SAME_ACCESS),
+                           "DuplicateHandle failed");
+    return outHandle;
+}
+
+inline MaybeError CloseHandle(HANDLE handle) {
+    DAWN_INTERNAL_ERROR_IF(!::CloseHandle(handle), "CloseHandle failed");
+    return {};
+}
+
+#elif DAWN_PLATFORM_IS(FUCHSIA)
+
+constexpr inline zx_handle_t kInvalidHandle = 0;
+
+inline bool IsHandleValid(zx_handle_t handle) {
+    return handle > 0;
+}
+
+inline ResultOrError<zx_handle_t> DuplicateHandle(zx_handle_t handle) {
+    zx_handle_t outHandle = ZX_HANDLE_INVALID;
+    zx_status_t status = DAWN_INTERNAL_ERROR_IF(
+        zx_handle_duplicate(descriptor->handle, ZX_RIGHT_SAME_RIGHTS, &outHandle) != ZX_OK,
+        "zx_handle_duplicate failed");
+    return outHandle;
+}
+
+inline bool CloseHandle(zx_handle_t handle) {
+    DAWN_INTERNAL_ERROR_IF(zx_handle_close(handle) != ZX_OK, "zx_handle_close failed");
+    return {};
+}
+
+#elif DAWN_PLATFORM_IS(POSIX)
+
+constexpr inline int kInvalidHandle = -1;
+
+inline bool IsHandleValid(int handle) {
+    return handle >= 0;
+}
+
+inline ResultOrError<int> DuplicateHandle(int handle) {
+    int outHandle = dup(handle);
+    DAWN_INTERNAL_ERROR_IF(outHandle < 0, "dup failed");
+    return outHandle;
+}
+
+inline MaybeError CloseHandle(int handle) {
+    DAWN_INTERNAL_ERROR_IF(close(handle) < 0, "close failed");
+    return {};
+}
+
+#endif
+
+}  // anonymous namespace
+
+SystemHandle::SystemHandle() : mHandle(kInvalidHandle) {}
+SystemHandle::SystemHandle(Handle handle) : mHandle(handle) {}
+
+bool SystemHandle::IsValid() const {
+    return IsHandleValid(mHandle);
+}
+
+SystemHandle::SystemHandle(SystemHandle&& rhs) {
+    mHandle = rhs.mHandle;
+    rhs.mHandle = kInvalidHandle;
+}
+
+SystemHandle& SystemHandle::operator=(SystemHandle&& rhs) {
+    if (this != &rhs) {
+        std::swap(mHandle, rhs.mHandle);
+    }
+    return *this;
+}
+
+SystemHandle::Handle SystemHandle::Get() const {
+    return mHandle;
+}
+
+SystemHandle::Handle SystemHandle::Detach() {
+    Handle handle = mHandle;
+    mHandle = kInvalidHandle;
+    return handle;
+}
+
+ResultOrError<SystemHandle> SystemHandle::Duplicate() const {
+    Handle handle;
+    DAWN_TRY_ASSIGN(handle, DuplicateHandle(mHandle));
+    return SystemHandle(handle);
+}
+
+void SystemHandle::Close() {
+    DAWN_ASSERT(IsValid());
+    auto result = CloseHandle(mHandle);
+    // Still invalidate the handle if Close failed.
+    // If Close failed, the handle surely was invalid already.
+    mHandle = kInvalidHandle;
+    if (DAWN_UNLIKELY(result.IsError())) {
+        dawn::ErrorLog() << result.AcquireError()->GetFormattedMessage();
+    }
+}
+
+SystemHandle::~SystemHandle() {
+    if (IsValid()) {
+        Close();
+    }
+}
+
+}  // namespace dawn::native
diff --git a/src/dawn/native/SystemHandle.h b/src/dawn/native/SystemHandle.h
new file mode 100644
index 0000000..b8647b6
--- /dev/null
+++ b/src/dawn/native/SystemHandle.h
@@ -0,0 +1,99 @@
+// Copyright 2023 The Dawn & Tint Authors
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are met:
+//
+// 1. Redistributions of source code must retain the above copyright notice, this
+//    list of conditions and the following disclaimer.
+//
+// 2. Redistributions in binary form must reproduce the above copyright notice,
+//    this list of conditions and the following disclaimer in the documentation
+//    and/or other materials provided with the distribution.
+//
+// 3. Neither the name of the copyright holder nor the names of its
+//    contributors may be used to endorse or promote products derived from
+//    this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#ifndef SRC_DAWN_NATIVE_SYSTEMHANDLE_H_
+#define SRC_DAWN_NATIVE_SYSTEMHANDLE_H_
+
+#include "dawn/common/NonCopyable.h"
+#include "dawn/common/Platform.h"
+#include "dawn/native/Error.h"
+
+namespace dawn::native {
+
+class SystemHandle : public NonCopyable {
+  public:
+    // Create an alias for the actual primitive handle type on a platform.
+#if DAWN_PLATFORM_IS(WINDOWS)
+    using Handle = void*;
+#elif DAWN_PLATFORM_IS(FUCHSIA)
+    using Handle = uint32_t;
+#elif DAWN_PLATFORM_IS(POSIX)
+    using Handle = int;
+#else
+#error "Platform not supported."
+#endif
+
+    SystemHandle();
+
+    // Create a SystemHandle by taking ownership of `handle`.
+    // Declaration is a template so that there is never implicit
+    // conversion of the handle arg.
+    template <typename Arg>
+    static SystemHandle Acquire(Arg handle) {
+        return SystemHandle(handle);
+    }
+
+    // Create a SystemHandle by duplicating `handle`.
+    // Declaration is a template so that there is never implicit
+    // conversion of the handle arg.
+    template <typename Arg>
+    static ResultOrError<SystemHandle> Duplicate(Arg handle) {
+        SystemHandle tmpOwnedHandle = SystemHandle::Acquire(handle);
+        SystemHandle dupHandle;
+        DAWN_TRY_ASSIGN(dupHandle, tmpOwnedHandle.Duplicate());
+        tmpOwnedHandle.Detach();
+        return dupHandle;
+    }
+
+    bool IsValid() const;
+
+    SystemHandle(SystemHandle&& rhs);
+    SystemHandle& operator=(SystemHandle&& rhs);
+
+    Handle Get() const;
+    Handle Detach();
+    ResultOrError<SystemHandle> Duplicate() const;
+    void Close();
+
+    ~SystemHandle();
+
+  private:
+    explicit SystemHandle(Handle handle);
+
+    // Constructor when the type does not match the Handle type on this platform.
+    template <typename Arg, typename = std::enable_if_t<!std::is_same_v<Arg, Handle>>>
+    explicit SystemHandle(Arg) : SystemHandle() {
+        static_assert(std::is_same_v<Arg, Handle>,
+                      "SystemHandle constucted from incorrect handle type.");
+    }
+
+    Handle mHandle;
+};
+
+}  // namespace dawn::native
+
+#endif  // SRC_DAWN_NATIVE_SYSTEMHANDLE_H_
diff --git a/src/dawn/native/d3d/Fence.cpp b/src/dawn/native/d3d/Fence.cpp
index 739407b..b9dfa90 100644
--- a/src/dawn/native/d3d/Fence.cpp
+++ b/src/dawn/native/d3d/Fence.cpp
@@ -35,14 +35,8 @@
 
 namespace dawn::native::d3d {
 
-Fence::Fence(UINT64 fenceValue, HANDLE sharedHandle)
-    : mFenceValue(fenceValue), mSharedHandle(sharedHandle) {}
-
-Fence::~Fence() {
-    if (mSharedHandle != nullptr) {
-        ::CloseHandle(mSharedHandle);
-    }
-}
+Fence::Fence(UINT64 fenceValue, SystemHandle sharedHandle)
+    : mFenceValue(fenceValue), mSharedHandle(std::move(sharedHandle)) {}
 
 UINT64 Fence::GetFenceValue() const {
     return mFenceValue;
diff --git a/src/dawn/native/d3d/Fence.h b/src/dawn/native/d3d/Fence.h
index 976410a..6278b10 100644
--- a/src/dawn/native/d3d/Fence.h
+++ b/src/dawn/native/d3d/Fence.h
@@ -31,6 +31,7 @@
 #include "dawn/common/RefCounted.h"
 #include "dawn/native/DawnNative.h"
 #include "dawn/native/Error.h"
+#include "dawn/native/SystemHandle.h"
 #include "dawn/native/d3d12/d3d12_platform.h"
 
 namespace dawn::native::d3d {
@@ -40,11 +41,10 @@
     UINT64 GetFenceValue() const;
 
   protected:
-    Fence(UINT64 fenceValue, HANDLE sharedHandle);
-    ~Fence() override;
+    Fence(UINT64 fenceValue, SystemHandle sharedHandle);
 
     const UINT64 mFenceValue;
-    const HANDLE mSharedHandle;
+    const SystemHandle mSharedHandle;
 };
 
 }  // namespace dawn::native::d3d
diff --git a/src/dawn/native/d3d/SharedFenceD3D.cpp b/src/dawn/native/d3d/SharedFenceD3D.cpp
index d00357c..d04d8a3 100644
--- a/src/dawn/native/d3d/SharedFenceD3D.cpp
+++ b/src/dawn/native/d3d/SharedFenceD3D.cpp
@@ -27,19 +27,15 @@
 
 #include "dawn/native/d3d/SharedFenceD3D.h"
 
+#include <utility>
+
 #include "dawn/native/ChainUtils.h"
 #include "dawn/native/d3d/DeviceD3D.h"
 
 namespace dawn::native::d3d {
 
-SharedFence::SharedFence(Device* device, const char* label, HANDLE ownedHandle)
-    : SharedFenceBase(device, label), mHandle(ownedHandle) {}
-
-SharedFence::~SharedFence() {
-    if (mHandle) {
-        ::CloseHandle(mHandle);
-    }
-}
+SharedFence::SharedFence(Device* device, const char* label, SystemHandle ownedHandle)
+    : SharedFenceBase(device, label), mHandle(std::move(ownedHandle)) {}
 
 MaybeError SharedFence::ExportInfoImpl(SharedFenceExportInfo* info) const {
     info->type = wgpu::SharedFenceType::DXGISharedHandle;
@@ -51,7 +47,7 @@
     FindInChain(info->nextInChain, &exportInfo);
 
     if (exportInfo != nullptr) {
-        exportInfo->handle = mHandle;
+        exportInfo->handle = mHandle.Get();
     }
     return {};
 }
diff --git a/src/dawn/native/d3d/SharedFenceD3D.h b/src/dawn/native/d3d/SharedFenceD3D.h
index 2632974..92f644f 100644
--- a/src/dawn/native/d3d/SharedFenceD3D.h
+++ b/src/dawn/native/d3d/SharedFenceD3D.h
@@ -29,6 +29,7 @@
 #define SRC_DAWN_NATIVE_D3D_SHARED_FENCE_D3D_H_
 
 #include "dawn/native/SharedFence.h"
+#include "dawn/native/SystemHandle.h"
 #include "dawn/native/d3d/d3d_platform.h"
 
 namespace dawn::native::d3d {
@@ -36,16 +37,13 @@
 class Device;
 
 class SharedFence : public SharedFenceBase {
-  public:
-    ~SharedFence() override;
-
   protected:
-    SharedFence(Device* device, const char* label, HANDLE ownedHandle);
+    SharedFence(Device* device, const char* label, SystemHandle ownedHandle);
 
   private:
     MaybeError ExportInfoImpl(SharedFenceExportInfo* info) const override;
 
-    HANDLE mHandle = nullptr;
+    SystemHandle mHandle;
 };
 
 }  // namespace dawn::native::d3d
diff --git a/src/dawn/native/d3d11/FenceD3D11.cpp b/src/dawn/native/d3d11/FenceD3D11.cpp
index 89e4f00..65af1aa 100644
--- a/src/dawn/native/d3d11/FenceD3D11.cpp
+++ b/src/dawn/native/d3d11/FenceD3D11.cpp
@@ -31,6 +31,7 @@
 
 #include "dawn/common/Log.h"
 #include "dawn/native/Error.h"
+#include "dawn/native/SystemHandle.h"
 #include "dawn/native/d3d/D3DError.h"
 #include "dawn/native/d3d12/DeviceD3D12.h"
 
@@ -41,21 +42,16 @@
                                                   HANDLE unownedHandle,
                                                   UINT64 fenceValue) {
     DAWN_ASSERT(unownedHandle != nullptr);
-    HANDLE ownedHandle = nullptr;
-    if (!::DuplicateHandle(::GetCurrentProcess(), unownedHandle, ::GetCurrentProcess(),
-                           &ownedHandle, 0, FALSE, DUPLICATE_SAME_ACCESS)) {
-        return DAWN_DEVICE_LOST_ERROR("D3D11 fence dup handle");
-    }
+    SystemHandle handle;
+    DAWN_TRY_ASSIGN(handle, SystemHandle::Duplicate(unownedHandle));
     ComPtr<ID3D11Fence> d3d11Fence;
-    DAWN_TRY_WITH_CLEANUP(
-        CheckHRESULT(device->OpenSharedFence(ownedHandle, IID_PPV_ARGS(&d3d11Fence)),
-                     "D3D11 fence open handle"),
-        { ::CloseHandle(ownedHandle); });
-    return AcquireRef(new Fence(std::move(d3d11Fence), fenceValue, ownedHandle));
+    DAWN_TRY(CheckHRESULT(device->OpenSharedFence(handle.Get(), IID_PPV_ARGS(&d3d11Fence)),
+                          "D3D11 fence open handle"));
+    return AcquireRef(new Fence(std::move(d3d11Fence), fenceValue, std::move(handle)));
 }
 
-Fence::Fence(ComPtr<ID3D11Fence> d3d11Fence, UINT64 fenceValue, HANDLE sharedHandle)
-    : Base(fenceValue, sharedHandle), mD3D11Fence(std::move(d3d11Fence)) {}
+Fence::Fence(ComPtr<ID3D11Fence> d3d11Fence, UINT64 fenceValue, SystemHandle sharedHandle)
+    : Base(fenceValue, std::move(sharedHandle)), mD3D11Fence(std::move(d3d11Fence)) {}
 
 Fence::~Fence() = default;
 
diff --git a/src/dawn/native/d3d11/FenceD3D11.h b/src/dawn/native/d3d11/FenceD3D11.h
index 17d4ae7..805a4c9 100644
--- a/src/dawn/native/d3d11/FenceD3D11.h
+++ b/src/dawn/native/d3d11/FenceD3D11.h
@@ -47,7 +47,7 @@
   private:
     using Base = d3d::Fence;
 
-    Fence(ComPtr<ID3D11Fence> d3d11Fence, UINT64 fenceValue, HANDLE sharedHandle);
+    Fence(ComPtr<ID3D11Fence> d3d11Fence, UINT64 fenceValue, SystemHandle sharedHandle);
     ~Fence() override;
 
     ComPtr<ID3D11Fence> mD3D11Fence;
diff --git a/src/dawn/native/d3d11/SharedFenceD3D11.cpp b/src/dawn/native/d3d11/SharedFenceD3D11.cpp
index fd4445e..98f628c 100644
--- a/src/dawn/native/d3d11/SharedFenceD3D11.cpp
+++ b/src/dawn/native/d3d11/SharedFenceD3D11.cpp
@@ -27,6 +27,8 @@
 
 #include "dawn/native/d3d11/SharedFenceD3D11.h"
 
+#include <utility>
+
 #include "dawn/native/d3d/D3DError.h"
 #include "dawn/native/d3d11/DeviceD3D11.h"
 
@@ -39,16 +41,13 @@
     const SharedFenceDXGISharedHandleDescriptor* descriptor) {
     DAWN_INVALID_IF(descriptor->handle == nullptr, "shared HANDLE is missing.");
 
-    HANDLE ownedHandle;
-    if (!::DuplicateHandle(::GetCurrentProcess(), descriptor->handle, ::GetCurrentProcess(),
-                           &ownedHandle, 0, FALSE, DUPLICATE_SAME_ACCESS)) {
-        return DAWN_INTERNAL_ERROR("Failed to DuplicateHandle");
-    }
+    SystemHandle ownedHandle;
+    DAWN_TRY_ASSIGN(ownedHandle, SystemHandle::Duplicate(descriptor->handle));
 
-    Ref<SharedFence> fence = AcquireRef(new SharedFence(device, label, ownedHandle));
-    DAWN_TRY(CheckHRESULT(
-        device->GetD3D11Device5()->OpenSharedFence(ownedHandle, IID_PPV_ARGS(&fence->mFence)),
-        "D3D11 fence open shared handle"));
+    Ref<SharedFence> fence = AcquireRef(new SharedFence(device, label, std::move(ownedHandle)));
+    DAWN_TRY(CheckHRESULT(device->GetD3D11Device5()->OpenSharedFence(descriptor->handle,
+                                                                     IID_PPV_ARGS(&fence->mFence)),
+                          "D3D11 fence open shared handle"));
     return fence;
 }
 
diff --git a/src/dawn/native/d3d12/FenceD3D12.cpp b/src/dawn/native/d3d12/FenceD3D12.cpp
index 8d3048d..043fa30 100644
--- a/src/dawn/native/d3d12/FenceD3D12.cpp
+++ b/src/dawn/native/d3d12/FenceD3D12.cpp
@@ -41,21 +41,17 @@
                                                   HANDLE unownedHandle,
                                                   UINT64 fenceValue) {
     DAWN_ASSERT(unownedHandle != nullptr);
-    HANDLE ownedHandle = nullptr;
-    if (!::DuplicateHandle(::GetCurrentProcess(), unownedHandle, ::GetCurrentProcess(),
-                           &ownedHandle, 0, FALSE, DUPLICATE_SAME_ACCESS)) {
-        return DAWN_DEVICE_LOST_ERROR("D3D12 fence dup handle");
-    }
+    SystemHandle ownedHandle;
+    DAWN_TRY_ASSIGN(ownedHandle, SystemHandle::Duplicate(unownedHandle));
+
     ComPtr<ID3D12Fence> d3d12Fence;
-    DAWN_TRY_WITH_CLEANUP(
-        CheckHRESULT(device->OpenSharedHandle(ownedHandle, IID_PPV_ARGS(&d3d12Fence)),
-                     "D3D12 fence open handle"),
-        { ::CloseHandle(ownedHandle); });
-    return AcquireRef(new Fence(std::move(d3d12Fence), fenceValue, ownedHandle));
+    DAWN_TRY(CheckHRESULT(device->OpenSharedHandle(ownedHandle.Get(), IID_PPV_ARGS(&d3d12Fence)),
+                          "D3D12 fence open handle"));
+    return AcquireRef(new Fence(std::move(d3d12Fence), fenceValue, std::move(ownedHandle)));
 }
 
-Fence::Fence(ComPtr<ID3D12Fence> d3d12Fence, UINT64 fenceValue, HANDLE sharedHandle)
-    : Base(fenceValue, sharedHandle), mD3D12Fence(std::move(d3d12Fence)) {}
+Fence::Fence(ComPtr<ID3D12Fence> d3d12Fence, UINT64 fenceValue, SystemHandle sharedHandle)
+    : Base(fenceValue, std::move(sharedHandle)), mD3D12Fence(std::move(d3d12Fence)) {}
 
 Fence::~Fence() = default;
 
diff --git a/src/dawn/native/d3d12/FenceD3D12.h b/src/dawn/native/d3d12/FenceD3D12.h
index 06f60d7..252ea03 100644
--- a/src/dawn/native/d3d12/FenceD3D12.h
+++ b/src/dawn/native/d3d12/FenceD3D12.h
@@ -47,7 +47,7 @@
   private:
     using Base = d3d::Fence;
 
-    Fence(ComPtr<ID3D12Fence> d3d12Fence, UINT64 fenceValue, HANDLE sharedHandle);
+    Fence(ComPtr<ID3D12Fence> d3d12Fence, UINT64 fenceValue, SystemHandle sharedHandle);
     ~Fence() override;
 
     ComPtr<ID3D12Fence> mD3D12Fence;
diff --git a/src/dawn/native/d3d12/SharedFenceD3D12.cpp b/src/dawn/native/d3d12/SharedFenceD3D12.cpp
index 33b5b14..bd8551b 100644
--- a/src/dawn/native/d3d12/SharedFenceD3D12.cpp
+++ b/src/dawn/native/d3d12/SharedFenceD3D12.cpp
@@ -41,16 +41,13 @@
     const SharedFenceDXGISharedHandleDescriptor* descriptor) {
     DAWN_INVALID_IF(descriptor->handle == nullptr, "shared HANDLE is missing.");
 
-    HANDLE ownedHandle;
-    if (!::DuplicateHandle(::GetCurrentProcess(), descriptor->handle, ::GetCurrentProcess(),
-                           &ownedHandle, 0, FALSE, DUPLICATE_SAME_ACCESS)) {
-        return DAWN_INTERNAL_ERROR("Failed to DuplicateHandle");
-    }
+    SystemHandle ownedHandle;
+    DAWN_TRY_ASSIGN(ownedHandle, SystemHandle::Duplicate(descriptor->handle));
 
-    Ref<SharedFence> fence = AcquireRef(new SharedFence(device, label, ownedHandle));
-    DAWN_TRY(CheckHRESULT(
-        device->GetD3D12Device()->OpenSharedHandle(ownedHandle, IID_PPV_ARGS(&fence->mFence)),
-        "D3D12 fence open shared handle"));
+    Ref<SharedFence> fence = AcquireRef(new SharedFence(device, label, std::move(ownedHandle)));
+    DAWN_TRY(CheckHRESULT(device->GetD3D12Device()->OpenSharedHandle(descriptor->handle,
+                                                                     IID_PPV_ARGS(&fence->mFence)),
+                          "D3D12 fence open shared handle"));
     return fence;
 }