[dawn][native] Fix ordering issue for AsyncTasks.

- Make sure that we update the task manager before we signal the tasks.
  This is important because the signal will wake any Waits, which is
  used to implement waiting for all tasks before deleting the task
  manager. Before, it was possible to race, and have a race where the
  main thread was woken and deleted the task manager before the task
  tried to remove itself resulting in a use after free.

Bug: 500378580
Change-Id: I49f3c04904e4447a413ffa6572c91e92cda8ecf9
Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/301515
Commit-Queue: Geoff Lang <geofflang@chromium.org>
Reviewed-by: Geoff Lang <geofflang@chromium.org>
Auto-Submit: Loko Kung <lokokung@google.com>
diff --git a/src/dawn/native/AsyncTask.cpp b/src/dawn/native/AsyncTask.cpp
index 272b33e..e9db474 100644
--- a/src/dawn/native/AsyncTask.cpp
+++ b/src/dawn/native/AsyncTask.cpp
@@ -77,7 +77,13 @@
         state->task = nullptr;
     });
     DAWN_ASSERT(task);
+
+    // Complete the task and update the state of the task manager. Note we need to make sure we
+    // update the state of the task manager before setting the task to Complete to ensure that at
+    // teardown when the task manager is waiting on the tasks, that the tasks no longer have a
+    // reference to the manager anymore.
     task();
+    mTaskManager.ExtractAsDangling()->mTasks.Use([this](auto tasks) { tasks->erase(this); });
 
     // Update the state, notify all waiting threads, and grab the completion callbacks to call them
     // outside the lock scope.
@@ -91,9 +97,6 @@
     for (auto completionCallback : completionCallbacks) {
         completionCallback();
     }
-
-    // Update the state of the task manager.
-    mTaskManager->mTasks.Use([this](auto tasks) { tasks->erase(this); });
 }
 
 ErrorGeneratingAsyncTask::ErrorGeneratingAsyncTask(AsyncTaskManager* taskManager,