[wgpu-headers] Updates mapAsync callback to match wgpu-header.

- Introduces 2nd userdata.
- Updates status and include message. Note that we are using a new
  status enum all together to (1) standardize the enum naming with the
  other async entry points, and (2) make it easier to update users.
- Note that usages will be updated in a follow up together with
  onSubmittedWorkDone since these callbacks have some dependencies.

Bug: 42241461
Change-Id: Ic42ee38213a2df907c02001f9636dbbdca86ef37
Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/188444
Commit-Queue: Loko Kung <lokokung@google.com>
Reviewed-by: Austin Eng <enga@chromium.org>
Reviewed-by: Kai Ninomiya <kainino@chromium.org>
diff --git a/generator/templates/api_cpp.h b/generator/templates/api_cpp.h
index d7d62f3..63e097d 100644
--- a/generator/templates/api_cpp.h
+++ b/generator/templates/api_cpp.h
@@ -280,7 +280,7 @@
                 {{as_annotated_cppType(arg)}}{{ ", "}}
             {%- endif -%}
         {%- endfor -%}
-    {{as_cppType(types["callback mode"].name)}} mode, F callback, T userdata) const
+    {{as_cppType(types["callback mode"].name)}} callbackMode, F callback, T userdata) const
 {%- endmacro %}
 
 //* This rendering macro should ONLY be used for callback info type functions.
@@ -313,7 +313,7 @@
                 {{as_annotated_cppType(arg)}}{{ ", "}}
             {%- endif -%}
         {%- endfor -%}
-    {{as_cppType(types["callback mode"].name)}} mode, L callback) const
+    {{as_cppType(types["callback mode"].name)}} callbackMode, L callback) const
 {%- endmacro %}
 
 //* This rendering macro should NOT be used for callback info type functions.
@@ -366,7 +366,7 @@
         {% set CallbackInfoType = (method.arguments|last).type %}
         {% set CallbackType = (CallbackInfoType.members|first).type %}
         {{as_cType(CallbackInfoType.name)}} callbackInfo = {};
-        callbackInfo.mode = static_cast<{{as_cType(types["callback mode"].name)}}>(mode);
+        callbackInfo.mode = static_cast<{{as_cType(types["callback mode"].name)}}>(callbackMode);
         callbackInfo.callback = [](
             {%- for arg in CallbackType.arguments -%}
                 {{as_annotated_cType(arg)}}{{", "}}
@@ -402,7 +402,7 @@
         );
 
         {{as_cType(CallbackInfoType.name)}} callbackInfo = {};
-        callbackInfo.mode = static_cast<{{as_cType(types["callback mode"].name)}}>(mode);
+        callbackInfo.mode = static_cast<{{as_cType(types["callback mode"].name)}}>(callbackMode);
         if constexpr (std::is_convertible_v<L, F*>) {
             callbackInfo.callback = [](
             {%- for arg in CallbackType.arguments -%}
diff --git a/src/dawn/dawn.json b/src/dawn/dawn.json
index f1cf9a6..8188df3 100644
--- a/src/dawn/dawn.json
+++ b/src/dawn/dawn.json
@@ -618,6 +618,18 @@
                 ]
             },
             {
+                "name": "map async 2",
+                "_comment": "TODO(crbug.com/dawn/2021): This is dawn/emscripten-only until we rename it to replace the old API. See bug for details.",
+                "tags": ["dawn", "emscripten"],
+                "returns": "future",
+                "args": [
+                    {"name": "mode", "type": "map mode"},
+                    {"name": "offset", "type": "size_t"},
+                    {"name": "size", "type": "size_t"},
+                    {"name": "callback info", "type": "buffer map callback info 2"}
+                ]
+            },
+            {
                 "name": "get mapped range",
                 "returns": "void *",
                 "args": [
@@ -697,6 +709,13 @@
             {"name": "userdata", "type": "void *"}
         ]
     },
+    "buffer map callback 2": {
+        "category": "callback function",
+        "args": [
+            {"name": "status", "type": "map async status"},
+            {"name": "message", "type": "char", "annotation": "const*", "length": "strlen"}
+        ]
+    },
     "buffer map callback info": {
         "category": "structure",
         "extensible": "in",
@@ -706,6 +725,12 @@
             {"name": "userdata", "type": "void *"}
         ]
     },
+    "buffer map callback info 2": {
+        "category": "callback info",
+        "members": [
+            {"name": "callback", "type": "buffer map callback 2"}
+        ]
+    },
     "buffer map async status": {
         "category": "enum",
         "emscripten_no_enum_table": true,
@@ -722,6 +747,17 @@
             {"value": 9, "name": "size out of range"}
         ]
     },
+    "map async status": {
+        "category": "enum",
+        "emscripten_no_enum_table": true,
+        "values": [
+            {"value": 0, "name": "success"},
+            {"value": 1, "name": "instance dropped"},
+            {"value": 2, "name": "error"},
+            {"value": 3, "name": "aborted"},
+            {"value": 4, "name": "unknown"}
+        ]
+    },
     "buffer map state": {
         "category": "enum",
         "values": [
diff --git a/src/dawn/dawn_wire.json b/src/dawn/dawn_wire.json
index 88ffb7e..da0aab9 100644
--- a/src/dawn/dawn_wire.json
+++ b/src/dawn/dawn_wire.json
@@ -37,7 +37,8 @@
             { "name": "future", "type": "future" },
             { "name": "mode", "type": "map mode" },
             { "name": "offset", "type": "uint64_t"},
-            { "name": "size", "type": "uint64_t"}
+            { "name": "size", "type": "uint64_t"},
+            { "name": "userdata count", "type": "uint8_t", "_comment": "TODO(crbug.com/dawn/2509): Remove this once Chromium overrides the correct functions in the proc table."}
         ],
         "buffer update mapped data": [
             { "name": "buffer id", "type": "ObjectId", "id_type": "buffer" },
@@ -127,6 +128,9 @@
             { "name": "event manager", "type": "ObjectHandle" },
             { "name": "future", "type": "future" },
             { "name": "status", "type": "buffer map async status" },
+            { "name": "status2", "type": "map async status" },
+            { "name": "message", "type": "char", "annotation": "const*", "length": "strlen", "optional": true },
+            { "name": "userdata count", "type": "uint8_t", "_comment": "TODO(crbug.com/dawn/2509): Remove this once Chromium overrides the correct functions in the proc table."},
             { "name": "read data update info length", "type": "uint64_t" },
             { "name": "read data update info", "type": "uint8_t", "annotation": "const*", "length": "read data update info length", "skip_serialize": true }
         ],
@@ -219,6 +223,7 @@
             "AdapterRequestDevice2",
             "BufferMapAsync",
             "BufferMapAsyncF",
+            "BufferMapAsync2",
             "BufferGetConstMappedRange",
             "BufferGetMappedRange",
             "BufferGetMapState",
diff --git a/src/dawn/native/Buffer.cpp b/src/dawn/native/Buffer.cpp
index 974ac9b..9cdffb0 100644
--- a/src/dawn/native/Buffer.cpp
+++ b/src/dawn/native/Buffer.cpp
@@ -33,6 +33,7 @@
 #include <string>
 #include <utility>
 
+#include "absl/container/flat_hash_map.h"
 #include "absl/strings/str_format.h"
 #include "dawn/common/Alloc.h"
 #include "dawn/common/Assert.h"
@@ -48,6 +49,7 @@
 #include "dawn/native/ObjectType_autogen.h"
 #include "dawn/native/PhysicalDevice.h"
 #include "dawn/native/Queue.h"
+#include "dawn/native/SystemEvent.h"
 #include "dawn/native/ValidationUtils_autogen.h"
 #include "dawn/platform/DawnPlatform.h"
 #include "dawn/platform/tracing/TraceEvent.h"
@@ -165,7 +167,21 @@
 
 }  // anonymous namespace
 
-struct BufferBase::MapAsyncEvent final : public EventManager::TrackedEvent {
+struct BufferBase::MapAsyncEvent : public EventManager::TrackedEvent {
+    MapAsyncEvent(wgpu::CallbackMode mode, QueueBase* queue, ExecutionSerial serial)
+        : TrackedEvent(mode, queue, serial) {}
+    // TODO(crbug.com/42241006): Note that currently this constructor is used for errors because a
+    // dropped device can result in failure when trying to get the queue. While using this fixes the
+    // problem, it causes an unintentional side-effect where it is possible to run into a
+    // UnsupportedMixedSource error on WaitAny even if we are only using a single device with
+    // MapAsync calls. This will be fixed once we correctly implement mixed sources.
+    explicit MapAsyncEvent(wgpu::CallbackMode mode)
+        : TrackedEvent(mode, SystemEvent::CreateSignaled()) {}
+
+    virtual void UnmapEarly(wgpu::BufferMapAsyncStatus status) = 0;
+};
+
+struct BufferBase::MapAsyncEvent1 final : public BufferBase::MapAsyncEvent {
     // MapAsyncEvent stores a raw pointer to the buffer so that it can
     // update the buffer's map state when it completes.
     // If the map completes early (error, unmap, destroy), then the buffer
@@ -180,11 +196,11 @@
     raw_ptr<void> mUserdata;
 
     // Create an event backed by the given queue execution serial.
-    MapAsyncEvent(DeviceBase* device,
-                  BufferBase* buffer,
-                  const BufferMapCallbackInfo& callbackInfo,
-                  ExecutionSerial serial)
-        : TrackedEvent(callbackInfo.mode, device->GetQueue(), serial),
+    MapAsyncEvent1(DeviceBase* device,
+                   BufferBase* buffer,
+                   const BufferMapCallbackInfo& callbackInfo,
+                   ExecutionSerial serial)
+        : MapAsyncEvent(callbackInfo.mode, device->GetQueue(), serial),
           mBufferOrEarlyStatus(buffer),
           mCallback(callbackInfo.callback),
           mUserdata(callbackInfo.userdata) {
@@ -193,10 +209,10 @@
     }
 
     // Create an event that's ready at creation (for errors, etc.)
-    MapAsyncEvent(DeviceBase* device,
-                  const BufferMapCallbackInfo& callbackInfo,
-                  wgpu::BufferMapAsyncStatus earlyStatus)
-        : TrackedEvent(callbackInfo.mode, device->GetQueue(), kBeginningOfGPUTime),
+    MapAsyncEvent1(DeviceBase* device,
+                   const BufferMapCallbackInfo& callbackInfo,
+                   wgpu::BufferMapAsyncStatus earlyStatus)
+        : MapAsyncEvent(callbackInfo.mode, device->GetQueue(), kBeginningOfGPUTime),
           mBufferOrEarlyStatus(earlyStatus),
           mCallback(callbackInfo.callback),
           mUserdata(callbackInfo.userdata) {
@@ -204,7 +220,7 @@
                                  uint64_t(kBeginningOfGPUTime));
     }
 
-    ~MapAsyncEvent() override { EnsureComplete(EventCompletionType::Shutdown); }
+    ~MapAsyncEvent1() override { EnsureComplete(EventCompletionType::Shutdown); }
 
     void Complete(EventCompletionType completionType) override {
         if (const auto* queueAndSerial = std::get_if<QueueAndSerial>(&GetCompletionData())) {
@@ -245,11 +261,128 @@
     // This can race with Complete such that the early status is ignored, but this is OK
     // because we will still unmap the buffer. It will be as-if the application called
     // Unmap/Destroy just after the map event completed.
-    void UnmapEarly(wgpu::BufferMapAsyncStatus status) {
+    void UnmapEarly(wgpu::BufferMapAsyncStatus status) override {
         mBufferOrEarlyStatus.Use([&](auto bufferOrEarlyStatus) { *bufferOrEarlyStatus = status; });
     }
 };
 
+struct BufferBase::MapAsyncEvent2 final : public BufferBase::MapAsyncEvent {
+    // MapAsyncEvent stores a raw pointer to the buffer so that it can update the buffer's map state
+    // when it completes. If the map completes early (error, unmap, destroy), then the buffer is no
+    // longer needed and we store the early status instead. The raw pointer is safe because the
+    // early status is set to destroyed before the buffer is dropped. Note: this could be an atomic
+    // + spin lock on a sentinel enum if the mutex cost is high.
+    struct BufferErrorData {
+        WGPUMapAsyncStatus status;
+        std::string message;
+    };
+    MutexProtected<std::variant<BufferBase*, BufferErrorData>> mBufferOrError;
+
+    WGPUBufferMapCallback2 mCallback;
+    raw_ptr<void> mUserdata1;
+    raw_ptr<void> mUserdata2;
+
+    // Create an event backed by the given queue execution serial.
+    MapAsyncEvent2(DeviceBase* device,
+                   BufferBase* buffer,
+                   const WGPUBufferMapCallbackInfo2& callbackInfo,
+                   ExecutionSerial serial)
+        : MapAsyncEvent(static_cast<wgpu::CallbackMode>(callbackInfo.mode),
+                        device->GetQueue(),
+                        serial),
+          mBufferOrError(buffer),
+          mCallback(callbackInfo.callback),
+          mUserdata1(callbackInfo.userdata1),
+          mUserdata2(callbackInfo.userdata2) {
+        TRACE_EVENT_ASYNC_BEGIN0(device->GetPlatform(), General, "Buffer::APIMapAsync",
+                                 uint64_t(serial));
+    }
+
+    // Create an event that's ready at creation (for errors, etc.)
+    MapAsyncEvent2(DeviceBase* device,
+                   const WGPUBufferMapCallbackInfo2& callbackInfo,
+                   const std::string& message)
+        : MapAsyncEvent(static_cast<wgpu::CallbackMode>(callbackInfo.mode)),
+          mBufferOrError(BufferErrorData{WGPUMapAsyncStatus_Error, message}),
+          mCallback(callbackInfo.callback),
+          mUserdata1(callbackInfo.userdata1),
+          mUserdata2(callbackInfo.userdata2) {
+        TRACE_EVENT_ASYNC_BEGIN0(device->GetPlatform(), General, "Buffer::APIMapAsync",
+                                 uint64_t(kBeginningOfGPUTime));
+    }
+
+    ~MapAsyncEvent2() override { EnsureComplete(EventCompletionType::Shutdown); }
+
+    void Complete(EventCompletionType completionType) override {
+        if (const auto* queueAndSerial = std::get_if<QueueAndSerial>(&GetCompletionData())) {
+            TRACE_EVENT_ASYNC_END0(queueAndSerial->queue->GetDevice()->GetPlatform(), General,
+                                   "Buffer::APIMapAsync",
+                                   uint64_t(queueAndSerial->completionSerial));
+        }
+
+        void* userdata1 = mUserdata1.ExtractAsDangling();
+        void* userdata2 = mUserdata2.ExtractAsDangling();
+
+        if (completionType == EventCompletionType::Shutdown) {
+            mCallback(WGPUMapAsyncStatus_InstanceDropped,
+                      "A valid external Instance reference no longer exists.", userdata1,
+                      userdata2);
+            return;
+        }
+
+        bool error = false;
+        BufferErrorData pendingErrorData;
+        Ref<MapAsyncEvent> pendingMapEvent;
+
+        // Lock the buffer / error. This may race with UnmapEarly which occurs when the buffer is
+        // unmapped or destroyed.
+        mBufferOrError.Use([&](auto bufferOrError) {
+            if (auto* errorData = std::get_if<BufferErrorData>(&*bufferOrError)) {
+                // Assign the early error, if it was set.
+                pendingErrorData = *errorData;
+                error = true;
+            } else if (auto** buffer = std::get_if<BufferBase*>(&*bufferOrError)) {
+                // Set the buffer state to Mapped if this pending map succeeded.
+                // TODO(crbug.com/dawn/831): in order to be thread safe, mutation of the
+                // state and pending map event needs to be atomic w.r.t. UnmapInternal.
+                DAWN_ASSERT((*buffer)->mState == BufferState::PendingMap);
+                (*buffer)->mState = BufferState::Mapped;
+
+                pendingMapEvent = std::move((*buffer)->mPendingMapEvent);
+            }
+        });
+        if (error) {
+            DAWN_ASSERT(!pendingErrorData.message.empty());
+            mCallback(pendingErrorData.status, pendingErrorData.message.c_str(), userdata1,
+                      userdata2);
+        } else {
+            mCallback(WGPUMapAsyncStatus_Success, nullptr, userdata1, userdata2);
+        }
+    }
+
+    // Set the buffer early status because it was unmapped early due to Unmap or Destroy.
+    // This can race with Complete such that the early status is ignored, but this is OK
+    // because we will still unmap the buffer. It will be as-if the application called
+    // Unmap/Destroy just after the map event completed.
+    void UnmapEarly(wgpu::BufferMapAsyncStatus status) override {
+        auto StatusToMessage = [](wgpu::BufferMapAsyncStatus status) {
+            switch (status) {
+                case wgpu::BufferMapAsyncStatus::DeviceLost:
+                    return "Device was lost.";
+                case wgpu::BufferMapAsyncStatus::DestroyedBeforeCallback:
+                    return "Buffer was destroyed before mapping was resolved.";
+                case wgpu::BufferMapAsyncStatus::UnmappedBeforeCallback:
+                    return "Buffer was unmapped before mapping was resolved.";
+                default:
+                    DAWN_UNREACHABLE();
+            }
+        };
+        mBufferOrError.Use([&](auto bufferOrError) {
+            *bufferOrError = BufferErrorData{WGPUMapAsyncStatus_Aborted, StatusToMessage(status)};
+        });
+    }
+};
+
 ResultOrError<UnpackedPtr<BufferDescriptor>> ValidateBufferDescriptor(
     DeviceBase* device,
     const BufferDescriptor* descriptor) {
@@ -623,14 +756,65 @@
         }();
 
         if (earlyStatus) {
-            event = AcquireRef(new MapAsyncEvent(GetDevice(), callbackInfo, *earlyStatus));
+            event = AcquireRef(new MapAsyncEvent1(GetDevice(), callbackInfo, *earlyStatus));
         } else {
             mMapMode = mode;
             mMapOffset = offset;
             mMapSize = size;
             mState = BufferState::PendingMap;
             mPendingMapEvent =
-                AcquireRef(new MapAsyncEvent(GetDevice(), this, callbackInfo, mLastUsageSerial));
+                AcquireRef(new MapAsyncEvent1(GetDevice(), this, callbackInfo, mLastUsageSerial));
+            event = mPendingMapEvent;
+        }
+    }
+
+    FutureID futureID = GetInstance()->GetEventManager()->TrackEvent(std::move(event));
+    return {futureID};
+}
+
+Future BufferBase::APIMapAsync2(wgpu::MapMode mode,
+                                size_t offset,
+                                size_t size,
+                                const WGPUBufferMapCallbackInfo2& callbackInfo) {
+    // TODO(crbug.com/dawn/2052): Once we always return a future, change this to log to the instance
+    // (note, not raise a validation error to the device) and return the null future.
+    DAWN_ASSERT(callbackInfo.nextInChain == nullptr);
+
+    Ref<EventManager::TrackedEvent> event;
+    {
+        // TODO(crbug.com/dawn/831) Manually acquire device lock instead of relying on code-gen for
+        // re-entrancy.
+        auto deviceLock(GetDevice()->GetScopedLock());
+
+        // Handle the defaulting of size required by WebGPU, even if in webgpu_cpp.h it is not
+        // possible to default the function argument (because there is the callback later in the
+        // argument list)
+        if ((size == wgpu::kWholeMapSize) && (offset <= mSize)) {
+            size = mSize - offset;
+        }
+
+        MaybeError maybeError = [&]() -> MaybeError {
+            DAWN_INVALID_IF(mState == BufferState::PendingMap,
+                            "%s already has an outstanding map pending.", this);
+            WGPUBufferMapAsyncStatus status;
+            DAWN_TRY(ValidateMapAsync(mode, offset, size, &status));
+            DAWN_TRY(MapAsyncImpl(mode, offset, size));
+            return {};
+        }();
+
+        if (maybeError.IsError()) {
+            auto error = maybeError.AcquireError();
+            event = AcquireRef(new MapAsyncEvent2(GetDevice(), callbackInfo, error->GetMessage()));
+            [[maybe_unused]] bool hadError = GetDevice()->ConsumedError(
+                std::move(error), "calling %s.MapAsync(%s, %u, %u, ...).", this, mode, offset,
+                size);
+        } else {
+            mMapMode = mode;
+            mMapOffset = offset;
+            mMapSize = size;
+            mState = BufferState::PendingMap;
+            mPendingMapEvent =
+                AcquireRef(new MapAsyncEvent2(GetDevice(), this, callbackInfo, mLastUsageSerial));
             event = mPendingMapEvent;
         }
     }
diff --git a/src/dawn/native/Buffer.h b/src/dawn/native/Buffer.h
index e1378ce..24fb24b 100644
--- a/src/dawn/native/Buffer.h
+++ b/src/dawn/native/Buffer.h
@@ -128,6 +128,10 @@
                         size_t offset,
                         size_t size,
                         const BufferMapCallbackInfo& callbackInfo);
+    Future APIMapAsync2(wgpu::MapMode mode,
+                        size_t offset,
+                        size_t size,
+                        const WGPUBufferMapCallbackInfo2& callbackInfo);
     void* APIGetMappedRange(size_t offset, size_t size);
     const void* APIGetConstMappedRange(size_t offset, size_t size);
     void APIUnmap();
@@ -190,6 +194,8 @@
     size_t mMapSize = 0;
 
     struct MapAsyncEvent;
+    struct MapAsyncEvent1;
+    struct MapAsyncEvent2;
     Ref<MapAsyncEvent> mPendingMapEvent;
 };
 
diff --git a/src/dawn/wire/client/Buffer.cpp b/src/dawn/wire/client/Buffer.cpp
index 6c166a4..c9b1a6f 100644
--- a/src/dawn/wire/client/Buffer.cpp
+++ b/src/dawn/wire/client/Buffer.cpp
@@ -29,6 +29,7 @@
 
 #include <functional>
 #include <limits>
+#include <string>
 #include <utility>
 
 #include "dawn/wire/BufferConsumer_impl.h"
@@ -209,6 +210,138 @@
     raw_ptr<Buffer> mBuffer;
 };
 
+class Buffer::MapAsyncEvent2 : public TrackedEvent {
+  public:
+    static constexpr EventType kType = EventType::MapAsync;
+
+    MapAsyncEvent2(const WGPUBufferMapCallbackInfo2& callbackInfo, Buffer* buffer)
+        : TrackedEvent(callbackInfo.mode),
+          mCallback(callbackInfo.callback),
+          mUserdata1(callbackInfo.userdata1),
+          mUserdata2(callbackInfo.userdata2),
+          mBuffer(buffer) {
+        DAWN_ASSERT(buffer != nullptr);
+        mBuffer->AddRef();
+    }
+
+    ~MapAsyncEvent2() override { mBuffer.ExtractAsDangling()->Release(); }
+
+    EventType GetType() override { return kType; }
+
+    bool IsPendingRequest(FutureID futureID) {
+        return mBuffer->mPendingMapRequest && mBuffer->mPendingMapRequest->futureID == futureID;
+    }
+
+    WireResult ReadyHook(FutureID futureID,
+                         WGPUMapAsyncStatus status,
+                         const char* message,
+                         uint64_t readDataUpdateInfoLength = 0,
+                         const uint8_t* readDataUpdateInfo = nullptr) {
+        if (status != WGPUMapAsyncStatus_Success) {
+            mStatus = status;
+            mMessage = message;
+            return WireResult::Success;
+        }
+
+        // If the request was already aborted via the client side, we don't need to actually do
+        // anything, so just return success.
+        if (!IsPendingRequest(futureID)) {
+            return WireResult::Success;
+        }
+
+        auto FailRequest = [this](const char* message) -> WireResult {
+            mStatus = WGPUMapAsyncStatus_Unknown;
+            mMessage = message;
+            return WireResult::FatalError;
+        };
+
+        mStatus = status;
+        DAWN_ASSERT(message == nullptr);
+        const auto& pending = mBuffer->mPendingMapRequest.value();
+        if (!pending.type) {
+            return FailRequest("Invalid map call without a specified mapping type.");
+        }
+        switch (*pending.type) {
+            case MapRequestType::Read: {
+                if (readDataUpdateInfoLength > std::numeric_limits<size_t>::max()) {
+                    // This is the size of data deserialized from the command stream, which must be
+                    // CPU-addressable.
+                    return FailRequest("Invalid data size returned from the server.");
+                }
+
+                // Update user map data with server returned data
+                if (!mBuffer->mReadHandle->DeserializeDataUpdate(
+                        readDataUpdateInfo, static_cast<size_t>(readDataUpdateInfoLength),
+                        pending.offset, pending.size)) {
+                    return FailRequest("Failed to deserialize data returned from the server.");
+                }
+                mBuffer->mMappedData = const_cast<void*>(mBuffer->mReadHandle->GetData());
+                break;
+            }
+            case MapRequestType::Write: {
+                mBuffer->mMappedData = mBuffer->mWriteHandle->GetData();
+                break;
+            }
+        }
+        mBuffer->mMappedOffset = pending.offset;
+        mBuffer->mMappedSize = pending.size;
+
+        return WireResult::Success;
+    }
+
+  private:
+    void CompleteImpl(FutureID futureID, EventCompletionType completionType) override {
+        if (completionType == EventCompletionType::Shutdown) {
+            mStatus = WGPUMapAsyncStatus_InstanceDropped;
+            mMessage = "A valid external Instance reference no longer exists.";
+        }
+
+        auto Callback = [this]() {
+            if (mCallback) {
+                mCallback(mStatus, mMessage ? mMessage->c_str() : nullptr,
+                          mUserdata1.ExtractAsDangling(), mUserdata2.ExtractAsDangling());
+            }
+        };
+
+        if (!IsPendingRequest(futureID)) {
+            DAWN_ASSERT(mStatus != WGPUMapAsyncStatus_Success);
+            return Callback();
+        }
+
+        if (mStatus == WGPUMapAsyncStatus_Success) {
+            if (mBuffer->mIsDeviceAlive.expired()) {
+                // If the device lost its last ref before this callback was resolved, we want to
+                // overwrite the status. This is necessary because otherwise dropping the last
+                // device reference could race w.r.t what this callback would see.
+                mStatus = WGPUMapAsyncStatus_Aborted;
+                mMessage = "Buffer was destroyed before mapping was resolved.";
+                return Callback();
+            }
+            DAWN_ASSERT(mBuffer->mPendingMapRequest->type);
+            switch (*mBuffer->mPendingMapRequest->type) {
+                case MapRequestType::Read:
+                    mBuffer->mMappedState = MapState::MappedForRead;
+                    break;
+                case MapRequestType::Write:
+                    mBuffer->mMappedState = MapState::MappedForWrite;
+                    break;
+            }
+        }
+        mBuffer->mPendingMapRequest = std::nullopt;
+        return Callback();
+    }
+
+    WGPUBufferMapCallback2 mCallback;
+    raw_ptr<void> mUserdata1;
+    raw_ptr<void> mUserdata2;
+
+    WGPUMapAsyncStatus mStatus;
+    std::optional<std::string> mMessage;
+
+    // Strong reference to the buffer so that when we call the callback we can pass the buffer.
+    raw_ptr<Buffer> mBuffer;
+};
+
 // static
 WGPUBuffer Buffer::Create(Device* device, const WGPUBufferDescriptor* descriptor) {
     Client* wireClient = device->GetClient();
@@ -324,8 +457,32 @@
     if (!mPendingMapRequest) {
         return;
     }
-    DAWN_CHECK(GetEventManager().SetFutureReady<MapAsyncEvent>(mPendingMapRequest->futureID,
-                                                               status) == WireResult::Success);
+
+    FutureID futureID = mPendingMapRequest->futureID;
+    bool isNewEntryPoint = mPendingMapRequest->isNewEntryPoint;
+    mPendingMapRequest = std::nullopt;
+
+    if (isNewEntryPoint) {
+        auto [newStatus, message] =
+            [](WGPUBufferMapAsyncStatus status) -> std::pair<WGPUMapAsyncStatus, const char*> {
+            switch (status) {
+                case WGPUBufferMapAsyncStatus_DestroyedBeforeCallback:
+                    return {WGPUMapAsyncStatus_Aborted,
+                            "Buffer was destroyed before mapping was resolved."};
+                case WGPUBufferMapAsyncStatus_UnmappedBeforeCallback:
+                    return {WGPUMapAsyncStatus_Aborted,
+                            "Buffer was unmapped before mapping was resolved."};
+                default:
+                    DAWN_UNREACHABLE();
+            }
+        }(status);
+
+        DAWN_CHECK(GetEventManager().SetFutureReady<MapAsyncEvent2>(futureID, newStatus, message) ==
+                   WireResult::Success);
+    } else {
+        DAWN_CHECK(GetEventManager().SetFutureReady<MapAsyncEvent>(futureID, status) ==
+                   WireResult::Success);
+    }
 }
 
 void Buffer::MapAsync(WGPUMapModeFlags mode,
@@ -372,7 +529,7 @@
         mapMode = MapRequestType::Write;
     }
 
-    mPendingMapRequest = {futureIDInternal, offset, size, mapMode};
+    mPendingMapRequest = {futureIDInternal, offset, size, mapMode, false};
 
     // Serialize the command to send to the server.
     BufferMapAsyncCmd cmd;
@@ -382,6 +539,56 @@
     cmd.mode = mode;
     cmd.offset = offset;
     cmd.size = size;
+    cmd.userdataCount = 1;
+
+    client->SerializeCommand(cmd);
+    return {futureIDInternal};
+}
+
+WGPUFuture Buffer::MapAsync2(WGPUMapModeFlags mode,
+                             size_t offset,
+                             size_t size,
+                             const WGPUBufferMapCallbackInfo2& callbackInfo) {
+    DAWN_ASSERT(GetRefcount() != 0);
+
+    Client* client = GetClient();
+    auto [futureIDInternal, tracked] =
+        GetEventManager().TrackEvent(std::make_unique<MapAsyncEvent2>(callbackInfo, this));
+    if (!tracked) {
+        return {futureIDInternal};
+    }
+
+    if (mPendingMapRequest) {
+        [[maybe_unused]] auto id = GetEventManager().SetFutureReady<MapAsyncEvent2>(
+            futureIDInternal, WGPUMapAsyncStatus_Error,
+            "Buffer already has an outstanding map pending.");
+        return {futureIDInternal};
+    }
+
+    // Handle the defaulting of size required by WebGPU.
+    if ((size == WGPU_WHOLE_MAP_SIZE) && (offset <= mSize)) {
+        size = mSize - offset;
+    }
+
+    // Set up the request structure that will hold information while this mapping is in flight.
+    std::optional<MapRequestType> mapMode;
+    if (mode & WGPUMapMode_Read) {
+        mapMode = MapRequestType::Read;
+    } else if (mode & WGPUMapMode_Write) {
+        mapMode = MapRequestType::Write;
+    }
+
+    mPendingMapRequest = {futureIDInternal, offset, size, mapMode, true};
+
+    // Serialize the command to send to the server.
+    BufferMapAsyncCmd cmd;
+    cmd.bufferId = GetWireId();
+    cmd.eventManagerHandle = GetEventManagerHandle();
+    cmd.future = {futureIDInternal};
+    cmd.mode = mode;
+    cmd.offset = offset;
+    cmd.size = size;
+    cmd.userdataCount = 2;
 
     client->SerializeCommand(cmd);
     return {futureIDInternal};
@@ -390,11 +597,20 @@
 WireResult Client::DoBufferMapAsyncCallback(ObjectHandle eventManager,
                                             WGPUFuture future,
                                             WGPUBufferMapAsyncStatus status,
+                                            WGPUMapAsyncStatus status2,
+                                            const char* message,
+                                            uint8_t userdataCount,
                                             uint64_t readDataUpdateInfoLength,
                                             const uint8_t* readDataUpdateInfo) {
-    return GetEventManager(eventManager)
-        .SetFutureReady<Buffer::MapAsyncEvent>(future.id, status, readDataUpdateInfoLength,
-                                               readDataUpdateInfo);
+    if (userdataCount == 1) {
+        return GetEventManager(eventManager)
+            .SetFutureReady<Buffer::MapAsyncEvent>(future.id, status, readDataUpdateInfoLength,
+                                                   readDataUpdateInfo);
+    } else {
+        return GetEventManager(eventManager)
+            .SetFutureReady<Buffer::MapAsyncEvent2>(future.id, status2, message,
+                                                    readDataUpdateInfoLength, readDataUpdateInfo);
+    }
 }
 
 void* Buffer::GetMappedRange(size_t offset, size_t size) {
diff --git a/src/dawn/wire/client/Buffer.h b/src/dawn/wire/client/Buffer.h
index e1abeac..c82b286 100644
--- a/src/dawn/wire/client/Buffer.h
+++ b/src/dawn/wire/client/Buffer.h
@@ -63,6 +63,10 @@
                          size_t offset,
                          size_t size,
                          const WGPUBufferMapCallbackInfo& callbackInfo);
+    WGPUFuture MapAsync2(WGPUMapModeFlags mode,
+                         size_t offset,
+                         size_t size,
+                         const WGPUBufferMapCallbackInfo2& callbackInfo);
     void* GetMappedRange(size_t offset, size_t size);
     const void* GetConstMappedRange(size_t offset, size_t size);
     void Unmap();
@@ -78,6 +82,7 @@
   private:
     friend class Client;
     class MapAsyncEvent;
+    class MapAsyncEvent2;
 
     // Prepares the callbacks to be called and potentially calls them
     void SetFutureStatus(WGPUBufferMapAsyncStatus status);
@@ -103,6 +108,9 @@
         // Because validation for request type is validated via the backend, we use an optional type
         // here. This is nullopt when an invalid request type is passed to the wire.
         std::optional<MapRequestType> type;
+        // Currently needs an additional boolean to indicate which entry point was used for the map.
+        // TODO(crbug.com/42241461): Remove this once we don't need to support both on the wire.
+        bool isNewEntryPoint = false;
     };
     enum class MapState {
         Unmapped,
diff --git a/src/dawn/wire/server/Server.h b/src/dawn/wire/server/Server.h
index a244b9d..207c519 100644
--- a/src/dawn/wire/server/Server.h
+++ b/src/dawn/wire/server/Server.h
@@ -129,6 +129,7 @@
     uint64_t offset;
     uint64_t size;
     WGPUMapModeFlags mode;
+    uint8_t userdataCount;
 };
 
 struct ErrorScopeUserdata : CallbackUserdata {
@@ -254,6 +255,9 @@
                                WGPUErrorType type,
                                const char* message);
     void OnBufferMapAsyncCallback(MapUserdata* userdata, WGPUBufferMapAsyncStatus status);
+    void OnBufferMapAsyncCallback2(MapUserdata* userdata,
+                                   WGPUMapAsyncStatus status,
+                                   const char* message);
     void OnQueueWorkDone(QueueWorkDoneUserdata* userdata, WGPUQueueWorkDoneStatus status);
     void OnCreateComputePipelineAsyncCallback(CreatePipelineAsyncUserData* userdata,
                                               WGPUCreatePipelineAsyncStatus status,
diff --git a/src/dawn/wire/server/ServerBuffer.cpp b/src/dawn/wire/server/ServerBuffer.cpp
index 3a3642f..73f1963 100644
--- a/src/dawn/wire/server/ServerBuffer.cpp
+++ b/src/dawn/wire/server/ServerBuffer.cpp
@@ -70,7 +70,8 @@
                                     WGPUFuture future,
                                     WGPUMapModeFlags mode,
                                     uint64_t offset64,
-                                    uint64_t size64) {
+                                    uint64_t size64,
+                                    uint8_t userdataCount) {
     // These requests are just forwarded to the buffer, with userdata containing what the
     // client will require in the return command.
     std::unique_ptr<MapUserdata> userdata = MakeUserdata<MapUserdata>();
@@ -79,6 +80,7 @@
     userdata->bufferObj = buffer->handle;
     userdata->future = future;
     userdata->mode = mode;
+    userdata->userdataCount = userdataCount;
 
     // Make sure that the deserialized offset and size are no larger than
     // std::numeric_limits<size_t>::max() so that they are CPU-addressable, and size is not
@@ -100,8 +102,16 @@
     userdata->offset = offset;
     userdata->size = size;
 
-    mProcs.bufferMapAsync(buffer->handle, mode, offset, size,
-                          ForwardToServer<&Server::OnBufferMapAsyncCallback>, userdata.release());
+    if (userdataCount == 1) {
+        mProcs.bufferMapAsync(buffer->handle, mode, offset, size,
+                              ForwardToServer<&Server::OnBufferMapAsyncCallback>,
+                              userdata.release());
+    } else {
+        mProcs.bufferMapAsync2(
+            buffer->handle, mode, offset, size,
+            {nullptr, WGPUCallbackMode_AllowSpontaneous,
+             ForwardToServer2<&Server::OnBufferMapAsyncCallback2>, userdata.release(), nullptr});
+    }
 
     return WireResult::Success;
 }
@@ -229,8 +239,68 @@
     cmd.eventManager = data->eventManager;
     cmd.future = data->future;
     cmd.status = status;
+    cmd.message = nullptr;
     cmd.readDataUpdateInfoLength = 0;
     cmd.readDataUpdateInfo = nullptr;
+    cmd.userdataCount = 1;
+
+    const void* readData = nullptr;
+    size_t readDataUpdateInfoLength = 0;
+    if (isSuccess) {
+        if (isRead) {
+            // Get the serialization size of the message to initialize ReadHandle data.
+            readData = mProcs.bufferGetConstMappedRange(data->bufferObj, data->offset, data->size);
+            readDataUpdateInfoLength =
+                buffer->readHandle->SizeOfSerializeDataUpdate(data->offset, data->size);
+            cmd.readDataUpdateInfoLength = readDataUpdateInfoLength;
+        } else {
+            DAWN_ASSERT(data->mode & WGPUMapMode_Write);
+            // The in-flight map request returned successfully.
+            buffer->mapWriteState = BufferMapWriteState::Mapped;
+            // Set the target of the WriteHandle to the mapped buffer data.
+            // writeHandle Target always refers to the buffer base address.
+            // but we call getMappedRange exactly with the range of data that is potentially
+            // modified (i.e. we don't want getMappedRange(0, wholeBufferSize) if only a
+            // subset of the buffer is actually mapped) in case the implementation does some
+            // range tracking.
+            buffer->writeHandle->SetTarget(static_cast<uint8_t*>(mProcs.bufferGetMappedRange(
+                                               data->bufferObj, data->offset, data->size)) -
+                                           data->offset);
+        }
+    }
+
+    SerializeCommand(cmd, CommandExtension{readDataUpdateInfoLength, [&](char* readHandleBuffer) {
+                                               if (isSuccess && isRead) {
+                                                   // The in-flight map request returned
+                                                   // successfully.
+                                                   buffer->readHandle->SerializeDataUpdate(
+                                                       readData, data->offset, data->size,
+                                                       readHandleBuffer);
+                                               }
+                                           }});
+}
+
+void Server::OnBufferMapAsyncCallback2(MapUserdata* data,
+                                       WGPUMapAsyncStatus status,
+                                       const char* message) {
+    // Skip sending the callback if the buffer has already been destroyed.
+    Known<WGPUBuffer> buffer;
+    if (Objects<WGPUBuffer>().Get(data->buffer.id, &buffer) != WireResult::Success ||
+        buffer->generation != data->buffer.generation) {
+        return;
+    }
+
+    bool isRead = data->mode & WGPUMapMode_Read;
+    bool isSuccess = status == WGPUMapAsyncStatus_Success;
+
+    ReturnBufferMapAsyncCallbackCmd cmd;
+    cmd.eventManager = data->eventManager;
+    cmd.future = data->future;
+    cmd.status2 = status;
+    cmd.message = message;
+    cmd.readDataUpdateInfoLength = 0;
+    cmd.readDataUpdateInfo = nullptr;
+    cmd.userdataCount = 2;
 
     const void* readData = nullptr;
     size_t readDataUpdateInfoLength = 0;
diff --git a/tools/android/BUILD.gn b/tools/android/BUILD.gn
index ccec004..b7caaae 100644
--- a/tools/android/BUILD.gn
+++ b/tools/android/BUILD.gn
@@ -113,6 +113,7 @@
     "java/android/dawn/InstanceFeatures.kt",
     "java/android/dawn/Limits.kt",
     "java/android/dawn/LoadOp.kt",
+    "java/android/dawn/MapAsyncStatus.kt",
     "java/android/dawn/MapMode.kt",
     "java/android/dawn/MipmapFilterMode.kt",
     "java/android/dawn/MultisampleState.kt",