Skip to content

Commit 7d5e330

Browse files
lokokungDawn LUCI CQ
authored andcommitted
[dawn] Make spontaneous device callbacks thread safe in native also.
- Unifies the code used for a lot of the device callbacks between the wire client and native. Note that I am currently using dawn/common for convenience, but it might be better in the long run to use another target or something since I needed to explicitly exclude the helpers for WASM builds since the logging callback is currently native only. Change-Id: Ia244a3dab0c9244d6a277c31d857210c9d3fc554 Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/297575 Reviewed-by: Kai Ninomiya <kainino@chromium.org> Reviewed-by: Corentin Wallez <cwallez@chromium.org> Commit-Queue: Loko Kung <lokokung@google.com>
1 parent 807a0a4 commit 7d5e330

File tree

8 files changed

+315
-253
lines changed

8 files changed

+315
-253
lines changed

src/dawn/common/BUILD.gn

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -401,6 +401,13 @@ if (is_win || is_linux || is_chromeos || is_mac || is_fuchsia || is_android ||
401401
]
402402
sources += get_target_outputs(":dawn_gpu_info_gen")
403403

404+
if (!is_wasm) {
405+
sources += [
406+
"WGPUDeviceCallbackInfos.cpp",
407+
"WGPUDeviceCallbackInfos.h",
408+
]
409+
}
410+
404411
public_deps = [
405412
":dawn_gpu_info_gen",
406413
":dawn_version_gen",

src/dawn/common/CMakeLists.txt

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -133,15 +133,15 @@ set(sources
133133
set(conditional_private_depends)
134134

135135
if (WIN32)
136-
list(APPEND headers
136+
list(APPEND private_headers
137137
"windows_with_undefs.h"
138138
"WindowsUtils.h"
139139
)
140140
list(APPEND sources
141141
"WindowsUtils.cpp"
142142
)
143143
elseif(APPLE)
144-
list(APPEND headers
144+
list(APPEND private_headers
145145
"IOSurfaceUtils.h"
146146
)
147147
list(APPEND sources
@@ -154,6 +154,15 @@ elseif(APPLE)
154154
)
155155
endif()
156156

157+
if (NOT EMSCRIPTEN)
158+
list(APPEND private_headers
159+
"WGPUDeviceCallbackInfos.h"
160+
)
161+
list(APPEND sources
162+
"WGPUDeviceCallbackInfos.cpp"
163+
)
164+
endif()
165+
157166
if (CMAKE_SYSTEM_NAME STREQUAL "Android")
158167
find_library(log_lib log)
159168
list(APPEND conditional_private_depends
Lines changed: 181 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,181 @@
1+
// Copyright 2026 The Dawn & Tint Authors
2+
//
3+
// Redistribution and use in source and binary forms, with or without
4+
// modification, are permitted provided that the following conditions are met:
5+
//
6+
// 1. Redistributions of source code must retain the above copyright notice, this
7+
// list of conditions and the following disclaimer.
8+
//
9+
// 2. Redistributions in binary form must reproduce the above copyright notice,
10+
// this list of conditions and the following disclaimer in the documentation
11+
// and/or other materials provided with the distribution.
12+
//
13+
// 3. Neither the name of the copyright holder nor the names of its
14+
// contributors may be used to endorse or promote products derived from
15+
// this software without specific prior written permission.
16+
//
17+
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
18+
// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19+
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
20+
// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
21+
// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22+
// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
23+
// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
24+
// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
25+
// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
26+
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27+
28+
#include "dawn/common/WGPUDeviceCallbackInfos.h"
29+
30+
#include "dawn/common/Log.h"
31+
32+
namespace dawn {
33+
namespace {
34+
// Default callback infos depending on the build type.
35+
#ifdef DAWN_ENABLE_ASSERTS
36+
static constexpr WGPUDeviceLostCallbackInfo kDefaultDeviceLostCallbackInfo = {
37+
nullptr, WGPUCallbackMode_AllowSpontaneous,
38+
[](WGPUDevice const*, WGPUDeviceLostReason, WGPUStringView, void*, void*) {
39+
static std::once_flag flag;
40+
std::call_once(flag, []() {
41+
dawn::WarningLog() << "No Dawn device lost callback was set. This is probably not "
42+
"intended. If you really want to ignore device lost "
43+
"and suppress this message, set the callback explicitly.";
44+
});
45+
},
46+
nullptr, nullptr};
47+
static constexpr WGPUUncapturedErrorCallbackInfo kDefaultUncapturedErrorCallbackInfo = {
48+
nullptr,
49+
[](WGPUDevice const*, WGPUErrorType, WGPUStringView, void*, void*) {
50+
static std::once_flag flag;
51+
std::call_once(flag, []() {
52+
dawn::WarningLog() << "No Dawn device uncaptured error callback was set. This is "
53+
"probably not intended. If you really want to ignore errors "
54+
"and suppress this message, set the callback explicitly.";
55+
});
56+
},
57+
nullptr, nullptr};
58+
static constexpr WGPULoggingCallbackInfo kDefaultLoggingCallbackInfo = {
59+
nullptr,
60+
[](WGPULoggingType, WGPUStringView, void*, void*) {
61+
static std::once_flag flag;
62+
std::call_once(flag, []() {
63+
dawn::WarningLog() << "No Dawn device logging callback callback was set. This is "
64+
"probably not intended. If you really want to ignore logs "
65+
"and suppress this message, set the callback explicitly.";
66+
});
67+
},
68+
nullptr, nullptr};
69+
#else
70+
static constexpr WGPUDeviceLostCallbackInfo kDefaultDeviceLostCallbackInfo = {
71+
nullptr, WGPUCallbackMode_AllowSpontaneous, nullptr, nullptr, nullptr};
72+
static constexpr WGPUUncapturedErrorCallbackInfo kDefaultUncapturedErrorCallbackInfo = {
73+
nullptr, nullptr, nullptr, nullptr};
74+
static constexpr WGPULoggingCallbackInfo kDefaultLoggingCallbackInfo = {nullptr, nullptr, nullptr,
75+
nullptr};
76+
#endif // DAWN_ENABLE_ASSERTS
77+
78+
const WGPUUncapturedErrorCallbackInfo& GetUncapturedErrorCallbackInfoOrDefault(
79+
const WGPUDeviceDescriptor* descriptor) {
80+
if (descriptor != nullptr && descriptor->uncapturedErrorCallbackInfo.callback != nullptr) {
81+
return descriptor->uncapturedErrorCallbackInfo;
82+
}
83+
return kDefaultUncapturedErrorCallbackInfo;
84+
}
85+
} // namespace
86+
87+
const WGPUDeviceLostCallbackInfo& GetDeviceLostCallbackInfoOrDefault(
88+
const WGPUDeviceDescriptor* descriptor) {
89+
if (descriptor != nullptr && descriptor->deviceLostCallbackInfo.callback != nullptr) {
90+
return descriptor->deviceLostCallbackInfo;
91+
}
92+
return kDefaultDeviceLostCallbackInfo;
93+
}
94+
95+
WGPUDeviceCallbackInfos::CallbackInfos::CallbackInfos() = default;
96+
97+
WGPUDeviceCallbackInfos::CallbackInfos::CallbackInfos(const WGPUUncapturedErrorCallbackInfo& error,
98+
const WGPULoggingCallbackInfo& logging) {
99+
if (error.callback != nullptr) {
100+
this->error = error;
101+
}
102+
if (logging.callback != nullptr) {
103+
this->logging = logging;
104+
}
105+
}
106+
107+
WGPUDeviceCallbackInfos::WGPUDeviceCallbackInfos() = default;
108+
109+
WGPUDeviceCallbackInfos::WGPUDeviceCallbackInfos(const WGPUDeviceDescriptor* descriptor)
110+
: mCallbackInfos(GetUncapturedErrorCallbackInfoOrDefault(descriptor),
111+
kDefaultLoggingCallbackInfo) {}
112+
113+
void WGPUDeviceCallbackInfos::CallErrorCallback(WGPUDevice const* device,
114+
WGPUErrorType type,
115+
WGPUStringView message) {
116+
std::optional<WGPUUncapturedErrorCallbackInfo> callbackInfo;
117+
mCallbackInfos.Use<NotifyType::None>([&](auto callbackInfos) {
118+
callbackInfo = callbackInfos->error;
119+
if (callbackInfo) {
120+
callbackInfos->semaphore += 1;
121+
}
122+
});
123+
124+
// If we don't have a callback info, we can just return.
125+
if (!callbackInfo) {
126+
return;
127+
}
128+
129+
// Call the callback without holding the lock to prevent any re-entrant issues.
130+
DAWN_ASSERT(callbackInfo->callback != nullptr);
131+
callbackInfo->callback(device, type, message, callbackInfo->userdata1, callbackInfo->userdata2);
132+
133+
mCallbackInfos.Use([&](auto callbackInfos) {
134+
DAWN_ASSERT(callbackInfos->semaphore > 0);
135+
callbackInfos->semaphore -= 1;
136+
});
137+
}
138+
139+
void WGPUDeviceCallbackInfos::CallLoggingCallback(WGPULoggingType type, WGPUStringView message) {
140+
std::optional<WGPULoggingCallbackInfo> callbackInfo;
141+
mCallbackInfos.Use<NotifyType::None>([&](auto callbackInfos) {
142+
callbackInfo = callbackInfos->logging;
143+
if (callbackInfo) {
144+
callbackInfos->semaphore += 1;
145+
}
146+
});
147+
148+
// If we don't have a callback info, we can just return.
149+
if (!callbackInfo) {
150+
return;
151+
}
152+
153+
// Call the callback without holding the lock to prevent any re-entrant issues.
154+
DAWN_ASSERT(callbackInfo->callback != nullptr);
155+
callbackInfo->callback(type, message, callbackInfo->userdata1, callbackInfo->userdata2);
156+
157+
mCallbackInfos.Use([&](auto callbackInfos) {
158+
DAWN_ASSERT(callbackInfos->semaphore > 0);
159+
callbackInfos->semaphore -= 1;
160+
});
161+
}
162+
163+
void WGPUDeviceCallbackInfos::SetLoggingCallbackInfo(const WGPULoggingCallbackInfo& callbackInfo) {
164+
mCallbackInfos.Use<NotifyType::None>(
165+
[&](auto callbackInfos) { callbackInfos->logging = callbackInfo; });
166+
}
167+
168+
void WGPUDeviceCallbackInfos::Clear() {
169+
mCallbackInfos.Use<NotifyType::None>([](auto callbackInfos) {
170+
callbackInfos->error = std::nullopt;
171+
callbackInfos->logging = std::nullopt;
172+
173+
// The uncaptured error and logging callbacks are spontaneous and must not be called
174+
// after we call the device lost's |mCallback| below. Although we have cleared those
175+
// callbacks, we need to wait for any remaining outstanding callbacks to finish before
176+
// continuing.
177+
callbackInfos.Wait([](auto& x) { return x.semaphore == 0; });
178+
});
179+
}
180+
181+
} // namespace dawn
Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,85 @@
1+
// Copyright 2026 The Dawn & Tint Authors
2+
//
3+
// Redistribution and use in source and binary forms, with or without
4+
// modification, are permitted provided that the following conditions are met:
5+
//
6+
// 1. Redistributions of source code must retain the above copyright notice, this
7+
// list of conditions and the following disclaimer.
8+
//
9+
// 2. Redistributions in binary form must reproduce the above copyright notice,
10+
// this list of conditions and the following disclaimer in the documentation
11+
// and/or other materials provided with the distribution.
12+
//
13+
// 3. Neither the name of the copyright holder nor the names of its
14+
// contributors may be used to endorse or promote products derived from
15+
// this software without specific prior written permission.
16+
//
17+
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
18+
// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19+
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
20+
// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
21+
// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22+
// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
23+
// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
24+
// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
25+
// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
26+
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27+
28+
#ifndef SRC_DAWN_COMMON_WGPUDEVICECALLBACKINFOS_H_
29+
#define SRC_DAWN_COMMON_WGPUDEVICECALLBACKINFOS_H_
30+
31+
#include <webgpu/webgpu.h>
32+
33+
#include <optional>
34+
35+
#include "dawn/common/MutexProtected.h"
36+
37+
namespace dawn {
38+
39+
const WGPUDeviceLostCallbackInfo& GetDeviceLostCallbackInfoOrDefault(
40+
const WGPUDeviceDescriptor* descriptor);
41+
42+
// Device level unconditionally spontaneous callbacks need to be synchronized so this class provides
43+
// a common wrapper for those callback infos so that the implementation can be shared across native
44+
// and wire client.
45+
class WGPUDeviceCallbackInfos {
46+
public:
47+
WGPUDeviceCallbackInfos();
48+
explicit WGPUDeviceCallbackInfos(const WGPUDeviceDescriptor* descriptor);
49+
50+
// APIs to call the callbacks.
51+
void CallErrorCallback(WGPUDevice const* device, WGPUErrorType type, WGPUStringView message);
52+
void CallLoggingCallback(WGPULoggingType type, WGPUStringView message);
53+
54+
// The logging callback currently needs to support a setter.
55+
void SetLoggingCallbackInfo(const WGPULoggingCallbackInfo& callbackInfo);
56+
57+
// Used when the device is lost and we want to clear out the callbacks. This helper waits until
58+
// there are no other places using the callbacks before returning. This is important since this
59+
// is generally used when completing the device lost event which users may use to clean up the
60+
// uncaptured error and logging callbacks.
61+
void Clear();
62+
63+
private:
64+
struct CallbackInfos {
65+
CallbackInfos();
66+
CallbackInfos(const WGPUUncapturedErrorCallbackInfo& error,
67+
const WGPULoggingCallbackInfo& logging);
68+
69+
// The callback infos are optional because once the device is lost, they are set to
70+
// std::nullopt and no longer do anything.
71+
std::optional<WGPUUncapturedErrorCallbackInfo> error = std::nullopt;
72+
std::optional<WGPULoggingCallbackInfo> logging = std::nullopt;
73+
74+
// Counter that tracks how many places are currently using callback infos. This is used to
75+
// ensure that before we call the device lost callback (which may deallocate the uncaptured
76+
// error and logging callbacks), we have ensured that there are no outstanding references to
77+
// those callbacks.
78+
uint32_t semaphore = 0;
79+
};
80+
MutexCondVarProtected<CallbackInfos> mCallbackInfos;
81+
};
82+
83+
} // namespace dawn
84+
85+
#endif // SRC_DAWN_COMMON_WGPUDEVICECALLBACKINFOS_H_

0 commit comments

Comments
 (0)