Skip to content

Commit 2bf640b

Browse files
lokokungDawn LUCI CQ
authored andcommitted
[dawn][d3d] Adds loop for WaitForMultipleObjects with INFINITY.
- Adding the loop addresses the reverted workaround. - Also includes a small fix for potential race in spontaneous event handling. - Re-enables all the related tests now that they seem to be passing fine now on the arm64 Qualcomm devices with this fix. - Also reverts "Fix MapAsyncAndWait use-after-free" commit 37905ab. Original change's description: > Fix MapAsyncAndWait use-after-free > > Fixes an issue where the callback registered in > DawnTestBase::MapAsyncAndWait could cause an access violation during > global test teardown due to trying to run a callback that was allocated > on the stack and had already gone out of scope. This only occurred when > instance.WaitAny() timed out, which would cause the callback to still > be around until the instance got destroyed and ensured that all pending > callbacks were complete. > > Now, the actual callback to run is provided via a shared_ptr and the > lambda provided to buffer.MapAsync() checks if it is null before running > it. After instance.WaitAny() returns but before we make any asserts, we > set the pointer to null, preventing the soon-to-be-dangling pointer from > being used in the future. > > This is meant as a temporary workaround, as WaitAny() should always run > the provided callback. This can be reverted once the underlying issue > is fixed. > > Bug: 459864803, 460743383 > Change-Id: I5384e74ff6ab9da6104b07e6d9d235bf5d4b40c2 > Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/273736 > Commit-Queue: Brian Sheedy <bsheedy@google.com> > Reviewed-by: Loko Kung <lokokung@google.com> > Auto-Submit: Brian Sheedy <bsheedy@google.com> > Reviewed-by: Kai Ninomiya <kainino@chromium.org> Bug: 459864803, 460743383, 461837996, 469328928, 465497435 Change-Id: Iabf26a66fbd6c92bf788a283c05b64d72e6f12ee Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/277975 Reviewed-by: Brian Sheedy <bsheedy@google.com> Commit-Queue: Kai Ninomiya <kainino@chromium.org> Auto-Submit: Loko Kung <lokokung@google.com> Commit-Queue: Loko Kung <lokokung@google.com> Reviewed-by: Kai Ninomiya <kainino@chromium.org>
1 parent fff9da2 commit 2bf640b

File tree

5 files changed

+44
-90
lines changed

5 files changed

+44
-90
lines changed

src/dawn/native/EventManager.cpp

Lines changed: 11 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -317,14 +317,7 @@ FutureID EventManager::TrackEvent(Ref<TrackedEvent>&& event) {
317317
FutureID futureID = mNextFutureID++;
318318
event->mFutureID = futureID;
319319

320-
// Handle the event now if it's spontaneous and ready.
321-
if (event->mCallbackMode == wgpu::CallbackMode::AllowSpontaneous) {
322-
if (event->IsReadyToComplete()) {
323-
event->EnsureComplete(EventCompletionType::Ready);
324-
return futureID;
325-
}
326-
}
327-
320+
// For queue events, we schedule a task to set the event ready w.r.t the completion serial.
328321
if (const auto* queueAndSerial = event->GetIfQueueAndSerial()) {
329322
if (auto q = queueAndSerial->queue.Promote()) {
330323
q->TrackSerialTask(queueAndSerial->completionSerial, [this, event]() {
@@ -341,6 +334,16 @@ FutureID EventManager::TrackEvent(Ref<TrackedEvent>&& event) {
341334
}
342335

343336
mEvents.Use([&](auto events) {
337+
// For ready spontaneous events, handle the event now. Note that we need to do the check in
338+
// a |Use| scope of |mEvents| to ensure that other threads that may reference the returned
339+
// Future will reflect the proper state of the event.
340+
if (event->mCallbackMode == wgpu::CallbackMode::AllowSpontaneous) {
341+
if (event->IsReadyToComplete()) {
342+
event->EnsureComplete(EventCompletionType::Ready);
343+
return;
344+
}
345+
}
346+
344347
if (!events->has_value()) {
345348
// We are shutting down, so if the event isn't spontaneous, call the callback now.
346349
if (event->mCallbackMode != wgpu::CallbackMode::AllowSpontaneous) {

src/dawn/native/WaitAnySystemEvent.h

Lines changed: 25 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -85,16 +85,32 @@ template <typename It>
8585
handles.push_back((*it).first.mPrimitive.Get());
8686
}
8787
DAWN_ASSERT(handles.size() <= MAXIMUM_WAIT_OBJECTS);
88-
DWORD status = WaitForMultipleObjects(handles.size(), handles.data(), /*bWaitAll=*/false,
89-
ToMilliseconds(timeout));
90-
if (status == WAIT_TIMEOUT) {
91-
return false;
92-
}
93-
DAWN_CHECK(WAIT_OBJECT_0 <= status && status < WAIT_OBJECT_0 + count);
94-
const size_t completedIndex = status - WAIT_OBJECT_0;
9588

96-
*(*(begin + completedIndex)).second = true;
97-
return true;
89+
// TODO(crbug.com/344798087): Handle the issue of timeouts in a more general way further up the
90+
// stack.
91+
// Note that without the looping here, we observed timeouts on arm64 Qualcomm devices. This
92+
// looping for the INIFINITY timeout case is also used as a workaround in the Vulkan backend and
93+
// should be sufficient for the time being because other cases, i.e. when the timeout is not
94+
// INFINITY, the user code would be expected to explicitly handle the timeout case anyways.
95+
DWORD status = WAIT_TIMEOUT;
96+
DWORD timeoutMS = ToMilliseconds(timeout);
97+
while (1) {
98+
status = WaitForMultipleObjects(handles.size(), handles.data(), /*bWaitAll=*/false,
99+
ToMilliseconds(timeout));
100+
if (status == WAIT_TIMEOUT) {
101+
if (timeoutMS == INFINITE) {
102+
continue;
103+
} else {
104+
return false;
105+
}
106+
}
107+
108+
DAWN_CHECK(WAIT_OBJECT_0 <= status && status < WAIT_OBJECT_0 + count);
109+
const size_t completedIndex = status - WAIT_OBJECT_0;
110+
111+
*(*(begin + completedIndex)).second = true;
112+
return true;
113+
}
98114
#elif DAWN_PLATFORM_IS(POSIX)
99115
absl::InlinedVector<pollfd, 4 /* avoid heap allocation for small waits */> pollfds;
100116
pollfds.reserve(count);

src/dawn/tests/DawnTest.cpp

Lines changed: 8 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -2087,34 +2087,14 @@ void DawnTestBase::MapAsyncAndWait(const wgpu::Buffer& buffer,
20872087
if (!UsesWire()) {
20882088
// We use a new mock callback here so that the validation on the call happens as soon as the
20892089
// scope of this call ends.
2090-
auto mockCb =
2091-
std::make_shared<MockCppCallback<void (*)(wgpu::MapAsyncStatus, wgpu::StringView)>>();
2092-
EXPECT_CALL(*mockCb, Call(wgpu::MapAsyncStatus::Success, _)).Times(1);
2093-
2094-
// TODO(crbug.com/460743383): This is a workaround for teardown causing WaitAny to return
2095-
// without calling the callback. Revert this to the state before
2096-
// https://dawn-review.googlesource.com/c/dawn/+/273736 when this is fixed.
2097-
// The mock callback is local to this function, but the async map request can live longer
2098-
// if the test times out. To prevent a use-after-free, we use a shared pointer to
2099-
// control the mock callback's availability.
2100-
wgpu::WaitStatus status = instance.WaitAny(
2101-
buffer.MapAsync(mapMode, offset, size, wgpu::CallbackMode::WaitAnyOnly,
2102-
[mockCb](wgpu::MapAsyncStatus status, wgpu::StringView message) {
2103-
if (mockCb != nullptr) {
2104-
mockCb->Callback()(status, message);
2105-
}
2106-
}),
2107-
UINT64_MAX);
2108-
2109-
// Disarm the callback. Since we need to verify expectations on the
2110-
// callback after disarming it, swap with a nullptr instead of simply
2111-
// setting it to null directly.
2112-
auto swappedCb =
2113-
std::make_shared<MockCppCallback<void (*)(wgpu::MapAsyncStatus, wgpu::StringView)>>();
2114-
swappedCb.reset();
2115-
mockCb.swap(swappedCb);
2116-
testing::Mock::VerifyAndClearExpectations(swappedCb.get());
2117-
ASSERT_EQ(status, wgpu::WaitStatus::Success);
2090+
MockCppCallback<void (*)(wgpu::MapAsyncStatus, wgpu::StringView)> mockCb;
2091+
EXPECT_CALL(mockCb, Call(wgpu::MapAsyncStatus::Success, _)).Times(1);
2092+
2093+
ASSERT_EQ(
2094+
instance.WaitAny(buffer.MapAsync(mapMode, offset, size, wgpu::CallbackMode::WaitAnyOnly,
2095+
mockCb.Callback()),
2096+
UINT64_MAX),
2097+
wgpu::WaitStatus::Success);
21182098
} else {
21192099
bool done = false;
21202100
buffer.MapAsync(mapMode, offset, size, wgpu::CallbackMode::AllowProcessEvents,

src/dawn/tests/end2end/BufferTests.cpp

Lines changed: 0 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -125,11 +125,6 @@ void CheckMapping(const void* actual, const void* expected, size_t size) {
125125

126126
// Test that the simplest map read works
127127
TEST_P(BufferMappingTests, MapRead_Basic) {
128-
// TODO(crbug.com/469328928, crbug.com/465497435): Flakily times out on
129-
// Snapdragon X Elite SoCs, suspected of causing a crash in global test
130-
// teardown as a result.
131-
DAWN_SUPPRESS_TEST_IF(IsWindows() && IsQualcomm());
132-
133128
wgpu::Buffer buffer = CreateMapReadBuffer(4);
134129

135130
const uint32_t myData = 0x01020304;
@@ -144,11 +139,6 @@ TEST_P(BufferMappingTests, MapRead_Basic) {
144139

145140
// Test map-reading a zero-sized buffer.
146141
TEST_P(BufferMappingTests, MapRead_ZeroSized) {
147-
// TODO(crbug.com/469328928, crbug.com/465497435): Flakily times out on
148-
// Snapdragon X Elite SoCs, suspected of causing a crash in global test
149-
// teardown as a result.
150-
DAWN_SUPPRESS_TEST_IF(IsWindows() && IsQualcomm());
151-
152142
wgpu::Buffer buffer = CreateMapReadBuffer(0);
153143

154144
MapAsyncAndWait(buffer, wgpu::MapMode::Read, 0, wgpu::kWholeMapSize);
@@ -158,11 +148,6 @@ TEST_P(BufferMappingTests, MapRead_ZeroSized) {
158148

159149
// Test map-reading with a non-zero offset
160150
TEST_P(BufferMappingTests, MapRead_NonZeroOffset) {
161-
// TODO(crbug.com/469328928, crbug.com/465497435): Flakily times out on
162-
// Snapdragon X Elite SoCs, suspected of causing a crash in global test
163-
// teardown as a result.
164-
DAWN_SUPPRESS_TEST_IF(IsWindows() && IsQualcomm());
165-
166151
uint32_t myData[3] = {0x01020304, 0x05060708, 0x090A0B0C};
167152

168153
wgpu::Buffer buffer = CreateMapReadBuffer(sizeof(myData));
@@ -180,11 +165,6 @@ TEST_P(BufferMappingTests, MapRead_NonZeroOffset) {
180165

181166
// Map read and unmap twice. Test that both of these two iterations work.
182167
TEST_P(BufferMappingTests, MapRead_Twice) {
183-
// TODO(crbug.com/469328928, crbug.com/465497435): Flakily times out on
184-
// Snapdragon X Elite SoCs, suspected of causing a crash in global test
185-
// teardown as a result.
186-
DAWN_SUPPRESS_TEST_IF(IsWindows() && IsQualcomm());
187-
188168
wgpu::Buffer buffer = CreateMapReadBuffer(4);
189169

190170
uint32_t myData = 0x01020304;
@@ -204,11 +184,6 @@ TEST_P(BufferMappingTests, MapRead_Twice) {
204184

205185
// Map read and test multiple get mapped range data
206186
TEST_P(BufferMappingTests, MapRead_MultipleMappedRange) {
207-
// TODO(crbug.com/469328928, crbug.com/465497435): Flakily times out on
208-
// Snapdragon X Elite SoCs, suspected of causing a crash in global test
209-
// teardown as a result.
210-
DAWN_SUPPRESS_TEST_IF(IsWindows() && IsQualcomm());
211-
212187
wgpu::Buffer buffer = CreateMapReadBuffer(12);
213188

214189
uint32_t myData[] = {0x00010203, 0x04050607, 0x08090a0b};
@@ -224,11 +199,6 @@ TEST_P(BufferMappingTests, MapRead_MultipleMappedRange) {
224199

225200
// Test map-reading a large buffer.
226201
TEST_P(BufferMappingTests, MapRead_Large) {
227-
// TODO(crbug.com/469328928, crbug.com/465497435): Flakily times out on
228-
// Snapdragon X Elite SoCs, suspected of causing a crash in global test
229-
// teardown as a result.
230-
DAWN_SUPPRESS_TEST_IF(IsWindows() && IsQualcomm());
231-
232202
constexpr uint32_t kDataSize = 1000 * 1000;
233203
constexpr size_t kByteSize = kDataSize * sizeof(uint32_t);
234204
wgpu::Buffer buffer = CreateMapReadBuffer(kByteSize);
@@ -264,11 +234,6 @@ TEST_P(BufferMappingTests, MapRead_Large) {
264234

265235
// Test that GetConstMappedRange works inside map-read callback
266236
TEST_P(BufferMappingTests, MapRead_InCallback) {
267-
// TODO(crbug.com/469328928, crbug.com/465497435): Flakily times out on
268-
// Snapdragon X Elite SoCs, suspected of causing a crash in global test
269-
// teardown as a result.
270-
DAWN_SUPPRESS_TEST_IF(IsWindows() && IsQualcomm());
271-
272237
constexpr size_t kBufferSize = 12;
273238
wgpu::Buffer buffer = CreateMapReadBuffer(kBufferSize);
274239

@@ -1373,10 +1338,6 @@ class BufferMapExtendedUsagesTests : public DawnTest {
13731338
DAWN_TEST_UNSUPPORTED_IF(UsesWire());
13741339
// Skip all tests if the required feature is not supported.
13751340
DAWN_TEST_UNSUPPORTED_IF(!SupportsFeatures({wgpu::FeatureName::BufferMapExtendedUsages}));
1376-
1377-
// TODO(crbug.com/465167911): Flakily gets unexpected nullptrs on
1378-
// Snapdragon X Elite SoCs.
1379-
DAWN_SUPPRESS_TEST_IF(IsWindows() && IsQualcomm());
13801341
}
13811342

13821343
std::vector<wgpu::FeatureName> GetRequiredFeatures() override {

src/dawn/tests/end2end/DepthStencilCopyTests.cpp

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -665,9 +665,6 @@ TEST_P(DepthCopyTests, FromNonZeroMipDepthAspect) {
665665
// inevitably written. So we need to make sure the original content of the buffer that's outside of
666666
// the copy extent is still correctly preserved.
667667
TEST_P(DepthCopyTests, PreserveBufferContent) {
668-
// TODO(crbug.com/461837996): Flaky on Snapdragon X Elite.
669-
DAWN_SUPPRESS_TEST_IF(IsWindows() && IsQualcomm());
670-
671668
constexpr uint32_t kBufferCopyOffsets[] = {0u, 4u, 512u};
672669
constexpr uint32_t kTestTextureSizes[][2] = {
673670
{1, 1},
@@ -1220,9 +1217,6 @@ TEST_P(StencilCopyTests, FromNonZeroMipStencilAspect) {
12201217
// inevitably written. So we need to make sure the original content of the buffer that's outside of
12211218
// the copy extent is still correctly preserved.
12221219
TEST_P(StencilCopyTests, PreserveBufferContent) {
1223-
// TODO(crbug.com/461837996): Flaky on Snapdragon X Elite.
1224-
DAWN_SUPPRESS_TEST_IF(IsWindows() && IsQualcomm());
1225-
12261220
constexpr uint32_t kBufferCopyOffsets[] = {0u, 4u, 512u};
12271221
constexpr uint32_t kTestTextureSizes[][2] = {
12281222
{1, 1},

0 commit comments

Comments
 (0)