Make async pipeline creation "succeed" even if device destroyed

This follows the spec changes that make lost devices appear to
function as much as possible.

It also significantly simplifies the callback handling code since
we no longer need to special-case device destroyed and device lost
scenarios.

Bug: dawn:1874
Change-Id: If10ebbb0bec9e99f3e7192a5729f80d1639e6c00
Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/136223
Kokoro: Kokoro <noreply+kokoro@google.com>
Reviewed-by: Kai Ninomiya <kainino@chromium.org>
Commit-Queue: Austin Eng <enga@chromium.org>
Reviewed-by: Loko Kung <lokokung@google.com>
diff --git a/src/dawn/native/CreatePipelineAsyncTask.cpp b/src/dawn/native/CreatePipelineAsyncTask.cpp
index e3d8108..5256dae 100644
--- a/src/dawn/native/CreatePipelineAsyncTask.cpp
+++ b/src/dawn/native/CreatePipelineAsyncTask.cpp
@@ -26,89 +26,6 @@
 
 namespace dawn::native {
 
-CreatePipelineAsyncCallbackTaskBase::CreatePipelineAsyncCallbackTaskBase(void* userdata)
-    : mStatus(WGPUCreatePipelineAsyncStatus_Success), mUserData(userdata) {}
-
-CreatePipelineAsyncCallbackTaskBase::CreatePipelineAsyncCallbackTaskBase(
-    WGPUCreatePipelineAsyncStatus status,
-    std::string errorMessage,
-    void* userdata)
-    : mErrorMessage(errorMessage), mStatus(status), mUserData(userdata) {}
-
-CreatePipelineAsyncCallbackTaskBase::~CreatePipelineAsyncCallbackTaskBase() = default;
-
-CreateComputePipelineAsyncCallbackTask::CreateComputePipelineAsyncCallbackTask(
-    Ref<ComputePipelineBase> pipeline,
-    WGPUCreateComputePipelineAsyncCallback callback,
-    void* userdata)
-    : CreatePipelineAsyncCallbackTaskBase(userdata),
-      mPipeline(std::move(pipeline)),
-      mCreateComputePipelineAsyncCallback(callback) {}
-
-CreateComputePipelineAsyncCallbackTask::CreateComputePipelineAsyncCallbackTask(
-    WGPUCreatePipelineAsyncStatus status,
-    std::string errorMessage,
-    WGPUCreateComputePipelineAsyncCallback callback,
-    void* userdata)
-    : CreatePipelineAsyncCallbackTaskBase(status, errorMessage, userdata),
-      mCreateComputePipelineAsyncCallback(callback) {}
-
-CreateComputePipelineAsyncCallbackTask::~CreateComputePipelineAsyncCallbackTask() = default;
-
-void CreateComputePipelineAsyncCallbackTask::FinishImpl() {
-    ASSERT(mCreateComputePipelineAsyncCallback != nullptr);
-    mCreateComputePipelineAsyncCallback(mStatus, ToAPI(mPipeline.Detach()), mErrorMessage.c_str(),
-                                        mUserData);
-}
-
-void CreateComputePipelineAsyncCallbackTask::HandleShutDownImpl() {
-    ASSERT(mCreateComputePipelineAsyncCallback != nullptr);
-    mCreateComputePipelineAsyncCallback(WGPUCreatePipelineAsyncStatus_DeviceDestroyed, nullptr,
-                                        "Device destroyed before callback", mUserData);
-}
-
-void CreateComputePipelineAsyncCallbackTask::HandleDeviceLossImpl() {
-    ASSERT(mCreateComputePipelineAsyncCallback != nullptr);
-    mCreateComputePipelineAsyncCallback(WGPUCreatePipelineAsyncStatus_DeviceLost, nullptr,
-                                        "Device lost before callback", mUserData);
-}
-
-CreateRenderPipelineAsyncCallbackTask::CreateRenderPipelineAsyncCallbackTask(
-    Ref<RenderPipelineBase> pipeline,
-    WGPUCreateRenderPipelineAsyncCallback callback,
-    void* userdata)
-    : CreatePipelineAsyncCallbackTaskBase(userdata),
-      mPipeline(std::move(pipeline)),
-      mCreateRenderPipelineAsyncCallback(callback) {}
-
-CreateRenderPipelineAsyncCallbackTask::CreateRenderPipelineAsyncCallbackTask(
-    WGPUCreatePipelineAsyncStatus status,
-    std::string errorMessage,
-    WGPUCreateRenderPipelineAsyncCallback callback,
-    void* userdata)
-    : CreatePipelineAsyncCallbackTaskBase(status, errorMessage, userdata),
-      mCreateRenderPipelineAsyncCallback(callback) {}
-
-CreateRenderPipelineAsyncCallbackTask::~CreateRenderPipelineAsyncCallbackTask() = default;
-
-void CreateRenderPipelineAsyncCallbackTask::FinishImpl() {
-    ASSERT(mCreateRenderPipelineAsyncCallback != nullptr);
-    mCreateRenderPipelineAsyncCallback(mStatus, ToAPI(mPipeline.Detach()), mErrorMessage.c_str(),
-                                       mUserData);
-}
-
-void CreateRenderPipelineAsyncCallbackTask::HandleShutDownImpl() {
-    ASSERT(mCreateRenderPipelineAsyncCallback != nullptr);
-    mCreateRenderPipelineAsyncCallback(WGPUCreatePipelineAsyncStatus_DeviceDestroyed, nullptr,
-                                       "Device destroyed before callback", mUserData);
-}
-
-void CreateRenderPipelineAsyncCallbackTask::HandleDeviceLossImpl() {
-    ASSERT(mCreateRenderPipelineAsyncCallback != nullptr);
-    mCreateRenderPipelineAsyncCallback(WGPUCreatePipelineAsyncStatus_DeviceLost, nullptr,
-                                       "Device lost before callback", mUserData);
-}
-
 CreateComputePipelineAsyncTask::CreateComputePipelineAsyncTask(
     Ref<ComputePipelineBase> nonInitializedComputePipeline,
     WGPUCreateComputePipelineAsyncCallback callback,
@@ -132,12 +49,8 @@
 
     MaybeError maybeError = mComputePipeline->Initialize();
     if (maybeError.IsError()) {
-        std::unique_ptr<ErrorData> error = maybeError.AcquireError();
-        WGPUCreatePipelineAsyncStatus status =
-            CreatePipelineAsyncStatusFromErrorType(error->GetType());
-        device->GetCallbackTaskManager()->AddCallbackTask(
-            std::make_unique<CreateComputePipelineAsyncCallbackTask>(status, error->GetMessage(),
-                                                                     mCallback, mUserdata));
+        device->AddComputePipelineAsyncCallbackTask(maybeError.AcquireError(), mCallback,
+                                                    mUserdata);
     } else {
         device->AddComputePipelineAsyncCallbackTask(mComputePipeline, mCallback, mUserdata);
     }
@@ -186,12 +99,7 @@
 
     MaybeError maybeError = mRenderPipeline->Initialize();
     if (maybeError.IsError()) {
-        std::unique_ptr<ErrorData> error = maybeError.AcquireError();
-        WGPUCreatePipelineAsyncStatus status =
-            CreatePipelineAsyncStatusFromErrorType(error->GetType());
-        device->GetCallbackTaskManager()->AddCallbackTask(
-            std::make_unique<CreateRenderPipelineAsyncCallbackTask>(status, error->GetMessage(),
-                                                                    mCallback, mUserdata));
+        device->AddRenderPipelineAsyncCallbackTask(maybeError.AcquireError(), mCallback, mUserdata);
     } else {
         device->AddRenderPipelineAsyncCallbackTask(mRenderPipeline, mCallback, mUserdata);
     }
diff --git a/src/dawn/native/CreatePipelineAsyncTask.h b/src/dawn/native/CreatePipelineAsyncTask.h
index d803db5..49df820 100644
--- a/src/dawn/native/CreatePipelineAsyncTask.h
+++ b/src/dawn/native/CreatePipelineAsyncTask.h
@@ -32,57 +32,6 @@
 class ShaderModuleBase;
 struct FlatComputePipelineDescriptor;
 
-struct CreatePipelineAsyncCallbackTaskBase : CallbackTask {
-    explicit CreatePipelineAsyncCallbackTaskBase(void* userData);
-    CreatePipelineAsyncCallbackTaskBase(WGPUCreatePipelineAsyncStatus status,
-                                        std::string errorMessage,
-                                        void* userData);
-    ~CreatePipelineAsyncCallbackTaskBase() override;
-
-  protected:
-    std::string mErrorMessage;
-    WGPUCreatePipelineAsyncStatus mStatus;
-    void* mUserData;
-};
-
-struct CreateComputePipelineAsyncCallbackTask : CreatePipelineAsyncCallbackTaskBase {
-    CreateComputePipelineAsyncCallbackTask(Ref<ComputePipelineBase> pipeline,
-                                           WGPUCreateComputePipelineAsyncCallback callback,
-                                           void* userdata);
-    CreateComputePipelineAsyncCallbackTask(WGPUCreatePipelineAsyncStatus status,
-                                           std::string errorMessage,
-                                           WGPUCreateComputePipelineAsyncCallback callback,
-                                           void* userdata);
-    ~CreateComputePipelineAsyncCallbackTask() override;
-
-  protected:
-    void FinishImpl() override;
-    void HandleShutDownImpl() final;
-    void HandleDeviceLossImpl() final;
-
-    Ref<ComputePipelineBase> mPipeline;
-    WGPUCreateComputePipelineAsyncCallback mCreateComputePipelineAsyncCallback;
-};
-
-struct CreateRenderPipelineAsyncCallbackTask : CreatePipelineAsyncCallbackTaskBase {
-    CreateRenderPipelineAsyncCallbackTask(Ref<RenderPipelineBase> pipeline,
-                                          WGPUCreateRenderPipelineAsyncCallback callback,
-                                          void* userdata);
-    CreateRenderPipelineAsyncCallbackTask(WGPUCreatePipelineAsyncStatus status,
-                                          std::string errorMessage,
-                                          WGPUCreateRenderPipelineAsyncCallback callback,
-                                          void* userdata);
-    ~CreateRenderPipelineAsyncCallbackTask() override;
-
-  protected:
-    void FinishImpl() override;
-    void HandleShutDownImpl() final;
-    void HandleDeviceLossImpl() final;
-
-    Ref<RenderPipelineBase> mPipeline;
-    WGPUCreateRenderPipelineAsyncCallback mCreateRenderPipelineAsyncCallback;
-};
-
 // CreateComputePipelineAsyncTask defines all the inputs and outputs of
 // CreateComputePipelineAsync() tasks, which are the same among all the backends.
 class CreateComputePipelineAsyncTask {
diff --git a/src/dawn/native/Device.cpp b/src/dawn/native/Device.cpp
index a066d81..b55a06b 100644
--- a/src/dawn/native/Device.cpp
+++ b/src/dawn/native/Device.cpp
@@ -646,7 +646,6 @@
         static wgpu::ErrorCallback defaultCallback = [](WGPUErrorType, char const*, void*) {};
         callback = defaultCallback;
     }
-    // TODO(crbug.com/dawn/1122): Call callbacks only on wgpuInstanceProcessEvents
     if (IsLost()) {
         mCallbackTaskManager->AddCallbackTask(
             std::bind(callback, WGPUErrorType_DeviceLost, "GPU device disconnected", userdata));
@@ -1146,20 +1145,29 @@
     TRACE_EVENT1(GetPlatform(), General, "DeviceBase::APICreateComputePipelineAsync", "label",
                  utils::GetLabelForTrace(descriptor->label));
 
-    MaybeError maybeResult = CreateComputePipelineAsync(descriptor, callback, userdata);
+    auto resultOrError = CreateUninitializedComputePipeline(descriptor);
+    // Enqueue the callback directly when an error has been found in the front-end
+    // validation.
+    if (resultOrError.IsError()) {
+        AddComputePipelineAsyncCallbackTask(resultOrError.AcquireError(), callback, userdata);
+        return;
+    }
 
-    // Call the callback directly when a validation error has been found in the front-end
-    // validations. If there is no error, then CreateComputePipelineAsync will call the
-    // callback.
-    if (maybeResult.IsError()) {
-        std::unique_ptr<ErrorData> error = maybeResult.AcquireError();
-        WGPUCreatePipelineAsyncStatus status =
-            CreatePipelineAsyncStatusFromErrorType(error->GetType());
-        // TODO(crbug.com/dawn/1122): Call callbacks only on wgpuInstanceProcessEvents
+    Ref<ComputePipelineBase> uninitializedComputePipeline = resultOrError.AcquireSuccess();
+
+    // Call the callback directly when we can get a cached compute pipeline object.
+    Ref<ComputePipelineBase> cachedComputePipeline =
+        GetCachedComputePipeline(uninitializedComputePipeline.Get());
+    if (cachedComputePipeline.Get() != nullptr) {
         mCallbackTaskManager->AddCallbackTask(
-            [callback, status, message = error->GetMessage(), userdata] {
-                callback(status, nullptr, message.c_str(), userdata);
-            });
+            std::bind(callback, WGPUCreatePipelineAsyncStatus_Success,
+                      ToAPI(cachedComputePipeline.Detach()), "", userdata));
+    } else {
+        // Otherwise we will create the pipeline object in InitializeComputePipelineAsyncImpl(),
+        // where the pipeline object may be initialized asynchronously and the result will be
+        // saved to mCreatePipelineAsyncTracker.
+        InitializeComputePipelineAsyncImpl(std::move(uninitializedComputePipeline), callback,
+                                           userdata);
     }
 }
 PipelineLayoutBase* DeviceBase::APICreatePipelineLayout(
@@ -1192,23 +1200,34 @@
                                               void* userdata) {
     TRACE_EVENT1(GetPlatform(), General, "DeviceBase::APICreateRenderPipelineAsync", "label",
                  utils::GetLabelForTrace(descriptor->label));
-    // TODO(dawn:563): Add validation error context.
-    MaybeError maybeResult = CreateRenderPipelineAsync(descriptor, callback, userdata);
 
-    // Call the callback directly when a validation error has been found in the front-end
-    // validations. If there is no error, then CreateRenderPipelineAsync will call the
-    // callback.
-    if (maybeResult.IsError()) {
-        std::unique_ptr<ErrorData> error = maybeResult.AcquireError();
-        WGPUCreatePipelineAsyncStatus status =
-            CreatePipelineAsyncStatusFromErrorType(error->GetType());
-        // TODO(crbug.com/dawn/1122): Call callbacks only on wgpuInstanceProcessEvents
+    auto resultOrError = CreateUninitializedRenderPipeline(descriptor);
+
+    // Enqueue the callback directly when an error has been found in the front-end
+    // validation.
+    if (resultOrError.IsError()) {
+        AddRenderPipelineAsyncCallbackTask(resultOrError.AcquireError(), callback, userdata);
+        return;
+    }
+
+    Ref<RenderPipelineBase> uninitializedRenderPipeline = resultOrError.AcquireSuccess();
+
+    // Call the callback directly when we can get a cached render pipeline object.
+    Ref<RenderPipelineBase> cachedRenderPipeline =
+        GetCachedRenderPipeline(uninitializedRenderPipeline.Get());
+    if (cachedRenderPipeline != nullptr) {
         mCallbackTaskManager->AddCallbackTask(
-            [callback, status, message = error->GetMessage(), userdata] {
-                callback(status, nullptr, message.c_str(), userdata);
-            });
+            std::bind(callback, WGPUCreatePipelineAsyncStatus_Success,
+                      ToAPI(cachedRenderPipeline.Detach()), "", userdata));
+    } else {
+        // Otherwise we will create the pipeline object in InitializeRenderPipelineAsyncImpl(),
+        // where the pipeline object may be initialized asynchronously and the result will be
+        // saved to mCreatePipelineAsyncTracker.
+        InitializeRenderPipelineAsyncImpl(std::move(uninitializedRenderPipeline), callback,
+                                          userdata);
     }
 }
+
 RenderBundleEncoder* DeviceBase::APICreateRenderBundleEncoder(
     const RenderBundleEncoderDescriptor* descriptor) {
     Ref<RenderBundleEncoder> result;
@@ -1575,20 +1594,9 @@
 
 ResultOrError<Ref<ComputePipelineBase>> DeviceBase::CreateComputePipeline(
     const ComputePipelineDescriptor* descriptor) {
-    DAWN_TRY(ValidateIsAlive());
-    if (IsValidationEnabled()) {
-        DAWN_TRY(ValidateComputePipelineDescriptor(this, descriptor));
-    }
+    Ref<ComputePipelineBase> uninitializedComputePipeline;
+    DAWN_TRY_ASSIGN(uninitializedComputePipeline, CreateUninitializedComputePipeline(descriptor));
 
-    // Ref will keep the pipeline layout alive until the end of the function where
-    // the pipeline will take another reference.
-    Ref<PipelineLayoutBase> layoutRef;
-    ComputePipelineDescriptor appliedDescriptor;
-    DAWN_TRY_ASSIGN(layoutRef, ValidateLayoutAndGetComputePipelineDescriptorWithDefaults(
-                                   this, *descriptor, &appliedDescriptor));
-
-    Ref<ComputePipelineBase> uninitializedComputePipeline =
-        CreateUninitializedComputePipelineImpl(&appliedDescriptor);
     Ref<ComputePipelineBase> cachedComputePipeline =
         GetCachedComputePipeline(uninitializedComputePipeline.Get());
     if (cachedComputePipeline.Get() != nullptr) {
@@ -1618,9 +1626,8 @@
     UNREACHABLE();
 }
 
-MaybeError DeviceBase::CreateComputePipelineAsync(const ComputePipelineDescriptor* descriptor,
-                                                  WGPUCreateComputePipelineAsyncCallback callback,
-                                                  void* userdata) {
+ResultOrError<Ref<ComputePipelineBase>> DeviceBase::CreateUninitializedComputePipeline(
+    const ComputePipelineDescriptor* descriptor) {
     DAWN_TRY(ValidateIsAlive());
     if (IsValidationEnabled()) {
         DAWN_TRY(ValidateComputePipelineDescriptor(this, descriptor));
@@ -1631,45 +1638,19 @@
     DAWN_TRY_ASSIGN(layoutRef, ValidateLayoutAndGetComputePipelineDescriptorWithDefaults(
                                    this, *descriptor, &appliedDescriptor));
 
-    Ref<ComputePipelineBase> uninitializedComputePipeline =
-        CreateUninitializedComputePipelineImpl(&appliedDescriptor);
-
-    // Call the callback directly when we can get a cached compute pipeline object.
-    Ref<ComputePipelineBase> cachedComputePipeline =
-        GetCachedComputePipeline(uninitializedComputePipeline.Get());
-    if (cachedComputePipeline.Get() != nullptr) {
-        // TODO(crbug.com/dawn/1122): Call callbacks only on wgpuInstanceProcessEvents
-        mCallbackTaskManager->AddCallbackTask(
-            std::bind(callback, WGPUCreatePipelineAsyncStatus_Success,
-                      ToAPI(cachedComputePipeline.Detach()), "", userdata));
-    } else {
-        // Otherwise we will create the pipeline object in InitializeComputePipelineAsyncImpl(),
-        // where the pipeline object may be initialized asynchronously and the result will be
-        // saved to mCreatePipelineAsyncTracker.
-        InitializeComputePipelineAsyncImpl(std::move(uninitializedComputePipeline), callback,
-                                           userdata);
-    }
-
-    return {};
+    return CreateUninitializedComputePipelineImpl(&appliedDescriptor);
 }
 
 // This function is overwritten with the async version on the backends that supports
-//  initializing compute pipelines asynchronously.
+// initializing compute pipelines asynchronously.
 void DeviceBase::InitializeComputePipelineAsyncImpl(Ref<ComputePipelineBase> computePipeline,
                                                     WGPUCreateComputePipelineAsyncCallback callback,
                                                     void* userdata) {
     MaybeError maybeError = computePipeline->Initialize();
     if (maybeError.IsError()) {
-        std::unique_ptr<ErrorData> error = maybeError.AcquireError();
-        WGPUCreatePipelineAsyncStatus status =
-            CreatePipelineAsyncStatusFromErrorType(error->GetType());
-        mCallbackTaskManager->AddCallbackTask(
-            std::make_unique<CreateComputePipelineAsyncCallbackTask>(status, error->GetMessage(),
-                                                                     callback, userdata));
+        AddComputePipelineAsyncCallbackTask(maybeError.AcquireError(), callback, userdata);
     } else {
-        mCallbackTaskManager->AddCallbackTask(
-            std::make_unique<CreateComputePipelineAsyncCallbackTask>(
-                AddOrGetCachedComputePipeline(std::move(computePipeline)), callback, userdata));
+        AddComputePipelineAsyncCallbackTask(std::move(computePipeline), callback, userdata);
     }
 }
 
@@ -1680,16 +1661,9 @@
                                                    void* userdata) {
     MaybeError maybeError = renderPipeline->Initialize();
     if (maybeError.IsError()) {
-        std::unique_ptr<ErrorData> error = maybeError.AcquireError();
-        WGPUCreatePipelineAsyncStatus status =
-            CreatePipelineAsyncStatusFromErrorType(error->GetType());
-        mCallbackTaskManager->AddCallbackTask(
-            std::make_unique<CreateRenderPipelineAsyncCallbackTask>(status, error->GetMessage(),
-                                                                    callback, userdata));
+        AddRenderPipelineAsyncCallbackTask(maybeError.AcquireError(), callback, userdata);
     } else {
-        mCallbackTaskManager->AddCallbackTask(
-            std::make_unique<CreateRenderPipelineAsyncCallbackTask>(
-                AddOrGetCachedRenderPipeline(std::move(renderPipeline)), callback, userdata));
+        AddRenderPipelineAsyncCallbackTask(std::move(renderPipeline), callback, userdata);
     }
 }
 
@@ -1733,20 +1707,8 @@
 
 ResultOrError<Ref<RenderPipelineBase>> DeviceBase::CreateRenderPipeline(
     const RenderPipelineDescriptor* descriptor) {
-    DAWN_TRY(ValidateIsAlive());
-    if (IsValidationEnabled()) {
-        DAWN_TRY(ValidateRenderPipelineDescriptor(this, descriptor));
-    }
-
-    // Ref will keep the pipeline layout alive until the end of the function where
-    // the pipeline will take another reference.
-    Ref<PipelineLayoutBase> layoutRef;
-    RenderPipelineDescriptor appliedDescriptor;
-    DAWN_TRY_ASSIGN(layoutRef, ValidateLayoutAndGetRenderPipelineDescriptorWithDefaults(
-                                   this, *descriptor, &appliedDescriptor));
-
-    Ref<RenderPipelineBase> uninitializedRenderPipeline =
-        CreateUninitializedRenderPipelineImpl(&appliedDescriptor);
+    Ref<RenderPipelineBase> uninitializedRenderPipeline;
+    DAWN_TRY_ASSIGN(uninitializedRenderPipeline, CreateUninitializedRenderPipeline(descriptor));
 
     Ref<RenderPipelineBase> cachedRenderPipeline =
         GetCachedRenderPipeline(uninitializedRenderPipeline.Get());
@@ -1758,9 +1720,8 @@
     return AddOrGetCachedRenderPipeline(std::move(uninitializedRenderPipeline));
 }
 
-MaybeError DeviceBase::CreateRenderPipelineAsync(const RenderPipelineDescriptor* descriptor,
-                                                 WGPUCreateRenderPipelineAsyncCallback callback,
-                                                 void* userdata) {
+ResultOrError<Ref<RenderPipelineBase>> DeviceBase::CreateUninitializedRenderPipeline(
+    const RenderPipelineDescriptor* descriptor) {
     DAWN_TRY(ValidateIsAlive());
     if (IsValidationEnabled()) {
         DAWN_TRY(ValidateRenderPipelineDescriptor(this, descriptor));
@@ -1773,26 +1734,7 @@
     DAWN_TRY_ASSIGN(layoutRef, ValidateLayoutAndGetRenderPipelineDescriptorWithDefaults(
                                    this, *descriptor, &appliedDescriptor));
 
-    Ref<RenderPipelineBase> uninitializedRenderPipeline =
-        CreateUninitializedRenderPipelineImpl(&appliedDescriptor);
-
-    // Call the callback directly when we can get a cached render pipeline object.
-    Ref<RenderPipelineBase> cachedRenderPipeline =
-        GetCachedRenderPipeline(uninitializedRenderPipeline.Get());
-    if (cachedRenderPipeline != nullptr) {
-        // TODO(crbug.com/dawn/1122): Call callbacks only on wgpuInstanceProcessEvents
-        mCallbackTaskManager->AddCallbackTask(
-            std::bind(callback, WGPUCreatePipelineAsyncStatus_Success,
-                      ToAPI(cachedRenderPipeline.Detach()), "", userdata));
-    } else {
-        // Otherwise we will create the pipeline object in InitializeRenderPipelineAsyncImpl(),
-        // where the pipeline object may be initialized asynchronously and the result will be
-        // saved to mCreatePipelineAsyncTracker.
-        InitializeRenderPipelineAsyncImpl(std::move(uninitializedRenderPipeline), callback,
-                                          userdata);
-    }
-
-    return {};
+    return CreateUninitializedRenderPipelineImpl(&appliedDescriptor);
 }
 
 ResultOrError<Ref<SamplerBase>> DeviceBase::CreateSampler(const SamplerDescriptor* descriptor) {
@@ -1942,68 +1884,74 @@
 }
 
 void DeviceBase::AddComputePipelineAsyncCallbackTask(
-    Ref<ComputePipelineBase> pipeline,
+    ResultOrError<Ref<ComputePipelineBase>> result,
     WGPUCreateComputePipelineAsyncCallback callback,
     void* userdata) {
-    // CreateComputePipelineAsyncWaitableCallbackTask is declared as an internal class as it
-    // needs to call the private member function DeviceBase::AddOrGetCachedComputePipeline().
-    struct CreateComputePipelineAsyncWaitableCallbackTask final
-        : CreateComputePipelineAsyncCallbackTask {
-        using CreateComputePipelineAsyncCallbackTask::CreateComputePipelineAsyncCallbackTask;
-        void FinishImpl() final {
+    if (result.IsError()) {
+        std::unique_ptr<ErrorData> error = result.AcquireError();
+        WGPUCreatePipelineAsyncStatus status =
+            CreatePipelineAsyncStatusFromErrorType(error->GetType());
+        mCallbackTaskManager->AddCallbackTask(
+            [callback, message = error->GetFormattedMessage(), status, userdata]() {
+                callback(status, nullptr, message.c_str(), userdata);
+            });
+    } else {
+        mCallbackTaskManager->AddCallbackTask([callback, pipeline = result.AcquireSuccess(),
+                                               userdata]() mutable {
             // TODO(dawn:529): call AddOrGetCachedComputePipeline() asynchronously in
             // CreateComputePipelineAsyncTaskImpl::Run() when the front-end pipeline cache is
             // thread-safe.
-            ASSERT(mPipeline != nullptr);
+            ASSERT(pipeline != nullptr);
             {
-                // This is called inside a callback, and no lock will be held by default so we have
-                // to lock now to protect the cache.
-                // Note: we don't lock inside AddOrGetCachedComputePipeline() to avoid deadlock
-                // because many places calling that method might already have the lock held. For
-                // example, APICreateComputePipeline()
-                auto deviceLock(mPipeline->GetDevice()->GetScopedLock());
-                mPipeline = mPipeline->GetDevice()->AddOrGetCachedComputePipeline(mPipeline);
+                // This is called inside a callback, and no lock will be held by default so we
+                // have to lock now to protect the cache. Note: we don't lock inside
+                // AddOrGetCachedComputePipeline() to avoid deadlock because many places calling
+                // that method might already have the lock held. For example,
+                // APICreateComputePipeline()
+                auto deviceLock(pipeline->GetDevice()->GetScopedLock());
+                if (pipeline->GetDevice()->GetState() == State::Alive) {
+                    pipeline =
+                        pipeline->GetDevice()->AddOrGetCachedComputePipeline(std::move(pipeline));
+                }
             }
-
-            CreateComputePipelineAsyncCallbackTask::FinishImpl();
-        }
-    };
-
-    mCallbackTaskManager->AddCallbackTask(
-        std::make_unique<CreateComputePipelineAsyncWaitableCallbackTask>(std::move(pipeline),
-                                                                         callback, userdata));
+            callback(WGPUCreatePipelineAsyncStatus_Success, ToAPI(pipeline.Detach()), "", userdata);
+        });
+    }
 }
 
-void DeviceBase::AddRenderPipelineAsyncCallbackTask(Ref<RenderPipelineBase> pipeline,
+void DeviceBase::AddRenderPipelineAsyncCallbackTask(ResultOrError<Ref<RenderPipelineBase>> result,
                                                     WGPUCreateRenderPipelineAsyncCallback callback,
                                                     void* userdata) {
-    // CreateRenderPipelineAsyncWaitableCallbackTask is declared as an internal class as it
-    // needs to call the private member function DeviceBase::AddOrGetCachedRenderPipeline().
-    struct CreateRenderPipelineAsyncWaitableCallbackTask final
-        : CreateRenderPipelineAsyncCallbackTask {
-        using CreateRenderPipelineAsyncCallbackTask::CreateRenderPipelineAsyncCallbackTask;
-
-        void FinishImpl() final {
+    if (result.IsError()) {
+        std::unique_ptr<ErrorData> error = result.AcquireError();
+        WGPUCreatePipelineAsyncStatus status =
+            CreatePipelineAsyncStatusFromErrorType(error->GetType());
+        mCallbackTaskManager->AddCallbackTask(
+            [callback, message = error->GetFormattedMessage(), status, userdata]() {
+                callback(status, nullptr, message.c_str(), userdata);
+            });
+    } else {
+        mCallbackTaskManager->AddCallbackTask([callback, pipeline = result.AcquireSuccess(),
+                                               userdata]() mutable {
             // TODO(dawn:529): call AddOrGetCachedRenderPipeline() asynchronously in
             // CreateRenderPipelineAsyncTaskImpl::Run() when the front-end pipeline cache is
             // thread-safe.
-            if (mPipeline.Get() != nullptr) {
+            ASSERT(pipeline != nullptr);
+            {
                 // This is called inside a callback, and no lock will be held by default so we have
                 // to lock now to protect the cache.
                 // Note: we don't lock inside AddOrGetCachedRenderPipeline() to avoid deadlock
                 // because many places calling that method might already have the lock held. For
                 // example, APICreateRenderPipeline()
-                auto deviceLock(mPipeline->GetDevice()->GetScopedLock());
-                mPipeline = mPipeline->GetDevice()->AddOrGetCachedRenderPipeline(mPipeline);
+                auto deviceLock(pipeline->GetDevice()->GetScopedLock());
+                if (pipeline->GetDevice()->GetState() == State::Alive) {
+                    pipeline =
+                        pipeline->GetDevice()->AddOrGetCachedRenderPipeline(std::move(pipeline));
+                }
             }
-
-            CreateRenderPipelineAsyncCallbackTask::FinishImpl();
-        }
-    };
-
-    mCallbackTaskManager->AddCallbackTask(
-        std::make_unique<CreateRenderPipelineAsyncWaitableCallbackTask>(std::move(pipeline),
-                                                                        callback, userdata));
+            callback(WGPUCreatePipelineAsyncStatus_Success, ToAPI(pipeline.Detach()), "", userdata);
+        });
+    }
 }
 
 PipelineCompatibilityToken DeviceBase::GetNextPipelineCompatibilityToken() {
diff --git a/src/dawn/native/Device.h b/src/dawn/native/Device.h
index e389ae9..d059fae 100644
--- a/src/dawn/native/Device.h
+++ b/src/dawn/native/Device.h
@@ -239,10 +239,8 @@
         const CommandEncoderDescriptor* descriptor = nullptr);
     ResultOrError<Ref<ComputePipelineBase>> CreateComputePipeline(
         const ComputePipelineDescriptor* descriptor);
-    MaybeError CreateComputePipelineAsync(const ComputePipelineDescriptor* descriptor,
-                                          WGPUCreateComputePipelineAsyncCallback callback,
-                                          void* userdata);
-
+    ResultOrError<Ref<ComputePipelineBase>> CreateUninitializedComputePipeline(
+        const ComputePipelineDescriptor* descriptor);
     ResultOrError<Ref<PipelineLayoutBase>> CreatePipelineLayout(
         const PipelineLayoutDescriptor* descriptor);
     ResultOrError<Ref<QuerySetBase>> CreateQuerySet(const QuerySetDescriptor* descriptor);
@@ -250,9 +248,8 @@
         const RenderBundleEncoderDescriptor* descriptor);
     ResultOrError<Ref<RenderPipelineBase>> CreateRenderPipeline(
         const RenderPipelineDescriptor* descriptor);
-    MaybeError CreateRenderPipelineAsync(const RenderPipelineDescriptor* descriptor,
-                                         WGPUCreateRenderPipelineAsyncCallback callback,
-                                         void* userdata);
+    ResultOrError<Ref<RenderPipelineBase>> CreateUninitializedRenderPipeline(
+        const RenderPipelineDescriptor* descriptor);
     ResultOrError<Ref<SamplerBase>> CreateSampler(const SamplerDescriptor* descriptor = nullptr);
     ResultOrError<Ref<ShaderModuleBase>> CreateShaderModule(
         const ShaderModuleDescriptor* descriptor,
@@ -405,10 +402,10 @@
     CallbackTaskManager* GetCallbackTaskManager() const;
     dawn::platform::WorkerTaskPool* GetWorkerTaskPool() const;
 
-    void AddComputePipelineAsyncCallbackTask(Ref<ComputePipelineBase> pipeline,
+    void AddComputePipelineAsyncCallbackTask(ResultOrError<Ref<ComputePipelineBase>> result,
                                              WGPUCreateComputePipelineAsyncCallback callback,
                                              void* userdata);
-    void AddRenderPipelineAsyncCallbackTask(Ref<RenderPipelineBase> pipeline,
+    void AddRenderPipelineAsyncCallbackTask(ResultOrError<Ref<RenderPipelineBase>> result,
                                             WGPUCreateRenderPipelineAsyncCallback callback,
                                             void* userdata);
 
diff --git a/src/dawn/tests/end2end/CreatePipelineAsyncTests.cpp b/src/dawn/tests/end2end/CreatePipelineAsyncTests.cpp
index c48e410..d46e284 100644
--- a/src/dawn/tests/end2end/CreatePipelineAsyncTests.cpp
+++ b/src/dawn/tests/end2end/CreatePipelineAsyncTests.cpp
@@ -375,8 +375,8 @@
         &csDesc,
         [](WGPUCreatePipelineAsyncStatus status, WGPUComputePipeline returnPipeline,
            const char* message, void* userdata) {
-            EXPECT_EQ(WGPUCreatePipelineAsyncStatus::WGPUCreatePipelineAsyncStatus_DeviceDestroyed,
-                      status);
+            EXPECT_EQ(WGPUCreatePipelineAsyncStatus_Success, status);
+            EXPECT_NE(returnPipeline, nullptr);
 
             CreatePipelineAsyncTask* task = static_cast<CreatePipelineAsyncTask*>(userdata);
             task->computePipeline = wgpu::ComputePipeline::Acquire(returnPipeline);
@@ -384,6 +384,10 @@
             task->message = message;
         },
         &task);
+
+    while (!task.isCompleted) {
+        WaitABit();
+    }
 }
 
 // Verify there is no error when the device is released before the callback of
@@ -407,8 +411,8 @@
         &renderPipelineDescriptor,
         [](WGPUCreatePipelineAsyncStatus status, WGPURenderPipeline returnPipeline,
            const char* message, void* userdata) {
-            EXPECT_EQ(WGPUCreatePipelineAsyncStatus::WGPUCreatePipelineAsyncStatus_DeviceDestroyed,
-                      status);
+            EXPECT_EQ(WGPUCreatePipelineAsyncStatus_Success, status);
+            EXPECT_NE(returnPipeline, nullptr);
 
             CreatePipelineAsyncTask* task = static_cast<CreatePipelineAsyncTask*>(userdata);
             task->renderPipeline = wgpu::RenderPipeline::Acquire(returnPipeline);
@@ -416,6 +420,10 @@
             task->message = message;
         },
         &task);
+
+    while (!task.isCompleted) {
+        WaitABit();
+    }
 }
 
 // Verify there is no error when the device is destroyed before the callback of
@@ -431,8 +439,8 @@
         &csDesc,
         [](WGPUCreatePipelineAsyncStatus status, WGPUComputePipeline returnPipeline,
            const char* message, void* userdata) {
-            EXPECT_EQ(WGPUCreatePipelineAsyncStatus::WGPUCreatePipelineAsyncStatus_DeviceDestroyed,
-                      status);
+            EXPECT_EQ(WGPUCreatePipelineAsyncStatus_Success, status);
+            EXPECT_NE(returnPipeline, nullptr);
 
             CreatePipelineAsyncTask* task = static_cast<CreatePipelineAsyncTask*>(userdata);
             task->computePipeline = wgpu::ComputePipeline::Acquire(returnPipeline);
@@ -441,6 +449,9 @@
         },
         &task);
     DestroyDevice();
+    while (!task.isCompleted) {
+        WaitABit();
+    }
 }
 
 // Verify there is no error when the device is destroyed before the callback of
@@ -464,8 +475,8 @@
         &renderPipelineDescriptor,
         [](WGPUCreatePipelineAsyncStatus status, WGPURenderPipeline returnPipeline,
            const char* message, void* userdata) {
-            EXPECT_EQ(WGPUCreatePipelineAsyncStatus::WGPUCreatePipelineAsyncStatus_DeviceDestroyed,
-                      status);
+            EXPECT_EQ(WGPUCreatePipelineAsyncStatus_Success, status);
+            EXPECT_NE(returnPipeline, nullptr);
 
             CreatePipelineAsyncTask* task = static_cast<CreatePipelineAsyncTask*>(userdata);
             task->renderPipeline = wgpu::RenderPipeline::Acquire(returnPipeline);
@@ -474,6 +485,9 @@
         },
         &task);
     DestroyDevice();
+    while (!task.isCompleted) {
+        WaitABit();
+    }
 }
 
 // Verify the code path of CreateComputePipelineAsync() to directly return the compute pipeline
diff --git a/src/dawn/tests/end2end/DeviceLifetimeTests.cpp b/src/dawn/tests/end2end/DeviceLifetimeTests.cpp
index 7a2cb09..881abfc 100644
--- a/src/dawn/tests/end2end/DeviceLifetimeTests.cpp
+++ b/src/dawn/tests/end2end/DeviceLifetimeTests.cpp
@@ -344,8 +344,9 @@
         &desc,
         [](WGPUCreatePipelineAsyncStatus status, WGPUComputePipeline cPipeline, const char* message,
            void* userdata) {
+            EXPECT_EQ(WGPUCreatePipelineAsyncStatus_Success, status);
+            EXPECT_NE(cPipeline, nullptr);
             wgpu::ComputePipeline::Acquire(cPipeline);
-            EXPECT_EQ(status, WGPUCreatePipelineAsyncStatus_DeviceDestroyed);
         },
         nullptr);
 
@@ -400,21 +401,23 @@
     // Create a pipeline ahead of time so it's in the cache.
     wgpu::ComputePipeline p = device.CreateComputePipeline(&desc);
 
-    bool wire = UsesWire();
+    bool done = false;
     device.CreateComputePipelineAsync(
         &desc,
         [](WGPUCreatePipelineAsyncStatus status, WGPUComputePipeline cPipeline, const char*,
            void* userdata) {
-            const bool wire = *static_cast<bool*>(userdata);
             wgpu::ComputePipeline::Acquire(cPipeline);
-            // On the wire, all callbacks get rejected immediately with once the device is deleted.
-            // In native, expect success since the compilation hits the frontend cache immediately.
-            // TODO(crbug.com/dawn/1122): These callbacks should be made consistent.
-            EXPECT_EQ(status, wire ? WGPUCreatePipelineAsyncStatus_DeviceDestroyed
-                                   : WGPUCreatePipelineAsyncStatus_Success);
+            EXPECT_EQ(status, WGPUCreatePipelineAsyncStatus_Success);
+            EXPECT_NE(cPipeline, nullptr);
+
+            *static_cast<bool*>(userdata) = true;
         },
-        &wire);
+        &done);
     device = nullptr;
+
+    while (!done) {
+        WaitABit();
+    }
 }
 
 // Test that the device can be dropped inside a createPipelineAsync callback which will hit the
@@ -442,6 +445,7 @@
             wgpu::ComputePipeline::Acquire(cPipeline);
             // Success because it hits the frontend cache immediately.
             EXPECT_EQ(status, WGPUCreatePipelineAsyncStatus_Success);
+            EXPECT_NE(cPipeline, nullptr);
 
             static_cast<Userdata*>(userdata)->device = nullptr;
             static_cast<Userdata*>(userdata)->done = true;
@@ -471,8 +475,9 @@
         &desc,
         [](WGPUCreatePipelineAsyncStatus status, WGPUComputePipeline cPipeline, const char* message,
            void* userdata) {
+            EXPECT_EQ(WGPUCreatePipelineAsyncStatus_Success, status);
+            EXPECT_NE(cPipeline, nullptr);
             wgpu::ComputePipeline::Acquire(cPipeline);
-            EXPECT_EQ(status, WGPUCreatePipelineAsyncStatus_DeviceDestroyed);
         },
         nullptr);
 
@@ -482,7 +487,7 @@
     device = nullptr;
 }
 
-// Test that the device can be dropped inside a createPipelineAsync callback which which will race
+// Test that the device can be dropped inside a createPipelineAsync callback which will race
 // with a compilation to add the same pipeline to the frontend cache
 TEST_P(DeviceLifetimeTests, DroppedInsideCreatePipelineAsyncRaceCache) {
     wgpu::ComputePipelineDescriptor desc;
@@ -501,8 +506,9 @@
         &desc,
         [](WGPUCreatePipelineAsyncStatus status, WGPUComputePipeline cPipeline, const char* message,
            void* userdata) {
+            EXPECT_EQ(WGPUCreatePipelineAsyncStatus_Success, status);
+            EXPECT_NE(cPipeline, nullptr);
             wgpu::ComputePipeline::Acquire(cPipeline);
-            EXPECT_EQ(status, WGPUCreatePipelineAsyncStatus_Success);
 
             static_cast<Userdata*>(userdata)->device = nullptr;
             static_cast<Userdata*>(userdata)->done = true;
diff --git a/src/dawn/tests/end2end/DeviceLostTests.cpp b/src/dawn/tests/end2end/DeviceLostTests.cpp
index 68f2155..dddcfb5 100644
--- a/src/dawn/tests/end2end/DeviceLostTests.cpp
+++ b/src/dawn/tests/end2end/DeviceLostTests.cpp
@@ -436,7 +436,7 @@
     LoseDeviceForTesting();
 }
 
-// Test that WGPUCreatePipelineAsyncStatus_DeviceLost can be correctly returned when device is lost
+// Test that WGPUCreatePipelineAsyncStatus_Success is returned when device is lost
 // before the callback of Create*PipelineAsync() is called.
 TEST_P(DeviceLostTest, DeviceLostBeforeCreatePipelineAsyncCallback) {
     wgpu::ShaderModule csModule = utils::CreateShaderModule(device, R"(
@@ -449,7 +449,9 @@
 
     auto callback = [](WGPUCreatePipelineAsyncStatus status, WGPUComputePipeline returnPipeline,
                        const char* message, void* userdata) {
-        EXPECT_EQ(WGPUCreatePipelineAsyncStatus::WGPUCreatePipelineAsyncStatus_DeviceLost, status);
+        EXPECT_EQ(WGPUCreatePipelineAsyncStatus_Success, status);
+        EXPECT_NE(returnPipeline, nullptr);
+        wgpu::ComputePipeline::Acquire(returnPipeline);
     };
 
     device.CreateComputePipelineAsync(&descriptor, callback, nullptr);
diff --git a/src/dawn/tests/unittests/wire/WireCreatePipelineAsyncTests.cpp b/src/dawn/tests/unittests/wire/WireCreatePipelineAsyncTests.cpp
index 2cc9dca..97346a8 100644
--- a/src/dawn/tests/unittests/wire/WireCreatePipelineAsyncTests.cpp
+++ b/src/dawn/tests/unittests/wire/WireCreatePipelineAsyncTests.cpp
@@ -360,7 +360,7 @@
     FlushClient();
 
     EXPECT_CALL(*mockCreateRenderPipelineAsyncCallback,
-                Call(WGPUCreatePipelineAsyncStatus_DeviceDestroyed, nullptr, _, this))
+                Call(WGPUCreatePipelineAsyncStatus_Success, _, _, this))
         .Times(1);
 
     wgpuDeviceRelease(device);
diff --git a/src/dawn/wire/client/Device.cpp b/src/dawn/wire/client/Device.cpp
index cbe204c..ed833e0 100644
--- a/src/dawn/wire/client/Device.cpp
+++ b/src/dawn/wire/client/Device.cpp
@@ -61,16 +61,18 @@
                           request->userdata);
     });
 
-    mCreatePipelineAsyncRequests.CloseAll([](CreatePipelineAsyncRequest* request) {
+    mCreatePipelineAsyncRequests.CloseAll([this](CreatePipelineAsyncRequest* request) {
         if (request->createComputePipelineAsyncCallback != nullptr) {
             request->createComputePipelineAsyncCallback(
-                WGPUCreatePipelineAsyncStatus_DeviceDestroyed, nullptr,
-                "Device destroyed before callback", request->userdata);
+                WGPUCreatePipelineAsyncStatus_Success,
+                ToAPI(GetClient()->Get<ComputePipeline>(request->pipelineObjectID)), "",
+                request->userdata);
         } else {
             ASSERT(request->createRenderPipelineAsyncCallback != nullptr);
             request->createRenderPipelineAsyncCallback(
-                WGPUCreatePipelineAsyncStatus_DeviceDestroyed, nullptr,
-                "Device destroyed before callback", request->userdata);
+                WGPUCreatePipelineAsyncStatus_Success,
+                ToAPI(GetClient()->Get<RenderPipeline>(request->pipelineObjectID)), "",
+                request->userdata);
         }
     });