[dawn][wire] Fixes last dropped reference for buffer unmap in wire.

- Makes the wire Buffer external refcounted so that when the last
  external reference is dropped, we can immediately resolve all
  mapAsync calls.
- Before this change, dropping the last external reference would
  not immediately trigger the callback.

Change-Id: I416d7ea91f020156bfb4b15b13b0d5d09c3e0aa2
Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/210135
Reviewed-by: Corentin Wallez <cwallez@chromium.org>
Reviewed-by: Kai Ninomiya <kainino@chromium.org>
Commit-Queue: Loko Kung <lokokung@google.com>
diff --git a/src/dawn/tests/unittests/validation/BufferValidationTests.cpp b/src/dawn/tests/unittests/validation/BufferValidationTests.cpp
index 32f6562..92d77ed 100644
--- a/src/dawn/tests/unittests/validation/BufferValidationTests.cpp
+++ b/src/dawn/tests/unittests/validation/BufferValidationTests.cpp
@@ -461,6 +461,18 @@
     WaitForAllOperations();
 }
 
+// Test map async but dropping the last reference before the result is ready.
+TEST_P(BufferMappingValidationTest, MapAsync_DroppedBeforeResult) {
+    wgpu::Buffer buffer = CreateBuffer(4);
+
+    MockMapAsyncCallback mockCb;
+    EXPECT_CALL(mockCb, Call(wgpu::MapAsyncStatus::Aborted, HasSubstr("destroyed"))).Times(1);
+
+    buffer.MapAsync(GetParam(), 0, 4, wgpu::CallbackMode::AllowProcessEvents, mockCb.Callback());
+    buffer = nullptr;
+    WaitForAllOperations();
+}
+
 // Test that the MapCallback isn't fired twice when unmap() is called inside the callback
 TEST_P(BufferMappingValidationTest, MapAsync_UnmapCalledInCallback) {
     wgpu::Buffer buffer = CreateBuffer(4);
diff --git a/src/dawn/wire/client/Buffer.cpp b/src/dawn/wire/client/Buffer.cpp
index d08710a..e9a50c9 100644
--- a/src/dawn/wire/client/Buffer.cpp
+++ b/src/dawn/wire/client/Buffer.cpp
@@ -201,7 +201,7 @@
 
     std::optional<WGPUBufferMapAsyncStatus> mStatus;
 
-    // Strong reference to the buffer so that when we call the callback we can pass the buffer.
+    // Reference to the buffer to handle the internal states.
     Ref<Buffer> mBuffer;
 };
 
@@ -443,7 +443,7 @@
                const ObjectHandle& eventManagerHandle,
                Device* device,
                const WGPUBufferDescriptor* descriptor)
-    : ObjectWithEventsBase(params, eventManagerHandle),
+    : RefCountedWithExternalCount<ObjectWithEventsBase>(params, eventManagerHandle),
       mSize(descriptor->size),
       mUsage(static_cast<WGPUBufferUsage>(descriptor->usage)),
       // This flag is for the write handle created by mappedAtCreation
@@ -457,6 +457,10 @@
     ObjectWithEventsBase::DeleteThis();
 }
 
+void Buffer::WillDropLastExternalRef() {
+    SetFutureStatus(WGPUBufferMapAsyncStatus_DestroyedBeforeCallback);
+}
+
 ObjectType Buffer::GetObjectType() const {
     return ObjectType::Buffer;
 }
diff --git a/src/dawn/wire/client/Buffer.h b/src/dawn/wire/client/Buffer.h
index 2abb17c..533635d 100644
--- a/src/dawn/wire/client/Buffer.h
+++ b/src/dawn/wire/client/Buffer.h
@@ -35,7 +35,7 @@
 
 #include "dawn/common/FutureUtils.h"
 #include "dawn/common/Ref.h"
-#include "dawn/common/RefCounted.h"
+#include "dawn/common/RefCountedWithExternalCount.h"
 #include "dawn/wire/WireClient.h"
 #include "dawn/wire/client/ObjectBase.h"
 #include "partition_alloc/pointers/raw_ptr.h"
@@ -44,7 +44,7 @@
 
 class Device;
 
-class Buffer final : public ObjectWithEventsBase {
+class Buffer final : public RefCountedWithExternalCount<ObjectWithEventsBase> {
   public:
     static WGPUBuffer Create(Device* device, const WGPUBufferDescriptor* descriptor);
     static WGPUBuffer CreateError(Device* device, const WGPUBufferDescriptor* descriptor);
@@ -87,6 +87,8 @@
     class MapAsyncEvent;
     class MapAsyncEvent2;
 
+    void WillDropLastExternalRef() override;
+
     // Prepares the callbacks to be called and potentially calls them
     void SetFutureStatus(WGPUBufferMapAsyncStatus status);