Implement CallbackTaskManager for Create*PipelineAsync

This patch implements CallbackTask and CallbackTaskManager to store
the callbacks of Create*PipelineAsync().

In the futureCallbackTaskManager will manage all the callbacks that
should be called in Device.Tick().

BUG=dawn:529

Change-Id: I6ad4352371eb44515bc2d85cdc68220c9b758b8e
Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/49060
Reviewed-by: Corentin Wallez <cwallez@chromium.org>
Reviewed-by: Austin Eng <enga@chromium.org>
Commit-Queue: Jiawei Shao <jiawei.shao@intel.com>
diff --git a/src/dawn_native/Device.cpp b/src/dawn_native/Device.cpp
index 713b88e..a2443b2 100644
--- a/src/dawn_native/Device.cpp
+++ b/src/dawn_native/Device.cpp
@@ -20,11 +20,12 @@
 #include "dawn_native/BindGroup.h"
 #include "dawn_native/BindGroupLayout.h"
 #include "dawn_native/Buffer.h"
+#include "dawn_native/CallbackTaskManager.h"
 #include "dawn_native/CommandBuffer.h"
 #include "dawn_native/CommandEncoder.h"
 #include "dawn_native/CompilationMessages.h"
 #include "dawn_native/ComputePipeline.h"
-#include "dawn_native/CreatePipelineAsyncTracker.h"
+#include "dawn_native/CreatePipelineAsyncTask.h"
 #include "dawn_native/DynamicUploader.h"
 #include "dawn_native/ErrorData.h"
 #include "dawn_native/ErrorScope.h"
@@ -125,7 +126,7 @@
         mCaches = std::make_unique<DeviceBase::Caches>();
         mErrorScopeStack = std::make_unique<ErrorScopeStack>();
         mDynamicUploader = std::make_unique<DynamicUploader>(this);
-        mCreatePipelineAsyncTracker = std::make_unique<CreatePipelineAsyncTracker>(this);
+        mCallbackTaskManager = std::make_unique<CallbackTaskManager>();
         mDeprecationWarnings = std::make_unique<DeprecationWarnings>();
         mInternalPipelineStore = std::make_unique<InternalPipelineStore>();
         mPersistentCache = std::make_unique<PersistentCache>(this);
@@ -142,8 +143,11 @@
     void DeviceBase::ShutDownBase() {
         // Skip handling device facilities if they haven't even been created (or failed doing so)
         if (mState != State::BeingCreated) {
-            // Reject all async pipeline creations.
-            mCreatePipelineAsyncTracker->ClearForShutDown();
+            // Call all the callbacks immediately as the device is about to shut down.
+            auto callbackTasks = mCallbackTaskManager->AcquireCallbackTasks();
+            for (std::unique_ptr<CallbackTask>& callbackTask : callbackTasks) {
+                callbackTask->HandleShutDown();
+            }
         }
 
         // Disconnect the device, depending on which state we are currently in.
@@ -188,7 +192,7 @@
         mState = State::Disconnected;
 
         mDynamicUploader = nullptr;
-        mCreatePipelineAsyncTracker = nullptr;
+        mCallbackTaskManager = nullptr;
         mPersistentCache = nullptr;
 
         mEmptyBindGroupLayout = nullptr;
@@ -238,7 +242,10 @@
             }
 
             mQueue->HandleDeviceLoss();
-            mCreatePipelineAsyncTracker->ClearForDeviceLoss();
+            auto callbackTasks = mCallbackTaskManager->AcquireCallbackTasks();
+            for (std::unique_ptr<CallbackTask>& callbackTask : callbackTasks) {
+                callbackTask->HandleDeviceLoss();
+            }
 
             // Still forward device loss errors to the error scopes so they all reject.
             mErrorScopeStack->HandleError(ToWGPUErrorType(type), message);
@@ -766,10 +773,10 @@
         }
 
         Ref<RenderPipelineBase> result = maybeResult.AcquireSuccess();
-        std::unique_ptr<CreateRenderPipelineAsyncTask> request =
-            std::make_unique<CreateRenderPipelineAsyncTask>(std::move(result), "", callback,
-                                                            userdata);
-        mCreatePipelineAsyncTracker->TrackTask(std::move(request), GetPendingCommandSerial());
+        std::unique_ptr<CreateRenderPipelineAsyncCallbackTask> callbackTask =
+            std::make_unique<CreateRenderPipelineAsyncCallbackTask>(std::move(result), "", callback,
+                                                                    userdata);
+        mCallbackTaskManager->AddCallbackTask(std::move(callbackTask));
     }
     RenderBundleEncoder* DeviceBase::APICreateRenderBundleEncoder(
         const RenderBundleEncoderDescriptor* descriptor) {
@@ -951,8 +958,19 @@
             // reclaiming resources one tick earlier.
             mDynamicUploader->Deallocate(mCompletedSerial);
             mQueue->Tick(mCompletedSerial);
+        }
 
-            mCreatePipelineAsyncTracker->Tick(mCompletedSerial);
+        // We have to check mCallbackTaskManager in every Tick because it is not related to any
+        // global serials.
+        if (!mCallbackTaskManager->IsEmpty()) {
+            // If a user calls Queue::Submit inside the callback, then the device will be ticked,
+            // which in turns ticks the tracker, causing reentrance and dead lock here. To prevent
+            // such reentrant call, we remove all the callback tasks from mCallbackTaskManager,
+            // update mCallbackTaskManager, then call all the callbacks.
+            auto callbackTasks = mCallbackTaskManager->AcquireCallbackTasks();
+            for (std::unique_ptr<CallbackTask>& callbackTask : callbackTasks) {
+                callbackTask->Finish();
+            }
         }
 
         return {};
@@ -1158,10 +1176,10 @@
             result = AddOrGetCachedPipeline(resultOrError.AcquireSuccess(), blueprintHash);
         }
 
-        std::unique_ptr<CreateComputePipelineAsyncTask> request =
-            std::make_unique<CreateComputePipelineAsyncTask>(result, errorMessage, callback,
-                                                             userdata);
-        mCreatePipelineAsyncTracker->TrackTask(std::move(request), GetPendingCommandSerial());
+        std::unique_ptr<CreateComputePipelineAsyncCallbackTask> callbackTask =
+            std::make_unique<CreateComputePipelineAsyncCallbackTask>(
+                std::move(result), errorMessage, callback, userdata);
+        mCallbackTaskManager->AddCallbackTask(std::move(callbackTask));
     }
 
     ResultOrError<Ref<PipelineLayoutBase>> DeviceBase::CreatePipelineLayout(