[dawn][native] Ensure device lock is held for |CheckAndUpdateCompletedSerials|.
- Some backends do not implement a thread-safe
ExecutionQueue::CheckAndUpdateCompletedSerials, as a result we need
to make sure that we are still holding the device lock when calling it.
Bug: 420686217
Change-Id: I08bd6548e7e385bb4901072ffd536ee1b9b28500
Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/244974
Commit-Queue: Kai Ninomiya <kainino@chromium.org>
Commit-Queue: Loko Kung <lokokung@google.com>
Reviewed-by: Kai Ninomiya <kainino@chromium.org>
Auto-Submit: Loko Kung <lokokung@google.com>
diff --git a/src/dawn/native/EventManager.cpp b/src/dawn/native/EventManager.cpp
index 17717ac..4248a2a 100644
--- a/src/dawn/native/EventManager.cpp
+++ b/src/dawn/native/EventManager.cpp
@@ -178,21 +178,16 @@
DAWN_TRY_ASSIGN(waitResult,
queue->WaitForQueueSerial(waitSerial, timeout));
}
+ // Update completed serials.
+ DAWN_TRY(queue->CheckPassedSerials());
}
return {};
}(),
"waiting for work in %s.", queue.Get());
}
-
- // Checking and updating serials cannot hold the device-wide lock because it may cause user
- // callbacks to fire. As a result, we update the serials without the lock, and only
- // reacquire the lock if there was an error.
- auto maybeError = queue->CheckPassedSerials();
- if (maybeError.IsError()) {
- auto deviceLock(device->GetScopedLock());
- [[maybe_unused]] bool error = device->ConsumedError(
- std::move(maybeError), "updating passed serials in %s", queue.Get());
- }
+ // TODO(crbug.com/421945313): Checking and updating serials cannot hold the device-wide lock
+ // because it may cause user callbacks to fire.
+ queue->UpdateCompletedSerial(queue->GetCompletedCommandSerial());
}
}
diff --git a/src/dawn/native/ExecutionQueue.cpp b/src/dawn/native/ExecutionQueue.cpp
index 6fbb56a..1359724 100644
--- a/src/dawn/native/ExecutionQueue.cpp
+++ b/src/dawn/native/ExecutionQueue.cpp
@@ -51,7 +51,15 @@
DAWN_ASSERT(completedSerial <=
ExecutionSerial(mLastSubmittedSerial.load(std::memory_order_acquire)));
- UpdateCompletedSerial(completedSerial);
+ // Atomically set mCompletedSerial to completedSerial if completedSerial is larger.
+ uint64_t current = mCompletedSerial.load(std::memory_order_acquire);
+ while (uint64_t(completedSerial) > current &&
+ !mCompletedSerial.compare_exchange_weak(current, uint64_t(completedSerial),
+ std::memory_order_acq_rel)) {
+ }
+ // TODO(crbug.com/421945313): We should call |UpdateCompletedSerial| here also, but since some
+ // backends rely on the device lock for safe use of |CheckAndUpdateCompletedSerials|, we
+ // separate that call out for now.
return {};
}
diff --git a/src/dawn/native/ExecutionQueue.h b/src/dawn/native/ExecutionQueue.h
index c52908c..256a306 100644
--- a/src/dawn/native/ExecutionQueue.h
+++ b/src/dawn/native/ExecutionQueue.h
@@ -94,11 +94,11 @@
// example, until the client has explictly issued a submission.
enum class SubmitMode { Normal, Passive };
- protected:
// Currently, the queue has two paths for serial updating, one is via DeviceBase::Tick which
// calls into the backend specific polling mechanisms implemented in
// CheckAndUpdateCompletedSerials. Alternatively, the backend can actively call
// UpdateCompletedSerial when a new serial is complete to make forward progress proactively.
+ // TODO(crbug.com/421945313): This shouldn't need to be public once we fix lock ordering.
void UpdateCompletedSerial(ExecutionSerial completedSerial);
private: