[dawn][common] Introduces Defer option in Guards.

- The Defer options in Guards allows users to queue additional work
  to be completed after a lock has been released. This is particularly
  useful for triggering code paths that can result in user callbacks
  being fired since callbacks can sometimes be API re-entrant
  leading to deadlocks.

Bug: 40643114
Change-Id: I6c5755e76bd3fac5de8f28397a91cb384a525dd9
Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/245315
Commit-Queue: Loko Kung <lokokung@google.com>
Reviewed-by: Kai Ninomiya <kainino@chromium.org>
diff --git a/src/dawn/common/BUILD.gn b/src/dawn/common/BUILD.gn
index a938ef0..8c0d76e 100644
--- a/src/dawn/common/BUILD.gn
+++ b/src/dawn/common/BUILD.gn
@@ -273,6 +273,8 @@
       "ContentLessObjectCache.h",
       "ContentLessObjectCacheable.h",
       "CoreFoundationRef.h",
+      "Defer.cpp",
+      "Defer.h",
       "DynamicLib.cpp",
       "DynamicLib.h",
       "Enumerator.h",
diff --git a/src/dawn/common/CMakeLists.txt b/src/dawn/common/CMakeLists.txt
index 3965ffc..7f723d5 100644
--- a/src/dawn/common/CMakeLists.txt
+++ b/src/dawn/common/CMakeLists.txt
@@ -54,6 +54,7 @@
     "ContentLessObjectCache.h"
     "ContentLessObjectCacheable.h"
     "CoreFoundationRef.h"
+    "Defer.h"
     "DynamicLib.h"
     "egl_platform.h"
     "Enumerator.h"
@@ -108,6 +109,7 @@
     "${DAWN_GPU_INFO_AUTOGEN_SOURCES}"
     "AlignedAlloc.cpp"
     "Assert.cpp"
+    "Defer.cpp"
     "DynamicLib.cpp"
     "FutureUtils.cpp"
     "GPUInfo.cpp"
diff --git a/src/dawn/common/Defer.cpp b/src/dawn/common/Defer.cpp
new file mode 100644
index 0000000..07cc803
--- /dev/null
+++ b/src/dawn/common/Defer.cpp
@@ -0,0 +1,44 @@
+// Copyright 2025 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 "dawn/common/Defer.h"
+
+#include <utility>
+
+namespace dawn {
+
+Defer::~Defer() {
+    for (auto& f : mFuncs) {
+        std::move(f)();
+    }
+}
+
+void Defer::Append(std::function<void()> f) {
+    mFuncs.push_back(std::move(f));
+}
+
+}  // namespace dawn
diff --git a/src/dawn/common/Defer.h b/src/dawn/common/Defer.h
new file mode 100644
index 0000000..7c552a0
--- /dev/null
+++ b/src/dawn/common/Defer.h
@@ -0,0 +1,52 @@
+// Copyright 2025 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_COMMON_DEFER_H_
+#define SRC_DAWN_COMMON_DEFER_H_
+
+#include <functional>
+#include <vector>
+
+#include "dawn/common/NonMovable.h"
+#include "dawn/common/StackAllocated.h"
+
+namespace dawn {
+
+// Defer object runs series of cleanup functions when it's destructor is called.
+class Defer : public NonMovable, StackAllocated {
+  public:
+    ~Defer();
+
+    void Append(std::function<void()> f);
+
+  private:
+    std::vector<std::function<void()>> mFuncs;
+};
+
+}  // namespace dawn
+
+#endif  // SRC_DAWN_COMMON_DEFER_H_
diff --git a/src/dawn/common/MutexProtected.h b/src/dawn/common/MutexProtected.h
index 1194d14..c922ea7 100644
--- a/src/dawn/common/MutexProtected.h
+++ b/src/dawn/common/MutexProtected.h
@@ -31,10 +31,12 @@
 #include <mutex>
 #include <utility>
 
+#include "dawn/common/Defer.h"
 #include "dawn/common/Mutex.h"
 #include "dawn/common/NonMovable.h"
 #include "dawn/common/Ref.h"
 #include "dawn/common/StackAllocated.h"
+#include "partition_alloc/pointers/raw_ptr.h"
 #include "partition_alloc/pointers/raw_ptr_exclusion.h"
 
 namespace dawn {
@@ -91,9 +93,18 @@
     auto* operator->() const { return Get(); }
     auto& operator*() const { return *Get(); }
 
+    void Defer(std::function<void()> f) {
+        DAWN_ASSERT(mDefer);
+        mDefer->Append(std::move(f));
+    }
+
   protected:
-    Guard(T* obj, typename Traits::MutexType& mutex) : mLock(Traits::GetMutex(mutex)), mObj(obj) {}
-    Guard(Guard&& other) : mLock(std::move(other.mLock)), mObj(std::move(other.mObj)) {
+    Guard(T* obj, typename Traits::MutexType& mutex, class Defer* defer = nullptr)
+        : mLock(Traits::GetMutex(mutex)), mObj(obj), mDefer(defer) {}
+    Guard(Guard&& other)
+        : mLock(std::move(other.mLock)),
+          mObj(std::move(other.mObj)),
+          mDefer(std::move(other.mDefer)) {
         other.mObj = nullptr;
     }
 
@@ -113,6 +124,7 @@
     // The pointer is always transiently used while the MutexProtected is in scope so it is
     // unlikely to be used after it is freed.
     RAW_PTR_EXCLUSION T* mObj = nullptr;
+    raw_ptr<class Defer> mDefer = nullptr;
 };
 
 template <typename T, typename Traits, template <typename, typename> class Guard = detail::Guard>
@@ -136,8 +148,15 @@
         return fn(Use());
     }
 
+    template <typename Fn>
+    auto UseWithDefer(Fn&& fn) {
+        Defer defer;
+        return fn(UseWithDefer(defer));
+    }
+
   protected:
     virtual Usage Use() = 0;
+    virtual Usage UseWithDefer(Defer& defer) = 0;
     virtual ConstUsage Use() const = 0;
 
     mutable typename Traits::MutexType mMutex;
@@ -191,9 +210,11 @@
     MutexProtected(Args&&... args) : mObj(std::forward<Args>(args)...) {}
 
     using Base::Use;
+    using Base::UseWithDefer;
 
   private:
     Usage Use() override { return Usage(&mObj, this->mMutex); }
+    Usage UseWithDefer(Defer& defer) override { return Usage(&mObj, this->mMutex, &defer); }
     ConstUsage Use() const override { return ConstUsage(&mObj, this->mMutex); }
 
     T mObj;
@@ -230,9 +251,13 @@
     using typename Base::Usage;
 
     using Base::Use;
+    using Base::UseWithDefer;
 
   private:
     Usage Use() override { return Usage(static_cast<T*>(this), this->mMutex); }
+    Usage UseWithDefer(Defer& defer) override {
+        return Usage(static_cast<T*>(this), this->mMutex, &defer);
+    }
     ConstUsage Use() const override {
         return ConstUsage(static_cast<const T*>(this), this->mMutex);
     }
diff --git a/src/dawn/native/d3d11/CommandRecordingContextD3D11.h b/src/dawn/native/d3d11/CommandRecordingContextD3D11.h
index 97b5e47..8b8d8f9 100644
--- a/src/dawn/native/d3d11/CommandRecordingContextD3D11.h
+++ b/src/dawn/native/d3d11/CommandRecordingContextD3D11.h
@@ -60,7 +60,10 @@
     using Base = ::dawn::detail::Guard<Ctx, Traits>;
 
     CommandRecordingContextGuard(CommandRecordingContextGuard&& rhs) = default;
-    CommandRecordingContextGuard(Ctx* ctx, typename Traits::MutexType& mutex) : Base(ctx, mutex) {
+    CommandRecordingContextGuard(Ctx* ctx,
+                                 typename Traits::MutexType& mutex,
+                                 Defer* defer = nullptr)
+        : Base(ctx, mutex, defer) {
         if (this->Get() && this->Get()->mD3D11Multithread) {
             this->Get()->mD3D11Multithread->Enter();
         }