Allow WaitListEvent to require multiple signals.

Change the atomic bool in WaitListEvent to a counter and require
multiple Signal calls before marking the event as complete.

Bug: 406522796
Change-Id: I2bb5c8d24d0af550a37c54c5dd70a5a57db290f2
Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/259254
Reviewed-by: Corentin Wallez <cwallez@chromium.org>
Reviewed-by: Loko Kung <lokokung@google.com>
Commit-Queue: Geoff Lang <geofflang@chromium.org>
diff --git a/src/dawn/native/WaitListEvent.cpp b/src/dawn/native/WaitListEvent.cpp
index 70f4420..3cf921b 100644
--- a/src/dawn/native/WaitListEvent.cpp
+++ b/src/dawn/native/WaitListEvent.cpp
@@ -33,16 +33,21 @@
 
 namespace dawn::native {
 
-WaitListEvent::WaitListEvent() = default;
+WaitListEvent::WaitListEvent(uint64_t requiredSignalCount)
+    : mRemainingSignalCount(requiredSignalCount) {
+    DAWN_ASSERT(mRemainingSignalCount > 0);
+}
 WaitListEvent::~WaitListEvent() = default;
 
 bool WaitListEvent::IsSignaled() const {
-    return mSignaled.load(std::memory_order_acquire);
+    return mRemainingSignalCount.load(std::memory_order_acquire) == 0;
 }
 
 void WaitListEvent::Signal() {
-    std::lock_guard<std::mutex> lock(mMutex);
-    if (!mSignaled.exchange(true, std::memory_order_release)) {
+    uint64_t prevSignalCount = mRemainingSignalCount.fetch_sub(1, std::memory_order_release);
+    DAWN_ASSERT(prevSignalCount != 0);
+    if (prevSignalCount == 1) {
+        std::lock_guard<std::mutex> lock(mMutex);
         for (SyncWaiter* w : std::move(mSyncWaiters)) {
             {
                 std::lock_guard<std::mutex> waiterLock(w->mutex);
diff --git a/src/dawn/native/WaitListEvent.h b/src/dawn/native/WaitListEvent.h
index fbb85b0..109df76 100644
--- a/src/dawn/native/WaitListEvent.h
+++ b/src/dawn/native/WaitListEvent.h
@@ -45,7 +45,7 @@
 
 class WaitListEvent : public RefCounted {
   public:
-    WaitListEvent();
+    explicit WaitListEvent(uint64_t requiredSignalCount = 1);
 
     bool IsSignaled() const;
     void Signal();
@@ -65,7 +65,7 @@
     };
 
     mutable std::mutex mMutex;
-    std::atomic_bool mSignaled{false};
+    std::atomic<uint64_t> mRemainingSignalCount;
     std::vector<raw_ptr<SyncWaiter>> mSyncWaiters;
     std::vector<SystemEventPipeSender> mAsyncWaiters;
 };
diff --git a/src/dawn/tests/white_box/WaitListEventTests.cpp b/src/dawn/tests/white_box/WaitListEventTests.cpp
index b97c7c9..bc66255 100644
--- a/src/dawn/tests/white_box/WaitListEventTests.cpp
+++ b/src/dawn/tests/white_box/WaitListEventTests.cpp
@@ -347,5 +347,16 @@
     signaler.join();
 }
 
+// Test events that require multiple signal calls
+TEST(WaitListEventTests, MultipleSignals) {
+    constexpr uint64_t kSignalCount = 10;
+    Ref<WaitListEvent> event1 = AcquireRef(new WaitListEvent(kSignalCount));
+    for (uint64_t i = 0; i < kSignalCount; i++) {
+        EXPECT_FALSE(event1->IsSignaled());
+        event1->Signal();
+    }
+    EXPECT_TRUE(event1->IsSignaled());
+}
+
 }  // namespace
 }  // namespace dawn::native