[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();
}