[dawn][headers] Add GetLostFuture to native and wire.

Bug: 377753478
Change-Id: I1ca3fb594388b409ed46e396966830fce6c272ff
Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/214157
Reviewed-by: Kai Ninomiya <kainino@chromium.org>
Commit-Queue: Loko Kung <lokokung@google.com>
diff --git a/src/dawn/dawn.json b/src/dawn/dawn.json
index aa32f4d..2f6e976 100644
--- a/src/dawn/dawn.json
+++ b/src/dawn/dawn.json
@@ -1535,8 +1535,7 @@
             },
             {
                 "name": "get lost future",
-                "returns": "future",
-                "tags": ["emscripten"]
+                "returns": "future"
             },
             {
                 "name": "has feature",
diff --git a/src/dawn/dawn_wire.json b/src/dawn/dawn_wire.json
index 240a9d0..97b23ea 100644
--- a/src/dawn/dawn_wire.json
+++ b/src/dawn/dawn_wire.json
@@ -246,6 +246,7 @@
             "DeviceGetAdapterInfo",
             "DeviceGetFeatures",
             "DeviceGetLimits",
+            "DeviceGetLostFuture",
             "DeviceHasFeature",
             "DevicePopErrorScope",
             "DevicePopErrorScopeF",
diff --git a/src/dawn/native/Device.cpp b/src/dawn/native/Device.cpp
index 0641cc1..bfc65ed 100644
--- a/src/dawn/native/Device.cpp
+++ b/src/dawn/native/Device.cpp
@@ -300,6 +300,7 @@
       mNextPipelineCompatibilityToken(1) {
     DAWN_ASSERT(descriptor);
 
+    DAWN_ASSERT(mLostEvent);
     mLostEvent->mDevice = this;
 
 #if defined(DAWN_ENABLE_ASSERTS)
@@ -410,7 +411,6 @@
     // We need to explicitly release the Queue before we complete the destructor so that the
     // Queue does not get destroyed after the Device.
     mQueue = nullptr;
-    mLostEvent = nullptr;
 }
 
 MaybeError DeviceBase::Initialize(Ref<QueueBase> defaultQueue) {
@@ -568,12 +568,7 @@
     // Skip handling device facilities if they haven't even been created (or failed doing so)
     if (mState != State::BeingCreated) {
         // The device is being destroyed so it will be lost, call the application callback.
-        if (mLostEvent != nullptr) {
-            mLostEvent->mReason = wgpu::DeviceLostReason::Destroyed;
-            mLostEvent->mMessage = "Device was destroyed.";
-            GetInstance()->GetEventManager()->SetFutureReady(mLostEvent.Get());
-            mLostEvent = nullptr;
-        }
+        HandleDeviceLost(wgpu::DeviceLostReason::Destroyed, "Device was destroyed.");
 
         // Call all the callbacks immediately as the device is about to shut down.
         // TODO(crbug.com/dawn/826): Cancel the tasks that are in flight if possible.
@@ -655,9 +650,18 @@
     Destroy();
 }
 
+void DeviceBase::HandleDeviceLost(wgpu::DeviceLostReason reason, std::string_view message) {
+    // Always use the first message and reason for device lost.
+    if (mLostEvent->mMessage.empty()) {
+        mLostEvent->mReason = reason;
+        mLostEvent->mMessage = message;
+        GetInstance()->GetEventManager()->SetFutureReady(mLostEvent.Get());
+    }
+}
+
 void DeviceBase::HandleError(std::unique_ptr<ErrorData> error,
                              InternalErrorType additionalAllowedErrors,
-                             WGPUDeviceLostReason lostReason) {
+                             wgpu::DeviceLostReason lostReason) {
     AppendDebugLayerMessages(error.get());
 
     InternalErrorType type = error->GetType();
@@ -713,13 +717,7 @@
         // The device was lost, schedule the application callback's execution.
         // Note: we don't invoke the callbacks directly here because it could cause re-entrances ->
         // possible deadlock.
-        if (mLostEvent != nullptr) {
-            mLostEvent->mReason = FromAPI(lostReason);
-            mLostEvent->mMessage = messageStr;
-            GetInstance()->GetEventManager()->SetFutureReady(mLostEvent.Get());
-            mLostEvent = nullptr;
-        }
-
+        HandleDeviceLost(lostReason, messageStr);
         mQueue->HandleDeviceLoss();
 
         // TODO(crbug.com/dawn/826): Cancel the tasks that are in flight if possible.
@@ -883,7 +881,7 @@
     // Note that since we are passing None as the allowedErrors, an additional message will be
     // appended noting that the error was unexpected. Since this call is for testing only it is not
     // too important, but useful for users to understand where the extra message is coming from.
-    HandleError(DAWN_INTERNAL_ERROR(std::string(message)), InternalErrorType::None, ToAPI(reason));
+    HandleError(DAWN_INTERNAL_ERROR(std::string(message)), InternalErrorType::None, reason);
 }
 
 DeviceBase::State DeviceBase::GetState() const {
@@ -1907,6 +1905,10 @@
     return mAdapter->APIGetInfo(adapterInfo);
 }
 
+Future DeviceBase::APIGetLostFuture() const {
+    return mLostEvent->GetFuture();
+}
+
 void DeviceBase::APIInjectError(wgpu::ErrorType type, StringView message) {
     if (ConsumedError(ValidateErrorType(type))) {
         return;
diff --git a/src/dawn/native/Device.h b/src/dawn/native/Device.h
index 2b0708f..b2c145b 100644
--- a/src/dawn/native/Device.h
+++ b/src/dawn/native/Device.h
@@ -123,7 +123,7 @@
     // users as the respective error rather than causing a device loss instead.
     void HandleError(std::unique_ptr<ErrorData> error,
                      InternalErrorType additionalAllowedErrors = InternalErrorType::None,
-                     WGPUDeviceLostReason lost_reason = WGPUDeviceLostReason_Unknown);
+                     wgpu::DeviceLostReason lost_reason = wgpu::DeviceLostReason::Unknown);
 
     MaybeError ValidateObject(const ApiObjectBase* object) const;
 
@@ -291,6 +291,7 @@
     void APIGetFeatures(wgpu::SupportedFeatures* features) const;
     void APIGetFeatures(SupportedFeatures* features) const;
     wgpu::Status APIGetAdapterInfo(AdapterInfo* adapterInfo) const;
+    Future APIGetLostFuture() const;
     void APIInjectError(wgpu::ErrorType type, StringView message);
     bool APITick();
     void APIValidateTextureDescriptor(const TextureDescriptor* desc);
@@ -528,6 +529,7 @@
     // ErrorSink implementation
     void ConsumeError(std::unique_ptr<ErrorData> error,
                       InternalErrorType additionalAllowedErrors = InternalErrorType::None) override;
+    void HandleDeviceLost(wgpu::DeviceLostReason reason, std::string_view message);
 
     bool HasPendingTasks();
     bool IsDeviceIdle();
diff --git a/src/dawn/native/EventManager.cpp b/src/dawn/native/EventManager.cpp
index 7a82ec6..95f556b 100644
--- a/src/dawn/native/EventManager.cpp
+++ b/src/dawn/native/EventManager.cpp
@@ -541,6 +541,10 @@
     DAWN_ASSERT(mCompleted);
 }
 
+Future EventManager::TrackedEvent::GetFuture() const {
+    return {mFutureID};
+}
+
 const EventManager::TrackedEvent::CompletionData& EventManager::TrackedEvent::GetCompletionData()
     const {
     return mCompletionData;
diff --git a/src/dawn/native/EventManager.h b/src/dawn/native/EventManager.h
index a53f47d..b0984af 100644
--- a/src/dawn/native/EventManager.h
+++ b/src/dawn/native/EventManager.h
@@ -130,7 +130,8 @@
     // EventCompletionType::Shutdown.
     ~TrackedEvent() override;
 
-    class WaitRef;
+    Future GetFuture() const;
+
     // Events may be one of two types:
     // - A queue and the ExecutionSerial after which the event will be completed.
     //   Used for queue completion.
diff --git a/src/dawn/tests/end2end/MultithreadTests.cpp b/src/dawn/tests/end2end/MultithreadTests.cpp
index d388689..b8c3bb5 100644
--- a/src/dawn/tests/end2end/MultithreadTests.cpp
+++ b/src/dawn/tests/end2end/MultithreadTests.cpp
@@ -191,6 +191,40 @@
     });
 }
 
+// Test that waiting for a device lost after it's lost does not block.
+TEST_P(MultithreadTests, Device_WaitForDroppedAfterDropped) {
+    auto future = device.GetLostFuture();
+
+    LoseDeviceForTesting();
+    EXPECT_EQ(GetInstance().WaitAny(future, 0), wgpu::WaitStatus::Success);
+
+    EXPECT_EQ(future.id, device.GetLostFuture().id);
+}
+
+// Test that we can wait for a device lost on another thread.
+TEST_P(MultithreadTests, Device_WaitForDroppedInAnotherThread) {
+    // TODO(crbug.com/dawn/1779): This test seems to cause flakiness in other sampling tests on
+    // NVIDIA.
+    DAWN_SUPPRESS_TEST_IF(IsD3D12() && IsNvidia());
+
+    enum class Step {
+        Begin,
+        Waiting,
+    };
+
+    LockStep<Step> lockStep(Step::Begin);
+    std::thread waitThread([&] {
+        auto future = device.GetLostFuture();
+        EXPECT_EQ(GetInstance().WaitAny(future, 0), wgpu::WaitStatus::TimedOut);
+        lockStep.Signal(Step::Waiting);
+        EXPECT_EQ(GetInstance().WaitAny(future, UINT64_MAX), wgpu::WaitStatus::Success);
+    });
+
+    lockStep.Wait(Step::Waiting);
+    LoseDeviceForTesting();
+    waitThread.join();
+}
+
 // Test that multiple buffers being created and mapped on multiple threads won't interfere with
 // each other.
 TEST_P(MultithreadTests, Buffers_MapInParallel) {
diff --git a/src/dawn/wire/client/Adapter.cpp b/src/dawn/wire/client/Adapter.cpp
index 2358d6c..2302b6d 100644
--- a/src/dawn/wire/client/Adapter.cpp
+++ b/src/dawn/wire/client/Adapter.cpp
@@ -298,7 +298,7 @@
     cmd.eventManagerHandle = GetEventManagerHandle();
     cmd.future = {futureIDInternal};
     cmd.deviceObjectHandle = device->GetWireHandle();
-    cmd.deviceLostFuture = device->GetDeviceLostFuture();
+    cmd.deviceLostFuture = device->GetLostFuture();
     cmd.descriptor = &wireDescriptor;
     cmd.userdataCount = 1;
 
@@ -330,7 +330,7 @@
     cmd.eventManagerHandle = GetEventManagerHandle();
     cmd.future = {futureIDInternal};
     cmd.deviceObjectHandle = device->GetWireHandle();
-    cmd.deviceLostFuture = device->GetDeviceLostFuture();
+    cmd.deviceLostFuture = device->GetLostFuture();
     cmd.descriptor = &wireDescriptor;
     cmd.userdataCount = 2;
 
diff --git a/src/dawn/wire/client/Device.cpp b/src/dawn/wire/client/Device.cpp
index 927dcc9..4677663 100644
--- a/src/dawn/wire/client/Device.cpp
+++ b/src/dawn/wire/client/Device.cpp
@@ -172,9 +172,10 @@
     EventType GetType() override { return kType; }
 
     WireResult ReadyHook(FutureID futureID, WGPUDeviceLostReason reason, WGPUStringView message) {
-        mReason = reason;
-        mMessage = ToString(message);
-        mDevice->mDeviceLostInfo.futureID = kNullFutureID;
+        if (mMessage.empty()) {
+            mReason = reason;
+            mMessage = ToString(message);
+        }
         return WireResult::Success;
     }
 
@@ -318,22 +319,18 @@
 }
 
 void Device::HandleDeviceLost(WGPUDeviceLostReason reason, WGPUStringView message) {
-    FutureID futureID = GetDeviceLostFuture().id;
-    if (futureID != kNullFutureID) {
-        DAWN_CHECK(GetEventManager().SetFutureReady<DeviceLostEvent>(futureID, reason, message) ==
-                   WireResult::Success);
-    }
+    FutureID futureID = GetLostFuture().id;
+    DAWN_CHECK(GetEventManager().SetFutureReady<DeviceLostEvent>(futureID, reason, message) ==
+               WireResult::Success);
     mIsAlive = false;
 }
 
-WGPUFuture Device::GetDeviceLostFuture() {
+WGPUFuture Device::GetLostFuture() {
     // Lazily track the device lost event so that event ordering w.r.t RequestDevice is correct.
     if (mDeviceLostInfo.event != nullptr) {
-        auto [deviceLostFutureIDInternal, tracked] =
+        auto [deviceLostFutureIDInternal, _] =
             GetEventManager().TrackEvent(std::move(mDeviceLostInfo.event));
-        if (tracked) {
-            mDeviceLostInfo.futureID = deviceLostFutureIDInternal;
-        }
+        mDeviceLostInfo.futureID = deviceLostFutureIDInternal;
     }
     return {mDeviceLostInfo.futureID};
 }
diff --git a/src/dawn/wire/client/Device.h b/src/dawn/wire/client/Device.h
index 9d7ae58..5f7a1a4 100644
--- a/src/dawn/wire/client/Device.h
+++ b/src/dawn/wire/client/Device.h
@@ -58,7 +58,6 @@
     void SetFeatures(const WGPUFeatureName* features, uint32_t featuresCount);
 
     bool IsAlive() const;
-    WGPUFuture GetDeviceLostFuture();
 
     void HandleError(WGPUErrorType errorType, WGPUStringView message);
     void HandleLogging(WGPULoggingType loggingType, WGPUStringView message);
@@ -94,6 +93,7 @@
         const WGPUCreateRenderPipelineAsyncCallbackInfo2& callbackInfo);
 
     WGPUStatus GetLimits(WGPUSupportedLimits* limits) const;
+    WGPUFuture GetLostFuture();
     bool HasFeature(WGPUFeatureName feature) const;
     void GetFeatures(WGPUSupportedFeatures* features) const;
     WGPUStatus GetAdapterInfo(WGPUAdapterInfo* info) const;