MapAsync update: keep read/write handle through lifetime of mappable buffers

Change dawn read/write handle for buffer mapping to be created at buffer
creation time instead of at mapAsync time. Update related buffer mapping
tests and wire tests.

Bug: dawn:773
Change-Id: I7dd423c94e1bc15cfe561ea33ec9e348ddf2bfe0
Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/51164
Reviewed-by: Austin Eng <enga@chromium.org>
Commit-Queue: Shrek Shao <shrekshao@google.com>
diff --git a/dawn_wire.json b/dawn_wire.json
index 5a71c22..406ef4e 100644
--- a/dawn_wire.json
+++ b/dawn_wire.json
@@ -20,24 +20,26 @@
     "commands": {
         "buffer map async": [
             { "name": "buffer id", "type": "ObjectId" },
-            { "name": "request serial", "type": "uint32_t" },
+            { "name": "request serial", "type": "uint64_t" },
             { "name": "mode", "type": "map mode" },
             { "name": "offset", "type": "uint64_t"},
-            { "name": "size", "type": "uint64_t"},
-            { "name": "handle create info length", "type": "uint64_t" },
-            { "name": "handle create info", "type": "uint8_t", "annotation": "const*", "length": "handle create info length", "skip_serialize": true}
+            { "name": "size", "type": "uint64_t"}
         ],
         "buffer update mapped data": [
             { "name": "buffer id", "type": "ObjectId" },
-            { "name": "write flush info length", "type": "uint64_t" },
-            { "name": "write flush info", "type": "uint8_t", "annotation": "const*", "length": "write flush info length", "skip_serialize": true}
+            { "name": "write data update info length", "type": "uint64_t" },
+            { "name": "write data update info", "type": "uint8_t", "annotation": "const*", "length": "write data update info length", "skip_serialize": true},
+            { "name": "offset", "type": "uint64_t"},
+            { "name": "size", "type": "uint64_t"}
         ],
         "device create buffer": [
             { "name": "device id", "type": "ObjectId" },
             { "name": "descriptor", "type": "buffer descriptor", "annotation": "const*" },
             { "name": "result", "type": "ObjectHandle", "handle_type": "buffer" },
-            { "name": "handle create info length", "type": "uint64_t" },
-            { "name": "handle create info", "type": "uint8_t", "annotation": "const*", "length": "handle create info length", "skip_serialize": true}
+            { "name": "read handle create info length", "type": "uint64_t" },
+            { "name": "read handle create info", "type": "uint8_t", "annotation": "const*", "length": "read handle create info length", "skip_serialize": true},
+            { "name": "write handle create info length", "type": "uint64_t" },
+            { "name": "write handle create info", "type": "uint8_t", "annotation": "const*", "length": "write handle create info length", "skip_serialize": true}
         ],
         "device create compute pipeline async": [
             { "name": "device id", "type": "ObjectId" },
@@ -87,10 +89,10 @@
     "return commands": {
         "buffer map async callback": [
             { "name": "buffer", "type": "ObjectHandle", "handle_type": "buffer" },
-            { "name": "request serial", "type": "uint32_t" },
+            { "name": "request serial", "type": "uint64_t" },
             { "name": "status", "type": "uint32_t" },
-            { "name": "read initial data info length", "type": "uint64_t" },
-            { "name": "read initial data info", "type": "uint8_t", "annotation": "const*", "length": "read initial data info length", "skip_serialize": true }
+            { "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 }
         ],
         "device create compute pipeline async callback": [
             { "name": "device", "type": "ObjectHandle", "handle_type": "device" },
diff --git a/src/dawn_wire/WireServer.cpp b/src/dawn_wire/WireServer.cpp
index bb3d7ba..bad5957 100644
--- a/src/dawn_wire/WireServer.cpp
+++ b/src/dawn_wire/WireServer.cpp
@@ -68,8 +68,10 @@
 
         MemoryTransferService::WriteHandle::~WriteHandle() = default;
 
-        void MemoryTransferService::WriteHandle::SetTarget(void* data, size_t dataLength) {
+        void MemoryTransferService::WriteHandle::SetTarget(void* data) {
             mTargetData = data;
+        }
+        void MemoryTransferService::WriteHandle::SetDataLength(size_t dataLength) {
             mDataLength = dataLength;
         }
     }  // namespace server
diff --git a/src/dawn_wire/client/Buffer.cpp b/src/dawn_wire/client/Buffer.cpp
index 6c9c70e..b735280 100644
--- a/src/dawn_wire/client/Buffer.cpp
+++ b/src/dawn_wire/client/Buffer.cpp
@@ -33,64 +33,93 @@
             return device->CreateErrorBuffer();
         }
 
+        std::unique_ptr<MemoryTransferService::ReadHandle> readHandle = nullptr;
         std::unique_ptr<MemoryTransferService::WriteHandle> writeHandle = nullptr;
-        void* writeData = nullptr;
-        size_t writeHandleCreateInfoLength = 0;
 
-        // If the buffer is mapped at creation, create a write handle that will represent the
-        // mapping of the whole buffer.
-        if (descriptor->mappedAtCreation) {
-            // Create the handle.
-            writeHandle.reset(
-                wireClient->GetMemoryTransferService()->CreateWriteHandle(descriptor->size));
-            if (writeHandle == nullptr) {
-                device->InjectError(WGPUErrorType_OutOfMemory, "Buffer mapping allocation failed");
-                return device->CreateErrorBuffer();
+        DeviceCreateBufferCmd cmd;
+        cmd.deviceId = device->id;
+        cmd.descriptor = descriptor;
+        cmd.readHandleCreateInfoLength = 0;
+        cmd.readHandleCreateInfo = nullptr;
+        cmd.writeHandleCreateInfoLength = 0;
+        cmd.writeHandleCreateInfo = nullptr;
+
+        if (mappable) {
+            if ((descriptor->usage & WGPUBufferUsage_MapRead) != 0) {
+                // Create the read handle on buffer creation.
+                readHandle.reset(
+                    wireClient->GetMemoryTransferService()->CreateReadHandle(descriptor->size));
+                if (readHandle == nullptr) {
+                    device->InjectError(WGPUErrorType_OutOfMemory,
+                                        "Failed to create buffer mapping");
+                    return device->CreateErrorBuffer();
+                }
+                cmd.readHandleCreateInfoLength = readHandle->SerializeCreateSize();
             }
 
-            // Open the handle, it may fail by returning a nullptr in writeData.
-            size_t writeDataLength = 0;
-            std::tie(writeData, writeDataLength) = writeHandle->Open();
-            if (writeData == nullptr) {
-                device->InjectError(WGPUErrorType_OutOfMemory, "Buffer mapping allocation failed");
-                return device->CreateErrorBuffer();
+            if ((descriptor->usage & WGPUBufferUsage_MapWrite) != 0 ||
+                descriptor->mappedAtCreation) {
+                // Create the write handle on buffer creation.
+                writeHandle.reset(
+                    wireClient->GetMemoryTransferService()->CreateWriteHandle(descriptor->size));
+                if (writeHandle == nullptr) {
+                    device->InjectError(WGPUErrorType_OutOfMemory,
+                                        "Failed to create buffer mapping");
+                    return device->CreateErrorBuffer();
+                }
+                cmd.writeHandleCreateInfoLength = writeHandle->SerializeCreateSize();
             }
-            ASSERT(writeDataLength == descriptor->size);
-
-            // Get the serialization size of the write handle.
-            writeHandleCreateInfoLength = writeHandle->SerializeCreateSize();
         }
 
         // Create the buffer and send the creation command.
+        // This must happen after any potential device->CreateErrorBuffer()
+        // as server expects allocating ids to be monotonically increasing
         auto* bufferObjectAndSerial = wireClient->BufferAllocator().New(wireClient);
         Buffer* buffer = bufferObjectAndSerial->object.get();
         buffer->mDevice = device;
         buffer->mDeviceIsAlive = device->GetAliveWeakPtr();
         buffer->mSize = descriptor->size;
+        buffer->mDestructWriteHandleOnUnmap = false;
 
-        DeviceCreateBufferCmd cmd;
-        cmd.deviceId = device->id;
-        cmd.descriptor = descriptor;
+        if (descriptor->mappedAtCreation) {
+            // If the buffer is mapped at creation, a write handle is created and will be
+            // destructed on unmap if the buffer doesn't have MapWrite usage
+            // The buffer is mapped right now.
+            buffer->mMapState = MapState::MappedAtCreation;
+
+            // This flag is for write handle created by mappedAtCreation
+            // instead of MapWrite usage. We don't have such a case for read handle
+            buffer->mDestructWriteHandleOnUnmap =
+                (descriptor->usage & WGPUBufferUsage_MapWrite) == 0;
+
+            buffer->mMapOffset = 0;
+            buffer->mMapSize = buffer->mSize;
+            ASSERT(writeHandle != nullptr);
+            buffer->mMappedData = writeHandle->GetData();
+        }
+
         cmd.result = ObjectHandle{buffer->id, bufferObjectAndSerial->generation};
-        cmd.handleCreateInfoLength = writeHandleCreateInfoLength;
-        cmd.handleCreateInfo = nullptr;
 
         wireClient->SerializeCommand(
-            cmd, writeHandleCreateInfoLength, [&](SerializeBuffer* serializeBuffer) {
-                if (descriptor->mappedAtCreation) {
-                    char* writeHandleBuffer;
+            cmd, cmd.readHandleCreateInfoLength + cmd.writeHandleCreateInfoLength,
+            [&](SerializeBuffer* serializeBuffer) {
+                if (readHandle != nullptr) {
+                    char* readHandleBuffer;
                     WIRE_TRY(
-                        serializeBuffer->NextN(writeHandleCreateInfoLength, &writeHandleBuffer));
+                        serializeBuffer->NextN(cmd.readHandleCreateInfoLength, &readHandleBuffer));
+                    // Serialize the ReadHandle into the space after the command.
+                    readHandle->SerializeCreate(readHandleBuffer);
+                    buffer->mReadHandle = std::move(readHandle);
+                }
+                if (writeHandle != nullptr) {
+                    char* writeHandleBuffer;
+                    WIRE_TRY(serializeBuffer->NextN(cmd.writeHandleCreateInfoLength,
+                                                    &writeHandleBuffer));
                     // Serialize the WriteHandle into the space after the command.
                     writeHandle->SerializeCreate(writeHandleBuffer);
-
-                    // Set the buffer state for the mapping at creation. The buffer now owns the
-                    // write handle..
                     buffer->mWriteHandle = std::move(writeHandle);
-                    buffer->mMappedData = writeData;
-                    buffer->mMapOffset = 0;
-                    buffer->mMapSize = buffer->mSize;
                 }
+
                 return WireResult::Success;
             });
         return ToAPI(buffer);
@@ -120,7 +149,7 @@
         }
         mRequests.clear();
 
-        FreeMappedData(true);
+        FreeMappedData();
     }
 
     void Buffer::CancelCallbacksForDisconnect() {
@@ -146,97 +175,41 @@
             size = mSize - offset;
         }
 
-        bool isReadMode = mode & WGPUMapMode_Read;
-        bool isWriteMode = mode & WGPUMapMode_Write;
-
-        // Step 1. Do early validation of READ ^ WRITE because the server rejects mode = 0.
-        if (!(isReadMode ^ isWriteMode)) {
-            if (!mDeviceIsAlive.expired()) {
-                mDevice->InjectError(WGPUErrorType_Validation,
-                                     "MapAsync mode must be exactly one of Read or Write");
-            }
-            if (callback != nullptr) {
-                callback(WGPUBufferMapAsyncStatus_Error, userdata);
-            }
-            return;
-        }
-
-        // Step 2. Create the request structure that will hold information while this mapping is
+        // Create the request structure that will hold information while this mapping is
         // in flight.
-        uint32_t serial = mRequestSerial++;
+        uint64_t serial = mRequestSerial++;
         ASSERT(mRequests.find(serial) == mRequests.end());
 
         Buffer::MapRequestData request = {};
         request.callback = callback;
         request.userdata = userdata;
-        request.size = size;
         request.offset = offset;
-
-        // Step 2a: Create the read / write handles for this request.
-        if (isReadMode) {
-            request.readHandle.reset(client->GetMemoryTransferService()->CreateReadHandle(size));
-            if (request.readHandle == nullptr) {
-                if (!mDeviceIsAlive.expired()) {
-                    mDevice->InjectError(WGPUErrorType_OutOfMemory,
-                                         "Failed to create buffer mapping");
-                }
-                callback(WGPUBufferMapAsyncStatus_Error, userdata);
-                return;
-            }
-        } else {
-            ASSERT(isWriteMode);
-            request.writeHandle.reset(client->GetMemoryTransferService()->CreateWriteHandle(size));
-            if (request.writeHandle == nullptr) {
-                if (!mDeviceIsAlive.expired()) {
-                    mDevice->InjectError(WGPUErrorType_OutOfMemory,
-                                         "Failed to create buffer mapping");
-                }
-                callback(WGPUBufferMapAsyncStatus_Error, userdata);
-                return;
-            }
+        request.size = size;
+        if (mode & WGPUMapMode_Read) {
+            request.type = MapRequestType::Read;
+        } else if (mode & WGPUMapMode_Write) {
+            request.type = MapRequestType::Write;
         }
 
-        // Step 3. Serialize the command to send to the server.
+        // Serialize the command to send to the server.
         BufferMapAsyncCmd cmd;
         cmd.bufferId = this->id;
         cmd.requestSerial = serial;
         cmd.mode = mode;
         cmd.offset = offset;
         cmd.size = size;
-        cmd.handleCreateInfo = nullptr;
 
-        // Step 3a. Fill the handle create info in the command.
-        if (isReadMode) {
-            cmd.handleCreateInfoLength = request.readHandle->SerializeCreateSize();
-            client->SerializeCommand(
-                cmd, cmd.handleCreateInfoLength, [&](SerializeBuffer* serializeBuffer) {
-                    char* readHandleBuffer;
-                    WIRE_TRY(serializeBuffer->NextN(cmd.handleCreateInfoLength, &readHandleBuffer));
-                    request.readHandle->SerializeCreate(readHandleBuffer);
-                    return WireResult::Success;
-                });
-        } else {
-            ASSERT(isWriteMode);
-            cmd.handleCreateInfoLength = request.writeHandle->SerializeCreateSize();
-            client->SerializeCommand(
-                cmd, cmd.handleCreateInfoLength, [&](SerializeBuffer* serializeBuffer) {
-                    char* writeHandleBuffer;
-                    WIRE_TRY(
-                        serializeBuffer->NextN(cmd.handleCreateInfoLength, &writeHandleBuffer));
-                    request.writeHandle->SerializeCreate(writeHandleBuffer);
-                    return WireResult::Success;
-                });
-        }
+        client->SerializeCommand(cmd);
 
-        // Step 4. Register this request so that we can retrieve it from its serial when the server
+        // Register this request so that we can retrieve it from its serial when the server
         // sends the callback.
         mRequests[serial] = std::move(request);
     }
 
-    bool Buffer::OnMapAsyncCallback(uint32_t requestSerial,
+    bool Buffer::OnMapAsyncCallback(uint64_t requestSerial,
                                     uint32_t status,
-                                    uint64_t readInitialDataInfoLength,
-                                    const uint8_t* readInitialDataInfo) {
+                                    uint64_t readDataUpdateInfoLength,
+                                    const uint8_t* readDataUpdateInfo) {
         auto requestIt = mRequests.find(requestSerial);
         if (requestIt == mRequests.end()) {
             return false;
@@ -254,60 +227,50 @@
             return false;
         };
 
-        bool isRead = request.readHandle != nullptr;
-        bool isWrite = request.writeHandle != nullptr;
-        ASSERT(isRead != isWrite);
-
         // Take into account the client-side status of the request if the server says it is a success.
         if (status == WGPUBufferMapAsyncStatus_Success) {
             status = request.clientStatus;
         }
 
-        size_t mappedDataLength = 0;
-        const void* mappedData = nullptr;
         if (status == WGPUBufferMapAsyncStatus_Success) {
-            if (mReadHandle || mWriteHandle) {
-                // Buffer is already mapped.
-                return FailRequest();
+            switch (request.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();
+                    }
+
+                    // Validate to prevent bad map request; buffer destroyed during map request
+                    if (mReadHandle == nullptr) {
+                        return FailRequest();
+                    }
+                    // Update user map data with server returned data
+                    if (!mReadHandle->DeserializeDataUpdate(
+                            readDataUpdateInfo, static_cast<size_t>(readDataUpdateInfoLength),
+                            request.offset, request.size)) {
+                        return FailRequest();
+                    }
+                    mMapState = MapState::MappedForRead;
+                    mMappedData = const_cast<void*>(mReadHandle->GetData());
+                    break;
+                }
+                case MapRequestType::Write: {
+                    if (mWriteHandle == nullptr) {
+                        return FailRequest();
+                    }
+                    mMapState = MapState::MappedForWrite;
+                    mMappedData = mWriteHandle->GetData();
+                    break;
+                }
+                default:
+                    UNREACHABLE();
             }
 
-            if (isRead) {
-                if (readInitialDataInfoLength > std::numeric_limits<size_t>::max()) {
-                    // This is the size of data deserialized from the command stream, which must be
-                    // CPU-addressable.
-                    return FailRequest();
-                }
-
-                // The server serializes metadata to initialize the contents of the ReadHandle.
-                // Deserialize the message and return a pointer and size of the mapped data for
-                // reading.
-                if (!request.readHandle->DeserializeInitialData(
-                        readInitialDataInfo, static_cast<size_t>(readInitialDataInfoLength),
-                        &mappedData, &mappedDataLength)) {
-                    // Deserialization shouldn't fail. This is a fatal error.
-                    return FailRequest();
-                }
-                ASSERT(mappedData != nullptr);
-
-            } else {
-                // Open the WriteHandle. This returns a pointer and size of mapped memory.
-                // On failure, |mappedData| may be null.
-                std::tie(mappedData, mappedDataLength) = request.writeHandle->Open();
-
-                if (mappedData == nullptr) {
-                    return FailRequest();
-                }
-            }
-
-            // The MapAsync request was successful. The buffer now owns the Read/Write handles
-            // until Unmap().
-            mReadHandle = std::move(request.readHandle);
-            mWriteHandle = std::move(request.writeHandle);
+            mMapOffset = request.offset;
+            mMapSize = request.size;
         }
 
-        mMapOffset = request.offset;
-        mMapSize = request.size;
-        mMappedData = const_cast<void*>(mappedData);
         if (request.callback) {
             request.callback(static_cast<WGPUBufferMapAsyncStatus>(status), request.userdata);
         }
@@ -319,7 +282,7 @@
         if (!IsMappedForWriting() || !CheckGetMappedRangeOffsetSize(offset, size)) {
             return nullptr;
         }
-        return static_cast<uint8_t*>(mMappedData) + (offset - mMapOffset);
+        return static_cast<uint8_t*>(mMappedData) + offset;
     }
 
     const void* Buffer::GetConstMappedRange(size_t offset, size_t size) {
@@ -327,7 +290,7 @@
             !CheckGetMappedRangeOffsetSize(offset, size)) {
             return nullptr;
         }
-        return static_cast<uint8_t*>(mMappedData) + (offset - mMapOffset);
+        return static_cast<uint8_t*>(mMappedData) + offset;
     }
 
     void Buffer::Unmap() {
@@ -339,32 +302,57 @@
         //   - Server -> Client: Result of MapRequest1
         //   - Unmap locally on the client
         //   - Server -> Client: Result of MapRequest2
-        if (mWriteHandle) {
+
+        // TODO(dawn:608): mDevice->InjectError(WGPUErrorType_Validation) for map oom failure
+        // and separate from buffer destroyed before unmap case
+
+        // mWriteHandle can still be nullptr if buffer has been destroyed before unmap
+        if ((mMapState == MapState::MappedForWrite || mMapState == MapState::MappedAtCreation) &&
+            mWriteHandle != nullptr) {
             // Writes need to be flushed before Unmap is sent. Unmap calls all associated
             // in-flight callbacks which may read the updated data.
-            ASSERT(mReadHandle == nullptr);
 
-            // Get the serialization size of metadata to flush writes.
-            size_t writeFlushInfoLength = mWriteHandle->SerializeFlushSize();
+            // Get the serialization size of data update writes.
+            size_t writeDataUpdateInfoLength =
+                mWriteHandle->SizeOfSerializeDataUpdate(mMapOffset, mMapSize);
 
             BufferUpdateMappedDataCmd cmd;
             cmd.bufferId = id;
-            cmd.writeFlushInfoLength = writeFlushInfoLength;
-            cmd.writeFlushInfo = nullptr;
+            cmd.writeDataUpdateInfoLength = writeDataUpdateInfoLength;
+            cmd.writeDataUpdateInfo = nullptr;
+            cmd.offset = mMapOffset;
+            cmd.size = mMapSize;
 
             client->SerializeCommand(
-                cmd, writeFlushInfoLength, [&](SerializeBuffer* serializeBuffer) {
+                cmd, writeDataUpdateInfoLength, [&](SerializeBuffer* serializeBuffer) {
                     char* writeHandleBuffer;
-                    WIRE_TRY(serializeBuffer->NextN(writeFlushInfoLength, &writeHandleBuffer));
+                    WIRE_TRY(serializeBuffer->NextN(writeDataUpdateInfoLength, &writeHandleBuffer));
 
                     // Serialize flush metadata into the space after the command.
                     // This closes the handle for writing.
-                    mWriteHandle->SerializeFlush(writeHandleBuffer);
+                    mWriteHandle->SerializeDataUpdate(writeHandleBuffer, cmd.offset, cmd.size);
+
                     return WireResult::Success;
                 });
+
+            // If mDestructWriteHandleOnUnmap is true, that means the write handle is merely
+            // for mappedAtCreation usage. It is destroyed on unmap after flush to server
+            // instead of at buffer destruction.
+            if (mMapState == MapState::MappedAtCreation && mDestructWriteHandleOnUnmap) {
+                mWriteHandle = nullptr;
+                if (mReadHandle) {
+                    // If it's both mappedAtCreation and MapRead we need to reset
+                    // mMappedData to readHandle's GetData(). This could be changed to
+                    // merging read/write handle in future
+                    mMappedData = const_cast<void*>(mReadHandle->GetData());
+                }
+            }
         }
 
-        FreeMappedData(false);
+        // Free map access tokens
+        mMapState = MapState::Unmapped;
+        mMapOffset = 0;
+        mMapSize = 0;
 
         // Tag all mapping requests still in flight as unmapped before callback.
         for (auto& it : mRequests) {
@@ -376,11 +364,13 @@
         BufferUnmapCmd cmd;
         cmd.self = ToAPI(this);
         client->SerializeCommand(cmd);
+        // TODO(dawn:608): add tracking mapped status and change to return bool for returning unmap
+        // success/failure
     }
 
     void Buffer::Destroy() {
-        // Remove the current mapping.
-        FreeMappedData(true);
+        // Remove the current mapping and destroy Read/WriteHandles.
+        FreeMappedData();
 
         // Tag all mapping requests still in flight as destroyed before callback.
         for (auto& it : mRequests) {
@@ -395,11 +385,11 @@
     }
 
     bool Buffer::IsMappedForReading() const {
-        return mReadHandle != nullptr;
+        return mMapState == MapState::MappedForRead;
     }
 
     bool Buffer::IsMappedForWriting() const {
-        return mWriteHandle != nullptr;
+        return mMapState == MapState::MappedForWrite || mMapState == MapState::MappedAtCreation;
     }
 
     bool Buffer::CheckGetMappedRangeOffsetSize(size_t offset, size_t size) const {
@@ -415,20 +405,20 @@
         return offsetInMappedRange <= mMapSize - size;
     }
 
-    void Buffer::FreeMappedData(bool destruction) {
+    void Buffer::FreeMappedData() {
 #if defined(DAWN_ENABLE_ASSERTS)
         // When in "debug" mode, 0xCA-out the mapped data when we free it so that in we can detect
         // use-after-free of the mapped data. This is particularly useful for WebGPU test about the
         // interaction of mapping and GC.
-        if (mMappedData && destruction) {
-            memset(mMappedData, 0xCA, mMapSize);
+        if (mMappedData) {
+            memset(static_cast<uint8_t*>(mMappedData) + mMapOffset, 0xCA, mMapSize);
         }
 #endif  // defined(DAWN_ENABLE_ASSERTS)
 
         mMapOffset = 0;
         mMapSize = 0;
-        mWriteHandle = nullptr;
         mReadHandle = nullptr;
+        mWriteHandle = nullptr;
         mMappedData = nullptr;
     }
 
diff --git a/src/dawn_wire/client/Buffer.h b/src/dawn_wire/client/Buffer.h
index 50b9639..a7d3fab 100644
--- a/src/dawn_wire/client/Buffer.h
+++ b/src/dawn_wire/client/Buffer.h
@@ -35,10 +35,10 @@
 
         ~Buffer();
 
-        bool OnMapAsyncCallback(uint32_t requestSerial,
+        bool OnMapAsyncCallback(uint64_t requestSerial,
                                 uint32_t status,
-                                uint64_t readInitialDataInfoLength,
-                                const uint8_t* readInitialDataInfo);
+                                uint64_t readDataUpdateInfoLength,
+                                const uint8_t* readDataUpdateInfo);
         void MapAsync(WGPUMapModeFlags mode,
                       size_t offset,
                       size_t size,
@@ -57,10 +57,19 @@
         bool IsMappedForWriting() const;
         bool CheckGetMappedRangeOffsetSize(size_t offset, size_t size) const;
 
-        void FreeMappedData(bool destruction);
+        void FreeMappedData();
 
         Device* mDevice;
 
+        enum class MapRequestType { None, Read, Write };
+
+        enum class MapState {
+            Unmapped,
+            MappedForRead,
+            MappedForWrite,
+            MappedAtCreation,
+        };
+
         // We want to defer all the validation to the server, which means we could have multiple
         // map request in flight at a single time and need to track them separately.
         // On well-behaved applications, only one request should exist at a single time.
@@ -75,12 +84,10 @@
             // from the server take precedence over the client-side status.
             WGPUBufferMapAsyncStatus clientStatus = WGPUBufferMapAsyncStatus_Success;
 
-            // TODO(enga): Use a tagged pointer to save space.
-            std::unique_ptr<MemoryTransferService::ReadHandle> readHandle = nullptr;
-            std::unique_ptr<MemoryTransferService::WriteHandle> writeHandle = nullptr;
+            MapRequestType type = MapRequestType::None;
         };
-        std::map<uint32_t, MapRequestData> mRequests;
-        uint32_t mRequestSerial = 0;
+        std::map<uint64_t, MapRequestData> mRequests;
+        uint64_t mRequestSerial = 0;
         uint64_t mSize = 0;
 
         // Only one mapped pointer can be active at a time because Unmap clears all the in-flight
@@ -88,6 +95,9 @@
         // TODO(enga): Use a tagged pointer to save space.
         std::unique_ptr<MemoryTransferService::ReadHandle> mReadHandle = nullptr;
         std::unique_ptr<MemoryTransferService::WriteHandle> mWriteHandle = nullptr;
+        MapState mMapState = MapState::Unmapped;
+        bool mDestructWriteHandleOnUnmap = false;
+
         void* mMappedData = nullptr;
         size_t mMapOffset = 0;
         size_t mMapSize = 0;
diff --git a/src/dawn_wire/client/ClientDoers.cpp b/src/dawn_wire/client/ClientDoers.cpp
index 2e864a4..e3e34bd 100644
--- a/src/dawn_wire/client/ClientDoers.cpp
+++ b/src/dawn_wire/client/ClientDoers.cpp
@@ -73,16 +73,16 @@
     }
 
     bool Client::DoBufferMapAsyncCallback(Buffer* buffer,
-                                          uint32_t requestSerial,
+                                          uint64_t requestSerial,
                                           uint32_t status,
-                                          uint64_t readInitialDataInfoLength,
-                                          const uint8_t* readInitialDataInfo) {
+                                          uint64_t readDataUpdateInfoLength,
+                                          const uint8_t* readDataUpdateInfo) {
         // The buffer might have been deleted or recreated so this isn't an error.
         if (buffer == nullptr) {
             return true;
         }
-        return buffer->OnMapAsyncCallback(requestSerial, status, readInitialDataInfoLength,
-                                          readInitialDataInfo);
+        return buffer->OnMapAsyncCallback(requestSerial, status, readDataUpdateInfoLength,
+                                          readDataUpdateInfo);
     }
 
     bool Client::DoQueueWorkDoneCallback(Queue* queue,
diff --git a/src/dawn_wire/client/ClientInlineMemoryTransferService.cpp b/src/dawn_wire/client/ClientInlineMemoryTransferService.cpp
index 80867da..9cb0eed 100644
--- a/src/dawn_wire/client/ClientInlineMemoryTransferService.cpp
+++ b/src/dawn_wire/client/ClientInlineMemoryTransferService.cpp
@@ -24,7 +24,8 @@
     class InlineMemoryTransferService : public MemoryTransferService {
         class ReadHandleImpl : public ReadHandle {
           public:
-            explicit ReadHandleImpl(size_t size) : mSize(size) {
+            explicit ReadHandleImpl(std::unique_ptr<uint8_t[]> stagingData, size_t size)
+                : mStagingData(std::move(stagingData)), mSize(size) {
             }
 
             ~ReadHandleImpl() override = default;
@@ -36,36 +37,36 @@
             void SerializeCreate(void*) override {
             }
 
-            bool DeserializeInitialData(const void* deserializePointer,
-                                        size_t deserializeSize,
-                                        const void** data,
-                                        size_t* dataLength) override {
-                if (deserializeSize != mSize || deserializePointer == nullptr) {
+            const void* GetData() override {
+                return mStagingData.get();
+            }
+
+            bool DeserializeDataUpdate(const void* deserializePointer,
+                                       size_t deserializeSize,
+                                       size_t offset,
+                                       size_t size) override {
+                if (deserializeSize != size || deserializePointer == nullptr) {
                     return false;
                 }
 
-                mStagingData = std::unique_ptr<uint8_t[]>(AllocNoThrow<uint8_t>(mSize));
-                if (!mStagingData) {
+                if (offset > mSize || size > mSize - offset) {
                     return false;
                 }
-                memcpy(mStagingData.get(), deserializePointer, mSize);
 
-                ASSERT(data != nullptr);
-                ASSERT(dataLength != nullptr);
-                *data = mStagingData.get();
-                *dataLength = mSize;
-
+                void* start = static_cast<uint8_t*>(mStagingData.get()) + offset;
+                memcpy(start, deserializePointer, size);
                 return true;
             }
 
           private:
-            size_t mSize;
             std::unique_ptr<uint8_t[]> mStagingData;
+            size_t mSize;
         };
 
         class WriteHandleImpl : public WriteHandle {
           public:
-            explicit WriteHandleImpl(size_t size) : mSize(size) {
+            explicit WriteHandleImpl(std::unique_ptr<uint8_t[]> stagingData, size_t size)
+                : mStagingData(std::move(stagingData)), mSize(size) {
             }
 
             ~WriteHandleImpl() override = default;
@@ -77,28 +78,27 @@
             void SerializeCreate(void*) override {
             }
 
-            std::pair<void*, size_t> Open() override {
-                mStagingData = std::unique_ptr<uint8_t[]>(AllocNoThrow<uint8_t>(mSize));
-                if (!mStagingData) {
-                    return std::make_pair(nullptr, 0);
-                }
-                memset(mStagingData.get(), 0, mSize);
-                return std::make_pair(mStagingData.get(), mSize);
+            void* GetData() override {
+                return mStagingData.get();
             }
 
-            size_t SerializeFlushSize() override {
-                return mSize;
+            size_t SizeOfSerializeDataUpdate(size_t offset, size_t size) override {
+                ASSERT(offset <= mSize);
+                ASSERT(size <= mSize - offset);
+                return size;
             }
 
-            void SerializeFlush(void* serializePointer) override {
+            void SerializeDataUpdate(void* serializePointer, size_t offset, size_t size) override {
                 ASSERT(mStagingData != nullptr);
                 ASSERT(serializePointer != nullptr);
-                memcpy(serializePointer, mStagingData.get(), mSize);
+                ASSERT(offset <= mSize);
+                ASSERT(size <= mSize - offset);
+                memcpy(serializePointer, static_cast<uint8_t*>(mStagingData.get()) + offset, size);
             }
 
           private:
-            size_t mSize;
             std::unique_ptr<uint8_t[]> mStagingData;
+            size_t mSize;
         };
 
       public:
@@ -107,11 +107,20 @@
         ~InlineMemoryTransferService() override = default;
 
         ReadHandle* CreateReadHandle(size_t size) override {
-            return new ReadHandleImpl(size);
+            auto stagingData = std::unique_ptr<uint8_t[]>(AllocNoThrow<uint8_t>(size));
+            if (stagingData) {
+                return new ReadHandleImpl(std::move(stagingData), size);
+            }
+            return nullptr;
         }
 
         WriteHandle* CreateWriteHandle(size_t size) override {
-            return new WriteHandleImpl(size);
+            auto stagingData = std::unique_ptr<uint8_t[]>(AllocNoThrow<uint8_t>(size));
+            if (stagingData) {
+                memset(stagingData.get(), 0, size);
+                return new WriteHandleImpl(std::move(stagingData), size);
+            }
+            return nullptr;
         }
     };
 
diff --git a/src/dawn_wire/client/ClientMemoryTransferService_mock.cpp b/src/dawn_wire/client/ClientMemoryTransferService_mock.cpp
index dd8d62f..6a523f2 100644
--- a/src/dawn_wire/client/ClientMemoryTransferService_mock.cpp
+++ b/src/dawn_wire/client/ClientMemoryTransferService_mock.cpp
@@ -35,15 +35,19 @@
         mService->OnReadHandleSerializeCreate(this, serializePointer);
     }
 
-    bool MockMemoryTransferService::MockReadHandle::DeserializeInitialData(
+    const void* MockMemoryTransferService::MockReadHandle::GetData() {
+        return mService->OnReadHandleGetData(this);
+    }
+
+    bool MockMemoryTransferService::MockReadHandle::DeserializeDataUpdate(
         const void* deserializePointer,
         size_t deserializeSize,
-        const void** data,
-        size_t* dataLength) {
+        size_t offset,
+        size_t size) {
         ASSERT(deserializeSize % sizeof(uint32_t) == 0);
-        return mService->OnReadHandleDeserializeInitialData(
-            this, reinterpret_cast<const uint32_t*>(deserializePointer), deserializeSize, data,
-            dataLength);
+        return mService->OnReadHandleDeserializeDataUpdate(
+            this, reinterpret_cast<const uint32_t*>(deserializePointer), deserializeSize, offset,
+            size);
     }
 
     MockMemoryTransferService::MockWriteHandle::MockWriteHandle(MockMemoryTransferService* service)
@@ -62,16 +66,19 @@
         mService->OnWriteHandleSerializeCreate(this, serializePointer);
     }
 
-    std::pair<void*, size_t> MockMemoryTransferService::MockWriteHandle::Open() {
-        return mService->OnWriteHandleOpen(this);
+    void* MockMemoryTransferService::MockWriteHandle::GetData() {
+        return mService->OnWriteHandleGetData(this);
     }
 
-    size_t MockMemoryTransferService::MockWriteHandle::SerializeFlushSize() {
-        return mService->OnWriteHandleSerializeFlushSize(this);
+    size_t MockMemoryTransferService::MockWriteHandle::SizeOfSerializeDataUpdate(size_t offset,
+                                                                                 size_t size) {
+        return mService->OnWriteHandleSizeOfSerializeDataUpdate(this, offset, size);
     }
 
-    void MockMemoryTransferService::MockWriteHandle::SerializeFlush(void* serializePointer) {
-        mService->OnWriteHandleSerializeFlush(this, serializePointer);
+    void MockMemoryTransferService::MockWriteHandle::SerializeDataUpdate(void* serializePointer,
+                                                                         size_t offset,
+                                                                         size_t size) {
+        mService->OnWriteHandleSerializeDataUpdate(this, serializePointer, offset, size);
     }
 
     MockMemoryTransferService::MockMemoryTransferService() = default;
diff --git a/src/dawn_wire/client/ClientMemoryTransferService_mock.h b/src/dawn_wire/client/ClientMemoryTransferService_mock.h
index 2d5eba4..5f9eeb0 100644
--- a/src/dawn_wire/client/ClientMemoryTransferService_mock.h
+++ b/src/dawn_wire/client/ClientMemoryTransferService_mock.h
@@ -31,10 +31,11 @@
 
             size_t SerializeCreateSize() override;
             void SerializeCreate(void* serializePointer) override;
-            bool DeserializeInitialData(const void* deserializePointer,
-                                        size_t deserializeSize,
-                                        const void** data,
-                                        size_t* dataLength) override;
+            const void* GetData() override;
+            bool DeserializeDataUpdate(const void* deserializePointer,
+                                       size_t deserializeSize,
+                                       size_t offset,
+                                       size_t size) override;
 
           private:
             MockMemoryTransferService* mService;
@@ -47,9 +48,9 @@
 
             size_t SerializeCreateSize() override;
             void SerializeCreate(void* serializePointer) override;
-            std::pair<void*, size_t> Open() override;
-            size_t SerializeFlushSize() override;
-            void SerializeFlush(void* serializePointer) override;
+            void* GetData() override;
+            size_t SizeOfSerializeDataUpdate(size_t offset, size_t size) override;
+            void SerializeDataUpdate(void* serializePointer, size_t offset, size_t size) override;
 
           private:
             MockMemoryTransferService* mService;
@@ -69,24 +70,27 @@
 
         MOCK_METHOD(size_t, OnReadHandleSerializeCreateSize, (const ReadHandle*));
         MOCK_METHOD(void, OnReadHandleSerializeCreate, (const ReadHandle*, void* serializePointer));
+        MOCK_METHOD((const void*), OnReadHandleGetData, (const ReadHandle*));
         MOCK_METHOD(bool,
-                    OnReadHandleDeserializeInitialData,
+                    OnReadHandleDeserializeDataUpdate,
                     (const ReadHandle*,
                      const uint32_t* deserializePointer,
                      size_t deserializeSize,
-                     const void** data,
-                     size_t* dataLength));
+                     size_t offset,
+                     size_t size));
         MOCK_METHOD(void, OnReadHandleDestroy, (const ReadHandle*));
 
         MOCK_METHOD(size_t, OnWriteHandleSerializeCreateSize, (const void* WriteHandle));
         MOCK_METHOD(void,
                     OnWriteHandleSerializeCreate,
                     (const void* WriteHandle, void* serializePointer));
-        MOCK_METHOD((std::pair<void*, size_t>), OnWriteHandleOpen, (const void* WriteHandle));
-        MOCK_METHOD(size_t, OnWriteHandleSerializeFlushSize, (const void* WriteHandle));
-        MOCK_METHOD(void,
-                    OnWriteHandleSerializeFlush,
-                    (const void* WriteHandle, void* serializePointer));
+        MOCK_METHOD((void*), OnWriteHandleGetData, (const void* WriteHandle));
+        MOCK_METHOD(size_t,
+                    OnWriteHandleSizeOfSerializeDataUpdate,
+                    (const void* WriteHandle, size_t offset, size_t size));
+        MOCK_METHOD(size_t,
+                    OnWriteHandleSerializeDataUpdate,
+                    (const void* WriteHandle, void* serializePointer, size_t offset, size_t size));
         MOCK_METHOD(void, OnWriteHandleDestroy, (const void* WriteHandle));
     };
 
diff --git a/src/dawn_wire/server/ObjectStorage.h b/src/dawn_wire/server/ObjectStorage.h
index d4aa83c..bc45b26 100644
--- a/src/dawn_wire/server/ObjectStorage.h
+++ b/src/dawn_wire/server/ObjectStorage.h
@@ -62,6 +62,9 @@
         std::unique_ptr<MemoryTransferService::ReadHandle> readHandle;
         std::unique_ptr<MemoryTransferService::WriteHandle> writeHandle;
         BufferMapWriteState mapWriteState = BufferMapWriteState::Unmapped;
+        WGPUBufferUsageFlags usage = WGPUBufferUsage_None;
+        // Indicate if writeHandle needs to be destroyed on unmap
+        bool mappedAtCreation = false;
     };
 
     // Pack the ObjectType and ObjectId as a single value for storage in
diff --git a/src/dawn_wire/server/Server.h b/src/dawn_wire/server/Server.h
index af16b88..7118135 100644
--- a/src/dawn_wire/server/Server.h
+++ b/src/dawn_wire/server/Server.h
@@ -111,13 +111,10 @@
 
         ObjectHandle buffer;
         WGPUBuffer bufferObj;
-        uint32_t requestSerial;
+        uint64_t requestSerial;
         uint64_t offset;
         uint64_t size;
         WGPUMapModeFlags mode;
-        // TODO(enga): Use a tagged pointer to save space.
-        std::unique_ptr<MemoryTransferService::ReadHandle> readHandle = nullptr;
-        std::unique_ptr<MemoryTransferService::WriteHandle> writeHandle = nullptr;
     };
 
     struct ErrorScopeUserdata : CallbackUserdata {
diff --git a/src/dawn_wire/server/ServerBuffer.cpp b/src/dawn_wire/server/ServerBuffer.cpp
index b9fda44..7005f38 100644
--- a/src/dawn_wire/server/ServerBuffer.cpp
+++ b/src/dawn_wire/server/ServerBuffer.cpp
@@ -25,9 +25,13 @@
         auto* buffer = BufferObjects().Get(cmd.selfId);
         DAWN_ASSERT(buffer != nullptr);
 
-        // The buffer was unmapped. Clear the Read/WriteHandle.
-        buffer->readHandle = nullptr;
-        buffer->writeHandle = nullptr;
+        if (buffer->mappedAtCreation && !(buffer->usage & WGPUMapMode_Write)) {
+            // This indicates the writeHandle is for mappedAtCreation only. Destroy on unmap
+            // writeHandle could have possibly been deleted if buffer is already destroyed so we
+            // don't assert it's non-null
+            buffer->writeHandle = nullptr;
+        }
+
         buffer->mapWriteState = BufferMapWriteState::Unmapped;
 
         return true;
@@ -47,12 +51,10 @@
     }
 
     bool Server::DoBufferMapAsync(ObjectId bufferId,
-                                  uint32_t requestSerial,
+                                  uint64_t requestSerial,
                                   WGPUMapModeFlags mode,
                                   uint64_t offset64,
-                                  uint64_t size64,
-                                  uint64_t handleCreateInfoLength,
-                                  const uint8_t* handleCreateInfo) {
+                                  uint64_t size64) {
         // These requests are just forwarded to the buffer, with userdata containing what the
         // client will require in the return command.
 
@@ -66,13 +68,6 @@
             return false;
         }
 
-        // The server only knows how to deal with write XOR read. Validate that.
-        bool isReadMode = mode & WGPUMapMode_Read;
-        bool isWriteMode = mode & WGPUMapMode_Write;
-        if (!(isReadMode ^ isWriteMode)) {
-            return false;
-        }
-
         std::unique_ptr<MapUserdata> userdata = MakeUserdata<MapUserdata>();
         userdata->buffer = ObjectHandle{bufferId, buffer->generation};
         userdata->bufferObj = buffer->handle;
@@ -80,8 +75,7 @@
         userdata->mode = mode;
 
         if (offset64 > std::numeric_limits<size_t>::max() ||
-            size64 > std::numeric_limits<size_t>::max() ||
-            handleCreateInfoLength > std::numeric_limits<size_t>::max()) {
+            size64 > std::numeric_limits<size_t>::max()) {
             OnBufferMapAsyncCallback(WGPUBufferMapAsyncStatus_Error, userdata.get());
             return true;
         }
@@ -92,32 +86,6 @@
         userdata->offset = offset;
         userdata->size = size;
 
-        // The handle will point to the mapped memory or staging memory for the mapping.
-        // Store it on the map request.
-        if (isWriteMode) {
-            // Deserialize metadata produced from the client to create a companion server handle.
-            MemoryTransferService::WriteHandle* writeHandle = nullptr;
-            if (!mMemoryTransferService->DeserializeWriteHandle(
-                    handleCreateInfo, static_cast<size_t>(handleCreateInfoLength), &writeHandle)) {
-                return false;
-            }
-            ASSERT(writeHandle != nullptr);
-
-            userdata->writeHandle =
-                std::unique_ptr<MemoryTransferService::WriteHandle>(writeHandle);
-        } else {
-            ASSERT(isReadMode);
-            // Deserialize metadata produced from the client to create a companion server handle.
-            MemoryTransferService::ReadHandle* readHandle = nullptr;
-            if (!mMemoryTransferService->DeserializeReadHandle(
-                    handleCreateInfo, static_cast<size_t>(handleCreateInfoLength), &readHandle)) {
-                return false;
-            }
-            ASSERT(readHandle != nullptr);
-
-            userdata->readHandle = std::unique_ptr<MemoryTransferService::ReadHandle>(readHandle);
-        }
-
         mProcs.bufferMapAsync(
             buffer->handle, mode, offset, size,
             ForwardToServer<decltype(
@@ -130,8 +98,10 @@
     bool Server::DoDeviceCreateBuffer(ObjectId deviceId,
                                       const WGPUBufferDescriptor* descriptor,
                                       ObjectHandle bufferResult,
-                                      uint64_t handleCreateInfoLength,
-                                      const uint8_t* handleCreateInfo) {
+                                      uint64_t readHandleCreateInfoLength,
+                                      const uint8_t* readHandleCreateInfo,
+                                      uint64_t writeHandleCreateInfoLength,
+                                      const uint8_t* writeHandleCreateInfo) {
         auto* device = DeviceObjects().Get(deviceId);
         if (device == nullptr) {
             return false;
@@ -145,56 +115,84 @@
         resultData->generation = bufferResult.generation;
         resultData->handle = mProcs.deviceCreateBuffer(device->handle, descriptor);
         resultData->deviceInfo = device->info.get();
+        resultData->usage = descriptor->usage;
+        resultData->mappedAtCreation = descriptor->mappedAtCreation;
         if (!TrackDeviceChild(resultData->deviceInfo, ObjectType::Buffer, bufferResult.id)) {
             return false;
         }
 
-        // If the buffer isn't mapped at creation, we are done.
-        if (!descriptor->mappedAtCreation) {
-            return handleCreateInfoLength == 0;
-        }
+        // isReadMode and isWriteMode could be true at the same time if usage contains
+        // WGPUMapMode_Read and buffer is mappedAtCreation
+        bool isReadMode = descriptor->usage & WGPUMapMode_Read;
+        bool isWriteMode = descriptor->usage & WGPUMapMode_Write || descriptor->mappedAtCreation;
 
-        // This is the size of data deserialized from the command stream to create the write handle,
-        // which must be CPU-addressable.
-        if (handleCreateInfoLength > std::numeric_limits<size_t>::max()) {
+        // This is the size of data deserialized from the command stream to create the read/write
+        // handle, which must be CPU-addressable.
+        if (readHandleCreateInfoLength > std::numeric_limits<size_t>::max() ||
+            writeHandleCreateInfoLength > std::numeric_limits<size_t>::max() ||
+            readHandleCreateInfoLength >
+                std::numeric_limits<size_t>::max() - writeHandleCreateInfoLength) {
             return false;
         }
 
-        void* mapping = mProcs.bufferGetMappedRange(resultData->handle, 0, descriptor->size);
-        if (mapping == nullptr) {
-            // A zero mapping is used to indicate an allocation error of an error buffer. This is a
-            // valid case and isn't fatal. Remember the buffer is an error so as to skip subsequent
-            // mapping operations.
-            resultData->mapWriteState = BufferMapWriteState::MapError;
-            return true;
+        if (isWriteMode) {
+            MemoryTransferService::WriteHandle* writeHandle = nullptr;
+            // Deserialize metadata produced from the client to create a companion server handle.
+            if (!mMemoryTransferService->DeserializeWriteHandle(
+                    writeHandleCreateInfo, static_cast<size_t>(writeHandleCreateInfoLength),
+                    &writeHandle)) {
+                return false;
+            }
+            ASSERT(writeHandle != nullptr);
+            resultData->writeHandle.reset(writeHandle);
+            writeHandle->SetDataLength(descriptor->size);
+
+            if (descriptor->mappedAtCreation) {
+                void* mapping =
+                    mProcs.bufferGetMappedRange(resultData->handle, 0, descriptor->size);
+                if (mapping == nullptr) {
+                    // A zero mapping is used to indicate an allocation error of an error buffer.
+                    // This is a valid case and isn't fatal. Remember the buffer is an error so as
+                    // to skip subsequent mapping operations.
+                    resultData->mapWriteState = BufferMapWriteState::MapError;
+                    return true;
+                }
+                ASSERT(mapping != nullptr);
+                writeHandle->SetTarget(mapping);
+
+                resultData->mapWriteState = BufferMapWriteState::Mapped;
+            }
         }
 
-        // Deserialize metadata produced from the client to create a companion server handle.
-        MemoryTransferService::WriteHandle* writeHandle = nullptr;
-        if (!mMemoryTransferService->DeserializeWriteHandle(
-                handleCreateInfo, static_cast<size_t>(handleCreateInfoLength), &writeHandle)) {
-            return false;
+        if (isReadMode) {
+            MemoryTransferService::ReadHandle* readHandle = nullptr;
+            // Deserialize metadata produced from the client to create a companion server handle.
+            if (!mMemoryTransferService->DeserializeReadHandle(
+                    readHandleCreateInfo, static_cast<size_t>(readHandleCreateInfoLength),
+                    &readHandle)) {
+                return false;
+            }
+            ASSERT(readHandle != nullptr);
+
+            resultData->readHandle.reset(readHandle);
         }
 
-        // Set the target of the WriteHandle to the mapped GPU memory.
-        ASSERT(writeHandle != nullptr);
-        writeHandle->SetTarget(mapping, descriptor->size);
-
-        resultData->mapWriteState = BufferMapWriteState::Mapped;
-        resultData->writeHandle.reset(writeHandle);
-
         return true;
     }
 
     bool Server::DoBufferUpdateMappedData(ObjectId bufferId,
-                                          uint64_t writeFlushInfoLength,
-                                          const uint8_t* writeFlushInfo) {
+                                          uint64_t writeDataUpdateInfoLength,
+                                          const uint8_t* writeDataUpdateInfo,
+                                          uint64_t offset,
+                                          uint64_t size) {
         // The null object isn't valid as `self`
         if (bufferId == 0) {
             return false;
         }
 
-        if (writeFlushInfoLength > std::numeric_limits<size_t>::max()) {
+        if (writeDataUpdateInfoLength > std::numeric_limits<size_t>::max() ||
+            offset > std::numeric_limits<size_t>::max() ||
+            size > std::numeric_limits<size_t>::max()) {
             return false;
         }
 
@@ -220,8 +218,9 @@
 
         // Deserialize the flush info and flush updated data from the handle into the target
         // of the handle. The target is set via WriteHandle::SetTarget.
-        return buffer->writeHandle->DeserializeFlush(writeFlushInfo,
-                                                     static_cast<size_t>(writeFlushInfoLength));
+        return buffer->writeHandle->DeserializeDataUpdate(
+            writeDataUpdateInfo, static_cast<size_t>(writeDataUpdateInfoLength),
+            static_cast<size_t>(offset), static_cast<size_t>(size));
     }
 
     void Server::OnBufferMapAsyncCallback(WGPUBufferMapAsyncStatus status, MapUserdata* data) {
@@ -238,39 +237,41 @@
         cmd.buffer = data->buffer;
         cmd.requestSerial = data->requestSerial;
         cmd.status = status;
-        cmd.readInitialDataInfoLength = 0;
-        cmd.readInitialDataInfo = nullptr;
+        cmd.readDataUpdateInfoLength = 0;
+        cmd.readDataUpdateInfo = nullptr;
 
         const void* readData = nullptr;
-        if (isSuccess && isRead) {
-            // Get the serialization size of the message to initialize ReadHandle data.
-            readData = mProcs.bufferGetConstMappedRange(data->bufferObj, data->offset, data->size);
-            cmd.readInitialDataInfoLength =
-                data->readHandle->SerializeInitialDataSize(readData, data->size);
+        if (isSuccess) {
+            if (isRead) {
+                // Get the serialization size of the message to initialize ReadHandle data.
+                readData =
+                    mProcs.bufferGetConstMappedRange(data->bufferObj, data->offset, data->size);
+                cmd.readDataUpdateInfoLength =
+                    bufferData->readHandle->SizeOfSerializeDataUpdate(data->offset, data->size);
+            } else {
+                ASSERT(data->mode & WGPUMapMode_Write);
+                // The in-flight map request returned successfully.
+                bufferData->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.
+                bufferData->writeHandle->SetTarget(
+                    static_cast<uint8_t*>(
+                        mProcs.bufferGetMappedRange(data->bufferObj, data->offset, data->size)) -
+                    data->offset);
+            }
         }
 
-        SerializeCommand(cmd, cmd.readInitialDataInfoLength, [&](SerializeBuffer* serializeBuffer) {
-            if (isSuccess) {
-                if (isRead) {
-                    char* readHandleBuffer;
-                    WIRE_TRY(
-                        serializeBuffer->NextN(cmd.readInitialDataInfoLength, &readHandleBuffer));
-
-                    // Serialize the initialization message into the space after the command.
-                    data->readHandle->SerializeInitialData(readData, data->size, readHandleBuffer);
-                    // The in-flight map request returned successfully.
-                    // Move the ReadHandle so it is owned by the buffer.
-                    bufferData->readHandle = std::move(data->readHandle);
-                } else {
-                    // The in-flight map request returned successfully.
-                    // Move the WriteHandle so it is owned by the buffer.
-                    bufferData->writeHandle = std::move(data->writeHandle);
-                    bufferData->mapWriteState = BufferMapWriteState::Mapped;
-                    // Set the target of the WriteHandle to the mapped buffer data.
-                    bufferData->writeHandle->SetTarget(
-                        mProcs.bufferGetMappedRange(data->bufferObj, data->offset, data->size),
-                        data->size);
-                }
+        SerializeCommand(cmd, cmd.readDataUpdateInfoLength, [&](SerializeBuffer* serializeBuffer) {
+            if (isSuccess && isRead) {
+                char* readHandleBuffer;
+                WIRE_TRY(serializeBuffer->NextN(cmd.readDataUpdateInfoLength, &readHandleBuffer));
+                // The in-flight map request returned successfully.
+                bufferData->readHandle->SerializeDataUpdate(readData, data->offset, data->size,
+                                                            readHandleBuffer);
             }
             return WireResult::Success;
         });
diff --git a/src/dawn_wire/server/ServerInlineMemoryTransferService.cpp b/src/dawn_wire/server/ServerInlineMemoryTransferService.cpp
index 105dee4..71347d2 100644
--- a/src/dawn_wire/server/ServerInlineMemoryTransferService.cpp
+++ b/src/dawn_wire/server/ServerInlineMemoryTransferService.cpp
@@ -28,17 +28,18 @@
             }
             ~ReadHandleImpl() override = default;
 
-            size_t SerializeInitialDataSize(const void* data, size_t dataLength) override {
-                return dataLength;
+            size_t SizeOfSerializeDataUpdate(size_t offset, size_t size) override {
+                return size;
             }
 
-            void SerializeInitialData(const void* data,
-                                      size_t dataLength,
-                                      void* serializePointer) override {
-                if (dataLength > 0) {
+            void SerializeDataUpdate(const void* data,
+                                     size_t offset,
+                                     size_t size,
+                                     void* serializePointer) override {
+                if (size > 0) {
                     ASSERT(data != nullptr);
                     ASSERT(serializePointer != nullptr);
-                    memcpy(serializePointer, data, dataLength);
+                    memcpy(serializePointer, data, size);
                 }
             }
         };
@@ -49,12 +50,18 @@
             }
             ~WriteHandleImpl() override = default;
 
-            bool DeserializeFlush(const void* deserializePointer, size_t deserializeSize) override {
-                if (deserializeSize != mDataLength || mTargetData == nullptr ||
+            bool DeserializeDataUpdate(const void* deserializePointer,
+                                       size_t deserializeSize,
+                                       size_t offset,
+                                       size_t size) override {
+                if (deserializeSize != size || mTargetData == nullptr ||
                     deserializePointer == nullptr) {
                     return false;
                 }
-                memcpy(mTargetData, deserializePointer, mDataLength);
+                if ((offset >= mDataLength && offset > 0) || size > mDataLength - offset) {
+                    return false;
+                }
+                memcpy(static_cast<uint8_t*>(mTargetData) + offset, deserializePointer, size);
                 return true;
             }
         };
diff --git a/src/dawn_wire/server/ServerMemoryTransferService_mock.cpp b/src/dawn_wire/server/ServerMemoryTransferService_mock.cpp
index b8b1696..165c6d3 100644
--- a/src/dawn_wire/server/ServerMemoryTransferService_mock.cpp
+++ b/src/dawn_wire/server/ServerMemoryTransferService_mock.cpp
@@ -26,15 +26,16 @@
         mService->OnReadHandleDestroy(this);
     }
 
-    size_t MockMemoryTransferService::MockReadHandle::SerializeInitialDataSize(const void* data,
-                                                                               size_t dataLength) {
-        return mService->OnReadHandleSerializeInitialDataSize(this, data, dataLength);
+    size_t MockMemoryTransferService::MockReadHandle::SizeOfSerializeDataUpdate(size_t offset,
+                                                                                size_t size) {
+        return mService->OnReadHandleSizeOfSerializeDataUpdate(this, offset, size);
     }
 
-    void MockMemoryTransferService::MockReadHandle::SerializeInitialData(const void* data,
-                                                                         size_t dataLength,
-                                                                         void* serializePointer) {
-        mService->OnReadHandleSerializeInitialData(this, data, dataLength, serializePointer);
+    void MockMemoryTransferService::MockReadHandle::SerializeDataUpdate(const void* data,
+                                                                        size_t offset,
+                                                                        size_t size,
+                                                                        void* serializePointer) {
+        mService->OnReadHandleSerializeDataUpdate(this, data, offset, size, serializePointer);
     }
 
     MockMemoryTransferService::MockWriteHandle::MockWriteHandle(MockMemoryTransferService* service)
@@ -45,18 +46,21 @@
         mService->OnWriteHandleDestroy(this);
     }
 
-    bool MockMemoryTransferService::MockWriteHandle::DeserializeFlush(
-        const void* deserializePointer,
-        size_t deserializeSize) {
-        ASSERT(deserializeSize % sizeof(uint32_t) == 0);
-        return mService->OnWriteHandleDeserializeFlush(
-            this, reinterpret_cast<const uint32_t*>(deserializePointer), deserializeSize);
-    }
-
     const uint32_t* MockMemoryTransferService::MockWriteHandle::GetData() const {
         return reinterpret_cast<const uint32_t*>(mTargetData);
     }
 
+    bool MockMemoryTransferService::MockWriteHandle::DeserializeDataUpdate(
+        const void* deserializePointer,
+        size_t deserializeSize,
+        size_t offset,
+        size_t size) {
+        ASSERT(deserializeSize % sizeof(uint32_t) == 0);
+        return mService->OnWriteHandleDeserializeDataUpdate(
+            this, reinterpret_cast<const uint32_t*>(deserializePointer), deserializeSize, offset,
+            size);
+    }
+
     MockMemoryTransferService::MockMemoryTransferService() = default;
     MockMemoryTransferService::~MockMemoryTransferService() = default;
 
diff --git a/src/dawn_wire/server/ServerMemoryTransferService_mock.h b/src/dawn_wire/server/ServerMemoryTransferService_mock.h
index f85aa64..23cf9ed 100644
--- a/src/dawn_wire/server/ServerMemoryTransferService_mock.h
+++ b/src/dawn_wire/server/ServerMemoryTransferService_mock.h
@@ -29,10 +29,11 @@
             MockReadHandle(MockMemoryTransferService* service);
             ~MockReadHandle() override;
 
-            size_t SerializeInitialDataSize(const void* data, size_t dataLength) override;
-            void SerializeInitialData(const void* data,
-                                      size_t dataLength,
-                                      void* serializePointer) override;
+            size_t SizeOfSerializeDataUpdate(size_t offset, size_t size) override;
+            void SerializeDataUpdate(const void* data,
+                                     size_t offset,
+                                     size_t size,
+                                     void* serializePointer) override;
 
           private:
             MockMemoryTransferService* mService;
@@ -43,7 +44,10 @@
             MockWriteHandle(MockMemoryTransferService* service);
             ~MockWriteHandle() override;
 
-            bool DeserializeFlush(const void* deserializePointer, size_t deserializeSize) override;
+            bool DeserializeDataUpdate(const void* deserializePointer,
+                                       size_t deserializeSize,
+                                       size_t offset,
+                                       size_t size) override;
 
             const uint32_t* GetData() const;
 
@@ -78,21 +82,24 @@
                      WriteHandle** writeHandle));
 
         MOCK_METHOD(size_t,
-                    OnReadHandleSerializeInitialDataSize,
-                    (const ReadHandle* readHandle, const void* data, size_t dataLength));
+                    OnReadHandleSizeOfSerializeDataUpdate,
+                    (const ReadHandle* readHandle, size_t offset, size_t size));
         MOCK_METHOD(void,
-                    OnReadHandleSerializeInitialData,
+                    OnReadHandleSerializeDataUpdate,
                     (const ReadHandle* readHandle,
                      const void* data,
-                     size_t dataLength,
+                     size_t offset,
+                     size_t size,
                      void* serializePointer));
         MOCK_METHOD(void, OnReadHandleDestroy, (const ReadHandle* readHandle));
 
         MOCK_METHOD(bool,
-                    OnWriteHandleDeserializeFlush,
+                    OnWriteHandleDeserializeDataUpdate,
                     (const WriteHandle* writeHandle,
                      const uint32_t* deserializePointer,
-                     size_t deserializeSize));
+                     size_t deserializeSize,
+                     size_t offset,
+                     size_t size));
         MOCK_METHOD(void, OnWriteHandleDestroy, (const WriteHandle* writeHandle));
     };
 
diff --git a/src/include/dawn_wire/WireClient.h b/src/include/dawn_wire/WireClient.h
index e633dc5..961e045 100644
--- a/src/include/dawn_wire/WireClient.h
+++ b/src/include/dawn_wire/WireClient.h
@@ -121,6 +121,7 @@
                 // deserialize the data update and apply
                 // it to the range (offset, offset + size) of allocation
                 // There could be nothing to be deserialized (if using shared memory)
+                // Needs to check potential offset/size OOB and overflow
                 // TODO(dawn:773): change to pure virtual after update on chromium side.
                 virtual bool DeserializeDataUpdate(const void* deserializePointer,
                                                    size_t deserializeSize,
diff --git a/src/include/dawn_wire/WireServer.h b/src/include/dawn_wire/WireServer.h
index 1e03880..bb3f7c2 100644
--- a/src/include/dawn_wire/WireServer.h
+++ b/src/include/dawn_wire/WireServer.h
@@ -132,8 +132,9 @@
 
                 // Set the target for writes from the client. DeserializeFlush should copy data
                 // into the target.
-                // TODO(dawn:773): only set backing buffer pointer data
-                void SetTarget(void* data, size_t dataLength);
+                void SetTarget(void* data);
+                // Set Staging data length for OOB check
+                void SetDataLength(size_t dataLength);
 
                 // TODO(dawn:773): remove after update on chromium side.
                 virtual bool DeserializeFlush(const void* deserializePointer,
@@ -143,6 +144,7 @@
 
                 // This function takes in the serialized result of
                 // client::MemoryTransferService::WriteHandle::SerializeDataUpdate.
+                // Needs to check potential offset/size OOB and overflow
                 virtual bool DeserializeDataUpdate(const void* deserializePointer,
                                                    size_t deserializeSize,
                                                    size_t offset,
@@ -152,7 +154,6 @@
 
               protected:
                 void* mTargetData = nullptr;
-                // TODO(dawn:773): only set backing buffer pointer data
                 size_t mDataLength = 0;
 
               private:
diff --git a/src/tests/end2end/BufferTests.cpp b/src/tests/end2end/BufferTests.cpp
index 25b0fde..12ac228 100644
--- a/src/tests/end2end/BufferTests.cpp
+++ b/src/tests/end2end/BufferTests.cpp
@@ -113,6 +113,21 @@
     buffer.Unmap();
 }
 
+// Map read and test multiple get mapped range data
+TEST_P(BufferMappingTests, MapRead_MultipleMappedRange) {
+    wgpu::Buffer buffer = CreateMapReadBuffer(12);
+
+    uint32_t myData[] = {0x00010203, 0x04050607, 0x08090a0b};
+    queue.WriteBuffer(buffer, 0, &myData, 12);
+
+    MapAsyncAndWait(buffer, wgpu::MapMode::Read, 0, 12);
+    ASSERT_EQ(myData[0], *static_cast<const uint32_t*>(buffer.GetConstMappedRange(0)));
+    ASSERT_EQ(myData[1], *(static_cast<const uint32_t*>(buffer.GetConstMappedRange(0)) + 1));
+    ASSERT_EQ(myData[2], *(static_cast<const uint32_t*>(buffer.GetConstMappedRange(0)) + 2));
+    ASSERT_EQ(myData[2], *static_cast<const uint32_t*>(buffer.GetConstMappedRange(8)));
+    buffer.Unmap();
+}
+
 // Test map-reading a large buffer.
 TEST_P(BufferMappingTests, MapRead_Large) {
     constexpr uint32_t kDataSize = 1000 * 1000;
@@ -257,6 +272,70 @@
     EXPECT_BUFFER_U32_EQ(myData, buffer, 0);
 }
 
+// Map write and unmap twice with different ranges and make sure the first write is preserved
+TEST_P(BufferMappingTests, MapWrite_TwicePreserve) {
+    wgpu::Buffer buffer = CreateMapWriteBuffer(12);
+
+    uint32_t data1 = 0x08090a0b;
+    size_t offset1 = 8;
+    MapAsyncAndWait(buffer, wgpu::MapMode::Write, offset1, sizeof(data1));
+    memcpy(buffer.GetMappedRange(offset1), &data1, sizeof(data1));
+    buffer.Unmap();
+
+    uint32_t data2 = 0x00010203;
+    size_t offset2 = 0;
+    MapAsyncAndWait(buffer, wgpu::MapMode::Write, offset2, sizeof(data2));
+    memcpy(buffer.GetMappedRange(offset2), &data2, sizeof(data2));
+    buffer.Unmap();
+
+    EXPECT_BUFFER_U32_EQ(data1, buffer, offset1);
+    EXPECT_BUFFER_U32_EQ(data2, buffer, offset2);
+}
+
+// Map write and unmap twice with overlapping ranges and make sure data is updated correctly
+TEST_P(BufferMappingTests, MapWrite_TwiceRangeOverlap) {
+    wgpu::Buffer buffer = CreateMapWriteBuffer(16);
+
+    uint32_t data1[] = {0x01234567, 0x89abcdef};
+    size_t offset1 = 8;
+    MapAsyncAndWait(buffer, wgpu::MapMode::Write, offset1, 8);
+    memcpy(buffer.GetMappedRange(offset1), data1, 8);
+    buffer.Unmap();
+
+    EXPECT_BUFFER_U32_EQ(0x00000000, buffer, 0);
+    EXPECT_BUFFER_U32_EQ(0x00000000, buffer, 4);
+    EXPECT_BUFFER_U32_EQ(0x01234567, buffer, 8);
+    EXPECT_BUFFER_U32_EQ(0x89abcdef, buffer, 12);
+
+    uint32_t data2[] = {0x01234567, 0x89abcdef, 0x55555555};
+    size_t offset2 = 0;
+    MapAsyncAndWait(buffer, wgpu::MapMode::Write, offset2, 12);
+    memcpy(buffer.GetMappedRange(offset2), data2, 12);
+    buffer.Unmap();
+
+    EXPECT_BUFFER_U32_EQ(0x01234567, buffer, 0);
+    EXPECT_BUFFER_U32_EQ(0x89abcdef, buffer, 4);
+    EXPECT_BUFFER_U32_EQ(0x55555555, buffer, 8);
+    EXPECT_BUFFER_U32_EQ(0x89abcdef, buffer, 12);
+}
+
+// Map write and test multiple mapped range data get updated correctly
+TEST_P(BufferMappingTests, MapWrite_MultipleMappedRange) {
+    wgpu::Buffer buffer = CreateMapWriteBuffer(12);
+
+    uint32_t data1 = 0x08090a0b;
+    size_t offset1 = 8;
+    uint32_t data2 = 0x00010203;
+    size_t offset2 = 0;
+    MapAsyncAndWait(buffer, wgpu::MapMode::Write, 0, 12);
+    memcpy(buffer.GetMappedRange(offset1), &data1, sizeof(data1));
+    memcpy(buffer.GetMappedRange(offset2), &data2, sizeof(data2));
+    buffer.Unmap();
+
+    EXPECT_BUFFER_U32_EQ(data1, buffer, offset1);
+    EXPECT_BUFFER_U32_EQ(data2, buffer, offset2);
+}
+
 // Test mapping a large buffer.
 TEST_P(BufferMappingTests, MapWrite_Large) {
     constexpr uint32_t kDataSize = 1000 * 1000;
@@ -330,20 +409,26 @@
     queue.WriteBuffer(buffer, 0, data, sizeof(data));
 
     // Map the buffer but do not wait on the result yet.
-    bool done = false;
+    bool done1 = false;
+    bool done2 = false;
     buffer.MapAsync(
         wgpu::MapMode::Read, 8, 4,
         [](WGPUBufferMapAsyncStatus status, void* userdata) {
             ASSERT_EQ(WGPUBufferMapAsyncStatus_Success, status);
             *static_cast<bool*>(userdata) = true;
         },
-        &done);
+        &done1);
 
     // Call MapAsync another time, it is an error because the buffer is already being mapped so
     // mMapOffset is not updated.
-    ASSERT_DEVICE_ERROR(buffer.MapAsync(wgpu::MapMode::Read, 0, 4, nullptr, nullptr));
+    ASSERT_DEVICE_ERROR(buffer.MapAsync(
+        wgpu::MapMode::Read, 0, 4,
+        [](WGPUBufferMapAsyncStatus status, void* userdata) {
+            *static_cast<bool*>(userdata) = true;
+        },
+        &done2));
 
-    while (!done) {
+    while (!done1 || !done2) {
         WaitABit();
     }
 
diff --git a/src/tests/unittests/wire/WireBufferMappingTests.cpp b/src/tests/unittests/wire/WireBufferMappingTests.cpp
index eab0b5b..22d8109 100644
--- a/src/tests/unittests/wire/WireBufferMappingTests.cpp
+++ b/src/tests/unittests/wire/WireBufferMappingTests.cpp
@@ -47,17 +47,7 @@
         WireTest::SetUp();
 
         mockBufferMapCallback = std::make_unique<StrictMock<MockBufferMapCallback>>();
-
-        WGPUBufferDescriptor descriptor = {};
-        descriptor.size = kBufferSize;
-
         apiBuffer = api.GetNewBuffer();
-        buffer = wgpuDeviceCreateBuffer(device, &descriptor);
-
-        EXPECT_CALL(api, DeviceCreateBuffer(apiDevice, _))
-            .WillOnce(Return(apiBuffer))
-            .RetiresOnSaturation();
-        FlushClient();
     }
 
     void TearDown() override {
@@ -77,6 +67,19 @@
         Mock::VerifyAndClearExpectations(&mockBufferMapCallback);
     }
 
+    void SetupBuffer(WGPUBufferUsageFlags usage) {
+        WGPUBufferDescriptor descriptor = {};
+        descriptor.size = kBufferSize;
+        descriptor.usage = usage;
+
+        buffer = wgpuDeviceCreateBuffer(device, &descriptor);
+
+        EXPECT_CALL(api, DeviceCreateBuffer(apiDevice, _))
+            .WillOnce(Return(apiBuffer))
+            .RetiresOnSaturation();
+        FlushClient();
+    }
+
   protected:
     static constexpr uint64_t kBufferSize = sizeof(uint32_t);
     // A successfully created buffer
@@ -85,9 +88,21 @@
 };
 
 // Tests specific to mapping for reading
+class WireBufferMappingReadTests : public WireBufferMappingTests {
+  public:
+    WireBufferMappingReadTests() {
+    }
+    ~WireBufferMappingReadTests() override = default;
+
+    void SetUp() override {
+        WireBufferMappingTests::SetUp();
+
+        SetupBuffer(WGPUBufferUsage_MapRead);
+    }
+};
 
 // Check mapping for reading a succesfully created buffer
-TEST_F(WireBufferMappingTests, MappingForReadSuccessBuffer) {
+TEST_F(WireBufferMappingReadTests, MappingForReadSuccessBuffer) {
     wgpuBufferMapAsync(buffer, WGPUMapMode_Read, 0, kBufferSize, ToMockBufferMapCallback, nullptr);
 
     uint32_t bufferContent = 31337;
@@ -115,7 +130,7 @@
 
 // Check that things work correctly when a validation error happens when mapping the buffer for
 // reading
-TEST_F(WireBufferMappingTests, ErrorWhileMappingForRead) {
+TEST_F(WireBufferMappingReadTests, ErrorWhileMappingForRead) {
     wgpuBufferMapAsync(buffer, WGPUMapMode_Read, 0, kBufferSize, ToMockBufferMapCallback, nullptr);
 
     EXPECT_CALL(api, OnBufferMapAsync(apiBuffer, WGPUMapMode_Read, 0, kBufferSize, _, _))
@@ -133,7 +148,7 @@
 
 // Check that the map read callback is called with UNKNOWN when the buffer is destroyed before the
 // request is finished
-TEST_F(WireBufferMappingTests, DestroyBeforeReadRequestEnd) {
+TEST_F(WireBufferMappingReadTests, DestroyBeforeReadRequestEnd) {
     wgpuBufferMapAsync(buffer, WGPUMapMode_Read, 0, kBufferSize, ToMockBufferMapCallback, nullptr);
 
     // Return success
@@ -158,7 +173,7 @@
 
 // Check the map read callback is called with "UnmappedBeforeCallback" when the map request would
 // have worked, but Unmap was called
-TEST_F(WireBufferMappingTests, UnmapCalledTooEarlyForRead) {
+TEST_F(WireBufferMappingReadTests, UnmapCalledTooEarlyForRead) {
     wgpuBufferMapAsync(buffer, WGPUMapMode_Read, 0, kBufferSize, ToMockBufferMapCallback, nullptr);
 
     uint32_t bufferContent = 31337;
@@ -186,7 +201,7 @@
 
 // Check that even if Unmap() was called early client-side, we correctly surface server-side
 // validation errors.
-TEST_F(WireBufferMappingTests, UnmapCalledTooEarlyForReadButServerSideError) {
+TEST_F(WireBufferMappingReadTests, UnmapCalledTooEarlyForReadButServerSideError) {
     wgpuBufferMapAsync(buffer, WGPUMapMode_Read, 0, kBufferSize, ToMockBufferMapCallback, nullptr);
 
     EXPECT_CALL(api, OnBufferMapAsync(apiBuffer, WGPUMapMode_Read, 0, kBufferSize, _, _))
@@ -209,7 +224,7 @@
 
 // Check the map read callback is called with "DestroyedBeforeCallback" when the map request would
 // have worked, but Destroy was called
-TEST_F(WireBufferMappingTests, DestroyCalledTooEarlyForRead) {
+TEST_F(WireBufferMappingReadTests, DestroyCalledTooEarlyForRead) {
     wgpuBufferMapAsync(buffer, WGPUMapMode_Read, 0, kBufferSize, ToMockBufferMapCallback, nullptr);
 
     uint32_t bufferContent = 31337;
@@ -237,7 +252,7 @@
 
 // Check that even if Destroy() was called early client-side, we correctly surface server-side
 // validation errors.
-TEST_F(WireBufferMappingTests, DestroyCalledTooEarlyForReadButServerSideError) {
+TEST_F(WireBufferMappingReadTests, DestroyCalledTooEarlyForReadButServerSideError) {
     wgpuBufferMapAsync(buffer, WGPUMapMode_Read, 0, kBufferSize, ToMockBufferMapCallback, nullptr);
 
     EXPECT_CALL(api, OnBufferMapAsync(apiBuffer, WGPUMapMode_Read, 0, kBufferSize, _, _))
@@ -258,8 +273,9 @@
     FlushServer();
 }
 
-// Check that an error map read callback gets nullptr while a buffer is already mapped
-TEST_F(WireBufferMappingTests, MappingForReadingErrorWhileAlreadyMappedGetsNullptr) {
+// Check that an error map read while a buffer is already mapped won't changed the result of get
+// mapped range
+TEST_F(WireBufferMappingReadTests, MappingForReadingErrorWhileAlreadyMappedUnchangeMapData) {
     // Successful map
     wgpuBufferMapAsync(buffer, WGPUMapMode_Read, 0, kBufferSize, ToMockBufferMapCallback, nullptr);
 
@@ -289,11 +305,12 @@
 
     FlushServer();
 
-    EXPECT_EQ(nullptr, wgpuBufferGetConstMappedRange(buffer, 0, kBufferSize));
+    EXPECT_EQ(bufferContent,
+              *static_cast<const uint32_t*>(wgpuBufferGetConstMappedRange(buffer, 0, kBufferSize)));
 }
 
 // Test that the MapReadCallback isn't fired twice when unmap() is called inside the callback
-TEST_F(WireBufferMappingTests, UnmapInsideMapReadCallback) {
+TEST_F(WireBufferMappingReadTests, UnmapInsideMapReadCallback) {
     wgpuBufferMapAsync(buffer, WGPUMapMode_Read, 0, kBufferSize, ToMockBufferMapCallback, nullptr);
 
     uint32_t bufferContent = 31337;
@@ -318,7 +335,7 @@
 
 // Test that the MapReadCallback isn't fired twice the buffer external refcount reaches 0 in the
 // callback
-TEST_F(WireBufferMappingTests, DestroyInsideMapReadCallback) {
+TEST_F(WireBufferMappingReadTests, DestroyInsideMapReadCallback) {
     wgpuBufferMapAsync(buffer, WGPUMapMode_Read, 0, kBufferSize, ToMockBufferMapCallback, nullptr);
 
     uint32_t bufferContent = 31337;
@@ -342,9 +359,21 @@
 }
 
 // Tests specific to mapping for writing
+class WireBufferMappingWriteTests : public WireBufferMappingTests {
+  public:
+    WireBufferMappingWriteTests() {
+    }
+    ~WireBufferMappingWriteTests() override = default;
+
+    void SetUp() override {
+        WireBufferMappingTests::SetUp();
+
+        SetupBuffer(WGPUBufferUsage_MapWrite);
+    }
+};
 
 // Check mapping for writing a succesfully created buffer
-TEST_F(WireBufferMappingTests, MappingForWriteSuccessBuffer) {
+TEST_F(WireBufferMappingWriteTests, MappingForWriteSuccessBuffer) {
     wgpuBufferMapAsync(buffer, WGPUMapMode_Write, 0, kBufferSize, ToMockBufferMapCallback, nullptr);
 
     uint32_t serverBufferContent = 31337;
@@ -382,7 +411,7 @@
 
 // Check that things work correctly when a validation error happens when mapping the buffer for
 // writing
-TEST_F(WireBufferMappingTests, ErrorWhileMappingForWrite) {
+TEST_F(WireBufferMappingWriteTests, ErrorWhileMappingForWrite) {
     wgpuBufferMapAsync(buffer, WGPUMapMode_Write, 0, kBufferSize, ToMockBufferMapCallback, nullptr);
 
     EXPECT_CALL(api, OnBufferMapAsync(apiBuffer, WGPUMapMode_Write, 0, kBufferSize, _, _))
@@ -400,7 +429,7 @@
 
 // Check that the map write callback is called with "DestroyedBeforeCallback" when the buffer is
 // destroyed before the request is finished
-TEST_F(WireBufferMappingTests, DestroyBeforeWriteRequestEnd) {
+TEST_F(WireBufferMappingWriteTests, DestroyBeforeWriteRequestEnd) {
     wgpuBufferMapAsync(buffer, WGPUMapMode_Write, 0, kBufferSize, ToMockBufferMapCallback, nullptr);
 
     // Return success
@@ -423,9 +452,9 @@
     FlushServer();
 }
 
-// Check the map read callback is called with "UnmappedBeforeCallback" when the map request would
+// Check the map write callback is called with "UnmappedBeforeCallback" when the map request would
 // have worked, but Unmap was called
-TEST_F(WireBufferMappingTests, UnmapCalledTooEarlyForWrite) {
+TEST_F(WireBufferMappingWriteTests, UnmapCalledTooEarlyForWrite) {
     wgpuBufferMapAsync(buffer, WGPUMapMode_Write, 0, kBufferSize, ToMockBufferMapCallback, nullptr);
 
     uint32_t bufferContent = 31337;
@@ -447,8 +476,8 @@
     FlushServer();
 }
 
-// Check that an error map read callback gets nullptr while a buffer is already mapped
-TEST_F(WireBufferMappingTests, MappingForWritingErrorWhileAlreadyMappedGetsNullptr) {
+// Check that an error map write while a buffer is already mapped
+TEST_F(WireBufferMappingWriteTests, MappingForWritingErrorWhileAlreadyMapped) {
     // Successful map
     wgpuBufferMapAsync(buffer, WGPUMapMode_Write, 0, kBufferSize, ToMockBufferMapCallback, nullptr);
 
@@ -478,11 +507,12 @@
 
     FlushServer();
 
-    EXPECT_EQ(nullptr, wgpuBufferGetMappedRange(buffer, 0, kBufferSize));
+    EXPECT_NE(nullptr,
+              static_cast<const uint32_t*>(wgpuBufferGetConstMappedRange(buffer, 0, kBufferSize)));
 }
 
 // Test that the MapWriteCallback isn't fired twice when unmap() is called inside the callback
-TEST_F(WireBufferMappingTests, UnmapInsideMapWriteCallback) {
+TEST_F(WireBufferMappingWriteTests, UnmapInsideMapWriteCallback) {
     wgpuBufferMapAsync(buffer, WGPUMapMode_Write, 0, kBufferSize, ToMockBufferMapCallback, nullptr);
 
     uint32_t bufferContent = 31337;
@@ -507,7 +537,7 @@
 
 // Test that the MapWriteCallback isn't fired twice the buffer external refcount reaches 0 in the
 // callback
-TEST_F(WireBufferMappingTests, DestroyInsideMapWriteCallback) {
+TEST_F(WireBufferMappingWriteTests, DestroyInsideMapWriteCallback) {
     wgpuBufferMapAsync(buffer, WGPUMapMode_Write, 0, kBufferSize, ToMockBufferMapCallback, nullptr);
 
     uint32_t bufferContent = 31337;
@@ -578,6 +608,7 @@
 TEST_F(WireBufferMappingTests, MappedAtCreationThenMapSuccess) {
     WGPUBufferDescriptor descriptor = {};
     descriptor.size = 4;
+    descriptor.usage = WGPUMapMode_Write;
     descriptor.mappedAtCreation = true;
 
     WGPUBuffer apiBuffer = api.GetNewBuffer();
@@ -639,7 +670,8 @@
 
     FlushServer();
 
-    EXPECT_EQ(nullptr, wgpuBufferGetConstMappedRange(buffer, 0, kBufferSize));
+    EXPECT_NE(nullptr,
+              static_cast<const uint32_t*>(wgpuBufferGetConstMappedRange(buffer, 0, kBufferSize)));
 
     wgpuBufferUnmap(buffer);
     EXPECT_CALL(api, BufferUnmap(apiBuffer)).Times(1);
@@ -694,6 +726,7 @@
 // Test that registering a callback then wire disconnect calls the callback with
 // DeviceLost.
 TEST_F(WireBufferMappingTests, MapThenDisconnect) {
+    SetupBuffer(WGPUMapMode_Write);
     wgpuBufferMapAsync(buffer, WGPUMapMode_Write, 0, kBufferSize, ToMockBufferMapCallback, this);
 
     EXPECT_CALL(api, OnBufferMapAsync(apiBuffer, WGPUMapMode_Write, 0, kBufferSize, _, _))
@@ -711,6 +744,8 @@
 // Test that registering a callback after wire disconnect calls the callback with
 // DeviceLost.
 TEST_F(WireBufferMappingTests, MapAfterDisconnect) {
+    SetupBuffer(WGPUMapMode_Read);
+
     GetWireClient()->Disconnect();
 
     EXPECT_CALL(*mockBufferMapCallback, Call(WGPUBufferMapAsyncStatus_DeviceLost, this)).Times(1);
diff --git a/src/tests/unittests/wire/WireDestroyObjectTests.cpp b/src/tests/unittests/wire/WireDestroyObjectTests.cpp
index f613051..beb2e03 100644
--- a/src/tests/unittests/wire/WireDestroyObjectTests.cpp
+++ b/src/tests/unittests/wire/WireDestroyObjectTests.cpp
@@ -56,58 +56,3 @@
     wgpuCommandEncoderFinish(encoder, nullptr);
     FlushClient(false);
 }
-
-// Test that calling a function that would generate an InjectError doesn't crash after
-// the device is destroyed.
-TEST_F(WireDestroyObjectTests, ImplicitInjectErrorAfterDestroyDevice) {
-    WGPUBufferDescriptor bufferDesc = {};
-    bufferDesc.size = 4;
-    WGPUBuffer buffer = wgpuDeviceCreateBuffer(device, &bufferDesc);
-
-    WGPUBuffer apiBuffer = api.GetNewBuffer();
-    EXPECT_CALL(api, DeviceCreateBuffer(apiDevice, _)).WillOnce(Return(apiBuffer));
-
-    FlushClient();
-
-    {
-        // Control case: MapAsync errors on invalid WGPUMapMode.
-        MockCallback<WGPUBufferMapCallback> mockBufferMapCallback;
-
-        EXPECT_CALL(api, DeviceInjectError(apiDevice, WGPUErrorType_Validation, _));
-        EXPECT_CALL(mockBufferMapCallback, Call(WGPUBufferMapAsyncStatus_Error, this));
-        wgpuBufferMapAsync(buffer, WGPUMapMode(0), 0, 4, mockBufferMapCallback.Callback(),
-                           mockBufferMapCallback.MakeUserdata(this));
-
-        FlushClient();
-    }
-
-    {
-        // Now, release the device. InjectError shouldn't happen.
-        wgpuDeviceRelease(device);
-        MockCallback<WGPUBufferMapCallback> mockBufferMapCallback;
-
-        EXPECT_CALL(mockBufferMapCallback, Call(WGPUBufferMapAsyncStatus_Error, this + 1));
-        wgpuBufferMapAsync(buffer, WGPUMapMode(0), 0, 4, mockBufferMapCallback.Callback(),
-                           mockBufferMapCallback.MakeUserdata(this + 1));
-
-        Sequence s1, s2;
-        // The device and child objects alre also released.
-        EXPECT_CALL(api, BufferRelease(apiBuffer)).InSequence(s1);
-        EXPECT_CALL(api, QueueRelease(apiQueue)).InSequence(s2);
-        EXPECT_CALL(api, OnDeviceSetUncapturedErrorCallback(apiDevice, nullptr, nullptr))
-            .Times(1)
-            .InSequence(s1, s2);
-        EXPECT_CALL(api, OnDeviceSetLoggingCallback(apiDevice, nullptr, nullptr))
-            .Times(1)
-            .InSequence(s1, s2);
-        EXPECT_CALL(api, OnDeviceSetDeviceLostCallback(apiDevice, nullptr, nullptr))
-            .Times(1)
-            .InSequence(s1, s2);
-        EXPECT_CALL(api, DeviceRelease(apiDevice)).InSequence(s1, s2);
-
-        FlushClient();
-
-        // Signal that we already released and cleared callbacks for |apiDevice|
-        DefaultApiDeviceWasReleased();
-    }
-}
diff --git a/src/tests/unittests/wire/WireMemoryTransferServiceTests.cpp b/src/tests/unittests/wire/WireMemoryTransferServiceTests.cpp
index d372f79..756dcc0 100644
--- a/src/tests/unittests/wire/WireMemoryTransferServiceTests.cpp
+++ b/src/tests/unittests/wire/WireMemoryTransferServiceTests.cpp
@@ -74,8 +74,8 @@
         mMappedBufferContent = 0;
         mUpdatedBufferContent++;
         mSerializeCreateInfo++;
-        mSerializeInitialDataInfo++;
-        mSerializeFlushInfo++;
+        mReadHandleSerializeDataInfo++;
+        mWriteHandleSerializeDataInfo++;
     }
 
     void TearDown() override {
@@ -103,9 +103,10 @@
     using ClientWriteHandle = client::MockMemoryTransferService::MockWriteHandle;
     using ServerWriteHandle = server::MockMemoryTransferService::MockWriteHandle;
 
-    std::pair<WGPUBuffer, WGPUBuffer> CreateBuffer() {
+    std::pair<WGPUBuffer, WGPUBuffer> CreateBuffer(WGPUBufferUsage usage = WGPUBufferUsage_None) {
         WGPUBufferDescriptor descriptor = {};
         descriptor.size = kBufferSize;
+        descriptor.usage = usage;
 
         WGPUBuffer apiBuffer = api.GetNewBuffer();
         WGPUBuffer buffer = wgpuDeviceCreateBuffer(device, &descriptor);
@@ -117,10 +118,12 @@
         return std::make_pair(apiBuffer, buffer);
     }
 
-    std::pair<WGPUBuffer, WGPUBuffer> CreateBufferMapped() {
+    std::pair<WGPUBuffer, WGPUBuffer> CreateBufferMapped(
+        WGPUBufferUsage usage = WGPUBufferUsage_None) {
         WGPUBufferDescriptor descriptor = {};
         descriptor.size = sizeof(mBufferContent);
         descriptor.mappedAtCreation = true;
+        descriptor.usage = usage;
 
         WGPUBuffer apiBuffer = api.GetNewBuffer();
 
@@ -180,42 +183,46 @@
             .WillOnce(InvokeWithoutArgs([&]() { return false; }));
     }
 
-    void ExpectServerReadHandleInitialize(ServerReadHandle* handle) {
-        EXPECT_CALL(serverMemoryTransferService, OnReadHandleSerializeInitialDataSize(handle, _, _))
-            .WillOnce(InvokeWithoutArgs([&]() { return sizeof(mSerializeInitialDataInfo); }));
-        EXPECT_CALL(serverMemoryTransferService, OnReadHandleSerializeInitialData(handle, _, _, _))
-            .WillOnce(WithArg<3>([&](void* serializePointer) {
-                memcpy(serializePointer, &mSerializeInitialDataInfo,
-                       sizeof(mSerializeInitialDataInfo));
-                return sizeof(mSerializeInitialDataInfo);
+    void ExpectServerReadHandleSerializeDataUpdate(ServerReadHandle* handle) {
+        EXPECT_CALL(serverMemoryTransferService,
+                    OnReadHandleSizeOfSerializeDataUpdate(handle, _, _))
+            .WillOnce(InvokeWithoutArgs([&]() { return sizeof(mReadHandleSerializeDataInfo); }));
+        EXPECT_CALL(serverMemoryTransferService,
+                    OnReadHandleSerializeDataUpdate(handle, _, _, _, _))
+            .WillOnce(WithArg<4>([&](void* serializePointer) {
+                memcpy(serializePointer, &mReadHandleSerializeDataInfo,
+                       sizeof(mReadHandleSerializeDataInfo));
+                return sizeof(mReadHandleSerializeDataInfo);
             }));
     }
 
-    void ExpectClientReadHandleDeserializeInitialize(ClientReadHandle* handle,
+    void ExpectClientReadHandleDeserializeDataUpdate(ClientReadHandle* handle,
                                                      uint32_t* mappedData) {
-        EXPECT_CALL(clientMemoryTransferService, OnReadHandleDeserializeInitialData(
-                                                     handle, Pointee(Eq(mSerializeInitialDataInfo)),
-                                                     sizeof(mSerializeInitialDataInfo), _, _))
-            .WillOnce(WithArgs<3, 4>([=](const void** data, size_t* dataLength) {
-                *data = mappedData;
-                *dataLength = sizeof(*mappedData);
-                return true;
-            }));
+        EXPECT_CALL(
+            clientMemoryTransferService,
+            OnReadHandleDeserializeDataUpdate(handle, Pointee(Eq(mReadHandleSerializeDataInfo)),
+                                              sizeof(mReadHandleSerializeDataInfo), _, _))
+            .WillOnce(Return(true));
     }
 
-    void MockClientReadHandleDeserializeInitializeFailure(ClientReadHandle* handle) {
-        EXPECT_CALL(clientMemoryTransferService, OnReadHandleDeserializeInitialData(
-                                                     handle, Pointee(Eq(mSerializeInitialDataInfo)),
-                                                     sizeof(mSerializeInitialDataInfo), _, _))
-            .WillOnce(InvokeWithoutArgs([&]() { return false; }));
+    void MockClientReadHandleDeserializeDataUpdateFailure(ClientReadHandle* handle) {
+        EXPECT_CALL(
+            clientMemoryTransferService,
+            OnReadHandleDeserializeDataUpdate(handle, Pointee(Eq(mReadHandleSerializeDataInfo)),
+                                              sizeof(mReadHandleSerializeDataInfo), _, _))
+            .WillOnce(Return(false));
     }
 
-    ClientWriteHandle* ExpectWriteHandleCreation() {
+    ClientWriteHandle* ExpectWriteHandleCreation(bool mappedAtCreation) {
         // Create the handle first so we can use it in later expectations.
         ClientWriteHandle* handle = clientMemoryTransferService.NewWriteHandle();
 
         EXPECT_CALL(clientMemoryTransferService, OnCreateWriteHandle(sizeof(mBufferContent)))
             .WillOnce(InvokeWithoutArgs([=]() { return handle; }));
+        if (mappedAtCreation) {
+            EXPECT_CALL(clientMemoryTransferService, OnWriteHandleGetData(handle))
+                .WillOnce(Return(&mBufferContent));
+        }
 
         return handle;
     }
@@ -254,46 +261,36 @@
         EXPECT_CALL(serverMemoryTransferService,
                     OnDeserializeWriteHandle(Pointee(Eq(mSerializeCreateInfo)),
                                              sizeof(mSerializeCreateInfo), _))
-            .WillOnce(InvokeWithoutArgs([&]() { return false; }));
+            .WillOnce(Return(false));
     }
 
-    void ExpectClientWriteHandleOpen(ClientWriteHandle* handle, uint32_t* mappedData) {
-        EXPECT_CALL(clientMemoryTransferService, OnWriteHandleOpen(handle))
-            .WillOnce(InvokeWithoutArgs(
-                [=]() { return std::make_pair(mappedData, sizeof(*mappedData)); }));
-    }
-
-    void MockClientWriteHandleOpenFailure(ClientWriteHandle* handle) {
-        EXPECT_CALL(clientMemoryTransferService, OnWriteHandleOpen(handle))
-            .WillOnce(InvokeWithoutArgs([&]() { return std::make_pair(nullptr, 0); }));
-    }
-
-    void ExpectClientWriteHandleSerializeFlush(ClientWriteHandle* handle) {
-        EXPECT_CALL(clientMemoryTransferService, OnWriteHandleSerializeFlushSize(handle))
-            .WillOnce(InvokeWithoutArgs([&]() { return sizeof(mSerializeFlushInfo); }));
-        EXPECT_CALL(clientMemoryTransferService, OnWriteHandleSerializeFlush(handle, _))
+    void ExpectClientWriteHandleSerializeDataUpdate(ClientWriteHandle* handle) {
+        EXPECT_CALL(clientMemoryTransferService,
+                    OnWriteHandleSizeOfSerializeDataUpdate(handle, _, _))
+            .WillOnce(InvokeWithoutArgs([&]() { return sizeof(mWriteHandleSerializeDataInfo); }));
+        EXPECT_CALL(clientMemoryTransferService, OnWriteHandleSerializeDataUpdate(handle, _, _, _))
             .WillOnce(WithArg<1>([&](void* serializePointer) {
-                memcpy(serializePointer, &mSerializeFlushInfo, sizeof(mSerializeFlushInfo));
-                return sizeof(mSerializeFlushInfo);
+                memcpy(serializePointer, &mWriteHandleSerializeDataInfo,
+                       sizeof(mWriteHandleSerializeDataInfo));
+                return sizeof(mWriteHandleSerializeDataInfo);
             }));
     }
 
-    void ExpectServerWriteHandleDeserializeFlush(ServerWriteHandle* handle, uint32_t expectedData) {
-        EXPECT_CALL(serverMemoryTransferService,
-                    OnWriteHandleDeserializeFlush(handle, Pointee(Eq(mSerializeFlushInfo)),
-                                                  sizeof(mSerializeFlushInfo)))
-            .WillOnce(InvokeWithoutArgs([=]() {
-                // The handle data should be updated.
-                EXPECT_EQ(*handle->GetData(), expectedData);
-                return true;
-            }));
+    void ExpectServerWriteHandleDeserializeDataUpdate(ServerWriteHandle* handle,
+                                                      uint32_t expectedData) {
+        EXPECT_CALL(
+            serverMemoryTransferService,
+            OnWriteHandleDeserializeDataUpdate(handle, Pointee(Eq(mWriteHandleSerializeDataInfo)),
+                                               sizeof(mWriteHandleSerializeDataInfo), _, _))
+            .WillOnce(Return(true));
     }
 
-    void MockServerWriteHandleDeserializeFlushFailure(ServerWriteHandle* handle) {
-        EXPECT_CALL(serverMemoryTransferService,
-                    OnWriteHandleDeserializeFlush(handle, Pointee(Eq(mSerializeFlushInfo)),
-                                                  sizeof(mSerializeFlushInfo)))
-            .WillOnce(InvokeWithoutArgs([&]() { return false; }));
+    void MockServerWriteHandleDeserializeDataUpdateFailure(ServerWriteHandle* handle) {
+        EXPECT_CALL(
+            serverMemoryTransferService,
+            OnWriteHandleDeserializeDataUpdate(handle, Pointee(Eq(mWriteHandleSerializeDataInfo)),
+                                               sizeof(mWriteHandleSerializeDataInfo), _, _))
+            .WillOnce(Return(false));
     }
 
     // Arbitrary values used within tests to check if serialized data is correctly passed
@@ -301,8 +298,8 @@
     // test expectations will check that serialized values are passed to the respective
     // deserialization function.
     static uint32_t mSerializeCreateInfo;
-    static uint32_t mSerializeInitialDataInfo;
-    static uint32_t mSerializeFlushInfo;
+    static uint32_t mReadHandleSerializeDataInfo;
+    static uint32_t mWriteHandleSerializeDataInfo;
 
     // Represents the buffer contents for the test.
     static uint32_t mBufferContent;
@@ -324,32 +321,38 @@
 uint32_t WireMemoryTransferServiceTests::mBufferContent = 1337;
 uint32_t WireMemoryTransferServiceTests::mUpdatedBufferContent = 2349;
 uint32_t WireMemoryTransferServiceTests::mSerializeCreateInfo = 4242;
-uint32_t WireMemoryTransferServiceTests::mSerializeInitialDataInfo = 1394;
-uint32_t WireMemoryTransferServiceTests::mSerializeFlushInfo = 1235;
+uint32_t WireMemoryTransferServiceTests::mReadHandleSerializeDataInfo = 1394;
+uint32_t WireMemoryTransferServiceTests::mWriteHandleSerializeDataInfo = 1235;
 
 // Test successful mapping for reading.
 TEST_F(WireMemoryTransferServiceTests, BufferMapReadSuccess) {
     WGPUBuffer buffer;
     WGPUBuffer apiBuffer;
-    std::tie(apiBuffer, buffer) = CreateBuffer();
-    FlushClient();
 
-    // The client should create and serialize a ReadHandle on mapAsync for reading.
+    // The client should create and serialize a ReadHandle on creation.
     ClientReadHandle* clientHandle = ExpectReadHandleCreation();
     ExpectReadHandleSerialization(clientHandle);
 
-    wgpuBufferMapAsync(buffer, WGPUMapMode_Read, 0, kBufferSize, ToMockBufferMapCallback, nullptr);
+    std::tie(apiBuffer, buffer) = CreateBuffer(WGPUBufferUsage_MapRead);
 
     // The server should deserialize the read handle from the client and then serialize
     // an initialization message.
     ServerReadHandle* serverHandle = ExpectServerReadHandleDeserialize();
-    ExpectServerReadHandleInitialize(serverHandle);
+
+    FlushClient();
+
+    wgpuBufferMapAsync(buffer, WGPUMapMode_Read, 0, kBufferSize, ToMockBufferMapCallback, nullptr);
+
+    // The handle serialize data update on mapAsync cmd
+    ExpectServerReadHandleSerializeDataUpdate(serverHandle);
 
     // Mock a successful callback
     EXPECT_CALL(api, OnBufferMapAsync(apiBuffer, WGPUMapMode_Read, 0, kBufferSize, _, _))
         .WillOnce(InvokeWithoutArgs([&]() {
             api.CallBufferMapAsyncCallback(apiBuffer, WGPUBufferMapAsyncStatus_Success);
         }));
+    EXPECT_CALL(clientMemoryTransferService, OnReadHandleGetData(clientHandle))
+        .WillOnce(Return(&mBufferContent));
     EXPECT_CALL(api, BufferGetConstMappedRange(apiBuffer, 0, kBufferSize))
         .WillOnce(Return(&mBufferContent));
 
@@ -358,53 +361,74 @@
     // The client receives a successful callback.
     EXPECT_CALL(*mockBufferMapCallback, Call(WGPUBufferMapAsyncStatus_Success, _)).Times(1);
 
-    // The client should receive the handle initialization message from the server.
-    ExpectClientReadHandleDeserializeInitialize(clientHandle, &mBufferContent);
+    // The client should receive the handle data update message from the server.
+    ExpectClientReadHandleDeserializeDataUpdate(clientHandle, &mBufferContent);
 
     FlushServer();
 
-    // The handle is destroyed once the buffer is unmapped.
-    EXPECT_CALL(clientMemoryTransferService, OnReadHandleDestroy(clientHandle)).Times(1);
     wgpuBufferUnmap(buffer);
-
-    EXPECT_CALL(serverMemoryTransferService, OnReadHandleDestroy(serverHandle)).Times(1);
     EXPECT_CALL(api, BufferUnmap(apiBuffer)).Times(1);
 
     FlushClient();
+
+    // The handle is destroyed once the buffer is destroyed.
+    EXPECT_CALL(clientMemoryTransferService, OnReadHandleDestroy(clientHandle)).Times(1);
+    EXPECT_CALL(serverMemoryTransferService, OnReadHandleDestroy(serverHandle)).Times(1);
+}
+
+// Test ReadHandle destroy behavior
+TEST_F(WireMemoryTransferServiceTests, BufferMapReadDestroy) {
+    WGPUBuffer buffer;
+    WGPUBuffer apiBuffer;
+
+    // The client should create and serialize a ReadHandle on creation.
+    ClientReadHandle* clientHandle = ExpectReadHandleCreation();
+    ExpectReadHandleSerialization(clientHandle);
+
+    std::tie(apiBuffer, buffer) = CreateBuffer(WGPUBufferUsage_MapRead);
+
+    // The server should deserialize the read handle from the client and then serialize
+    // an initialization message.
+    ServerReadHandle* serverHandle = ExpectServerReadHandleDeserialize();
+
+    FlushClient();
+
+    // The handle is destroyed once the buffer is destroyed.
+    EXPECT_CALL(clientMemoryTransferService, OnReadHandleDestroy(clientHandle)).Times(1);
+    wgpuBufferDestroy(buffer);
+    EXPECT_CALL(serverMemoryTransferService, OnReadHandleDestroy(serverHandle)).Times(1);
+    EXPECT_CALL(api, BufferDestroy(apiBuffer)).Times(1);
+
+    FlushClient();
 }
 
 // Test unsuccessful mapping for reading.
 TEST_F(WireMemoryTransferServiceTests, BufferMapReadError) {
     WGPUBuffer buffer;
     WGPUBuffer apiBuffer;
-    std::tie(apiBuffer, buffer) = CreateBuffer();
-    FlushClient();
 
-    // The client should create and serialize a ReadHandle on mapAsync.
+    // The client should create and serialize a ReadHandle on creation.
     ClientReadHandle* clientHandle = ExpectReadHandleCreation();
     ExpectReadHandleSerialization(clientHandle);
 
-    wgpuBufferMapAsync(buffer, WGPUMapMode_Read, 0, kBufferSize, ToMockBufferMapCallback, nullptr);
-
     // The server should deserialize the ReadHandle from the client.
     ServerReadHandle* serverHandle = ExpectServerReadHandleDeserialize();
 
+    std::tie(apiBuffer, buffer) = CreateBuffer(WGPUBufferUsage_MapRead);
+    FlushClient();
+
+    wgpuBufferMapAsync(buffer, WGPUMapMode_Read, 0, kBufferSize, ToMockBufferMapCallback, nullptr);
+
     // Mock a failed callback.
     EXPECT_CALL(api, OnBufferMapAsync(apiBuffer, WGPUMapMode_Read, 0, kBufferSize, _, _))
         .WillOnce(InvokeWithoutArgs(
             [&]() { api.CallBufferMapAsyncCallback(apiBuffer, WGPUBufferMapAsyncStatus_Error); }));
 
-    // Since the mapping failed, the handle is immediately destroyed.
-    EXPECT_CALL(serverMemoryTransferService, OnReadHandleDestroy(serverHandle)).Times(1);
-
     FlushClient();
 
     // The client receives an error callback.
     EXPECT_CALL(*mockBufferMapCallback, Call(WGPUBufferMapAsyncStatus_Error, _)).Times(1);
 
-    // The client receives the map failure and destroys the handle.
-    EXPECT_CALL(clientMemoryTransferService, OnReadHandleDestroy(clientHandle)).Times(1);
-
     FlushServer();
 
     wgpuBufferUnmap(buffer);
@@ -412,68 +436,64 @@
     EXPECT_CALL(api, BufferUnmap(apiBuffer)).Times(1);
 
     FlushClient();
+
+    // The handle is destroyed once the buffer is destroyed.
+    EXPECT_CALL(clientMemoryTransferService, OnReadHandleDestroy(clientHandle)).Times(1);
+    EXPECT_CALL(serverMemoryTransferService, OnReadHandleDestroy(serverHandle)).Times(1);
 }
 
 // Test ReadHandle creation failure.
 TEST_F(WireMemoryTransferServiceTests, BufferMapReadHandleCreationFailure) {
-    WGPUBuffer buffer;
-    WGPUBuffer apiBuffer;
-    std::tie(apiBuffer, buffer) = CreateBuffer();
-    FlushClient();
-
     // Mock a ReadHandle creation failure
     MockReadHandleCreationFailure();
 
-    // Failed creation of a ReadHandle is a mapping failure and the client synchronously receives
-    // an error callback.
-    EXPECT_CALL(*mockBufferMapCallback, Call(WGPUBufferMapAsyncStatus_Error, _)).Times(1);
+    WGPUBufferDescriptor descriptor = {};
+    descriptor.size = kBufferSize;
+    descriptor.usage = WGPUBufferUsage_MapRead;
 
-    wgpuBufferMapAsync(buffer, WGPUMapMode_Read, 0, kBufferSize, ToMockBufferMapCallback, nullptr);
+    wgpuDeviceCreateBuffer(device, &descriptor);
 }
 
 // Test MapRead DeserializeReadHandle failure.
 TEST_F(WireMemoryTransferServiceTests, BufferMapReadDeserializeReadHandleFailure) {
     WGPUBuffer buffer;
     WGPUBuffer apiBuffer;
-    std::tie(apiBuffer, buffer) = CreateBuffer();
-    FlushClient();
 
     // The client should create and serialize a ReadHandle on mapping for reading..
     ClientReadHandle* clientHandle = ExpectReadHandleCreation();
     ExpectReadHandleSerialization(clientHandle);
 
-    wgpuBufferMapAsync(buffer, WGPUMapMode_Read, 0, kBufferSize, ToMockBufferMapCallback, nullptr);
+    std::tie(apiBuffer, buffer) = CreateBuffer(WGPUBufferUsage_MapRead);
 
     // Mock a Deserialization failure.
     MockServerReadHandleDeserializeFailure();
 
     FlushClient(false);
 
-    // The server received a fatal failure and the client callback was never returned.
-    // It is called when the wire is destructed.
-    EXPECT_CALL(*mockBufferMapCallback, Call(WGPUBufferMapAsyncStatus_DestroyedBeforeCallback, _))
-        .Times(1);
-
+    // The handle is destroyed once the buffer is destroyed.
     EXPECT_CALL(clientMemoryTransferService, OnReadHandleDestroy(clientHandle)).Times(1);
 }
 
-// Test read handle DeserializeInitialData failure.
-TEST_F(WireMemoryTransferServiceTests, BufferMapReadDeserializeInitialDataFailure) {
+// Test read handle DeserializeDataUpdate failure.
+TEST_F(WireMemoryTransferServiceTests, BufferMapReadDeserializeDataUpdateFailure) {
     WGPUBuffer buffer;
     WGPUBuffer apiBuffer;
-    std::tie(apiBuffer, buffer) = CreateBuffer();
-    FlushClient();
 
     // The client should create and serialize a ReadHandle on mapping for reading.
     ClientReadHandle* clientHandle = ExpectReadHandleCreation();
     ExpectReadHandleSerialization(clientHandle);
 
-    wgpuBufferMapAsync(buffer, WGPUMapMode_Read, 0, kBufferSize, ToMockBufferMapCallback, nullptr);
-
     // The server should deserialize the read handle from the client and then serialize
     // an initialization message.
     ServerReadHandle* serverHandle = ExpectServerReadHandleDeserialize();
-    ExpectServerReadHandleInitialize(serverHandle);
+
+    std::tie(apiBuffer, buffer) = CreateBuffer(WGPUBufferUsage_MapRead);
+    FlushClient();
+
+    wgpuBufferMapAsync(buffer, WGPUMapMode_Read, 0, kBufferSize, ToMockBufferMapCallback, nullptr);
+
+    // The handle serialize data update on mapAsync cmd
+    ExpectServerReadHandleSerializeDataUpdate(serverHandle);
 
     // Mock a successful callback
     EXPECT_CALL(api, OnBufferMapAsync(apiBuffer, WGPUMapMode_Read, 0, kBufferSize, _, _))
@@ -485,19 +505,18 @@
 
     FlushClient();
 
-    // The client should receive the handle initialization message from the server.
+    // The client should receive the handle data update message from the server.
     // Mock a deserialization failure.
-    MockClientReadHandleDeserializeInitializeFailure(clientHandle);
+    MockClientReadHandleDeserializeDataUpdateFailure(clientHandle);
 
     // Failed deserialization is a fatal failure and the client synchronously receives a
     // DEVICE_LOST callback.
     EXPECT_CALL(*mockBufferMapCallback, Call(WGPUBufferMapAsyncStatus_DeviceLost, _)).Times(1);
 
-    // The handle will be destroyed since deserializing failed.
-    EXPECT_CALL(clientMemoryTransferService, OnReadHandleDestroy(clientHandle)).Times(1);
-
     FlushServer(false);
 
+    // The handle is destroyed once the buffer is destroyed.
+    EXPECT_CALL(clientMemoryTransferService, OnReadHandleDestroy(clientHandle)).Times(1);
     EXPECT_CALL(serverMemoryTransferService, OnReadHandleDestroy(serverHandle)).Times(1);
 }
 
@@ -505,25 +524,30 @@
 TEST_F(WireMemoryTransferServiceTests, BufferMapReadDestroyBeforeUnmap) {
     WGPUBuffer buffer;
     WGPUBuffer apiBuffer;
-    std::tie(apiBuffer, buffer) = CreateBuffer();
-    FlushClient();
 
-    // The client should create and serialize a ReadHandle on mapping for reading.
+    // The client should create and serialize a ReadHandle on mapping for reading..
     ClientReadHandle* clientHandle = ExpectReadHandleCreation();
     ExpectReadHandleSerialization(clientHandle);
 
-    wgpuBufferMapAsync(buffer, WGPUMapMode_Read, 0, kBufferSize, ToMockBufferMapCallback, nullptr);
-
     // The server should deserialize the read handle from the client and then serialize
     // an initialization message.
     ServerReadHandle* serverHandle = ExpectServerReadHandleDeserialize();
-    ExpectServerReadHandleInitialize(serverHandle);
+
+    std::tie(apiBuffer, buffer) = CreateBuffer(WGPUBufferUsage_MapRead);
+    FlushClient();
+
+    wgpuBufferMapAsync(buffer, WGPUMapMode_Read, 0, kBufferSize, ToMockBufferMapCallback, nullptr);
+
+    // The handle serialize data update on mapAsync cmd
+    ExpectServerReadHandleSerializeDataUpdate(serverHandle);
 
     // Mock a successful callback
     EXPECT_CALL(api, OnBufferMapAsync(apiBuffer, WGPUMapMode_Read, 0, kBufferSize, _, _))
         .WillOnce(InvokeWithoutArgs([&]() {
             api.CallBufferMapAsyncCallback(apiBuffer, WGPUBufferMapAsyncStatus_Success);
         }));
+    EXPECT_CALL(clientMemoryTransferService, OnReadHandleGetData(clientHandle))
+        .WillOnce(Return(&mBufferContent));
     EXPECT_CALL(api, BufferGetConstMappedRange(apiBuffer, 0, kBufferSize))
         .WillOnce(Return(&mBufferContent));
 
@@ -532,8 +556,8 @@
     // The client receives a successful callback.
     EXPECT_CALL(*mockBufferMapCallback, Call(WGPUBufferMapAsyncStatus_Success, _)).Times(1);
 
-    // The client should receive the handle initialization message from the server.
-    ExpectClientReadHandleDeserializeInitialize(clientHandle, &mBufferContent);
+    // The client should receive the handle data update message from the server.
+    ExpectClientReadHandleDeserializeDataUpdate(clientHandle, &mBufferContent);
 
     FlushServer();
 
@@ -559,22 +583,26 @@
 TEST_F(WireMemoryTransferServiceTests, BufferMapWriteSuccess) {
     WGPUBuffer buffer;
     WGPUBuffer apiBuffer;
-    std::tie(apiBuffer, buffer) = CreateBuffer();
-    FlushClient();
 
-    ClientWriteHandle* clientHandle = ExpectWriteHandleCreation();
+    ClientWriteHandle* clientHandle = ExpectWriteHandleCreation(false);
     ExpectWriteHandleSerialization(clientHandle);
 
-    wgpuBufferMapAsync(buffer, WGPUMapMode_Write, 0, kBufferSize, ToMockBufferMapCallback, nullptr);
+    std::tie(apiBuffer, buffer) = CreateBuffer(WGPUBufferUsage_MapWrite);
 
     // The server should then deserialize the WriteHandle from the client.
     ServerWriteHandle* serverHandle = ExpectServerWriteHandleDeserialization();
 
+    FlushClient();
+
+    wgpuBufferMapAsync(buffer, WGPUMapMode_Write, 0, kBufferSize, ToMockBufferMapCallback, nullptr);
+
     // Mock a successful callback.
     EXPECT_CALL(api, OnBufferMapAsync(apiBuffer, WGPUMapMode_Write, 0, kBufferSize, _, _))
         .WillOnce(InvokeWithoutArgs([&]() {
             api.CallBufferMapAsyncCallback(apiBuffer, WGPUBufferMapAsyncStatus_Success);
         }));
+    EXPECT_CALL(clientMemoryTransferService, OnWriteHandleGetData(clientHandle))
+        .WillOnce(Return(&mBufferContent));
     EXPECT_CALL(api, BufferGetMappedRange(apiBuffer, 0, kBufferSize))
         .WillOnce(Return(&mMappedBufferContent));
 
@@ -583,62 +611,81 @@
     // The client receives a successful callback.
     EXPECT_CALL(*mockBufferMapCallback, Call(WGPUBufferMapAsyncStatus_Success, _)).Times(1);
 
-    // Since the mapping succeeds, the client opens the WriteHandle.
-    ExpectClientWriteHandleOpen(clientHandle, &mMappedBufferContent);
-
     FlushServer();
 
     // The client writes to the handle contents.
     mMappedBufferContent = mUpdatedBufferContent;
 
-    // The client will then flush and destroy the handle on Unmap()
-    ExpectClientWriteHandleSerializeFlush(clientHandle);
-    EXPECT_CALL(clientMemoryTransferService, OnWriteHandleDestroy(clientHandle)).Times(1);
+    // The client will then serialize data update and destroy the handle on Unmap()
+    ExpectClientWriteHandleSerializeDataUpdate(clientHandle);
 
     wgpuBufferUnmap(buffer);
 
-    // The server deserializes the Flush message.
-    ExpectServerWriteHandleDeserializeFlush(serverHandle, mUpdatedBufferContent);
+    // The server deserializes the data update message.
+    ExpectServerWriteHandleDeserializeDataUpdate(serverHandle, mUpdatedBufferContent);
 
-    // After the handle is updated it can be destroyed.
-    EXPECT_CALL(serverMemoryTransferService, OnWriteHandleDestroy(serverHandle)).Times(1);
     EXPECT_CALL(api, BufferUnmap(apiBuffer)).Times(1);
 
     FlushClient();
+
+    // The handle is destroyed once the buffer is destroyed.
+    EXPECT_CALL(clientMemoryTransferService, OnWriteHandleDestroy(clientHandle)).Times(1);
+    EXPECT_CALL(serverMemoryTransferService, OnWriteHandleDestroy(serverHandle)).Times(1);
+}
+
+// Test WriteHandle destroy behavior
+TEST_F(WireMemoryTransferServiceTests, BufferMapWriteDestroy) {
+    WGPUBuffer buffer;
+    WGPUBuffer apiBuffer;
+
+    ClientWriteHandle* clientHandle = ExpectWriteHandleCreation(false);
+    ExpectWriteHandleSerialization(clientHandle);
+
+    std::tie(apiBuffer, buffer) = CreateBuffer(WGPUBufferUsage_MapWrite);
+
+    // The server should then deserialize the WriteHandle from the client.
+    ServerWriteHandle* serverHandle = ExpectServerWriteHandleDeserialization();
+
+    FlushClient();
+
+    // The handle is destroyed once the buffer is destroyed.
+    EXPECT_CALL(clientMemoryTransferService, OnWriteHandleDestroy(clientHandle)).Times(1);
+    wgpuBufferDestroy(buffer);
+    EXPECT_CALL(serverMemoryTransferService, OnWriteHandleDestroy(serverHandle)).Times(1);
+    EXPECT_CALL(api, BufferDestroy(apiBuffer)).Times(1);
+
+    FlushClient();
 }
 
 // Test unsuccessful MapWrite.
 TEST_F(WireMemoryTransferServiceTests, BufferMapWriteError) {
     WGPUBuffer buffer;
     WGPUBuffer apiBuffer;
-    std::tie(apiBuffer, buffer) = CreateBuffer();
-    FlushClient();
 
-    // The client should create and serialize a WriteHandle on mapping for writing.
-    ClientWriteHandle* clientHandle = ExpectWriteHandleCreation();
+    // The client should create and serialize a WriteHandle on buffer creation with MapWrite usage.
+    ClientWriteHandle* clientHandle = ExpectWriteHandleCreation(false);
     ExpectWriteHandleSerialization(clientHandle);
 
-    wgpuBufferMapAsync(buffer, WGPUMapMode_Write, 0, kBufferSize, ToMockBufferMapCallback, nullptr);
+    std::tie(apiBuffer, buffer) = CreateBuffer(WGPUBufferUsage_MapWrite);
 
     // The server should then deserialize the WriteHandle from the client.
     ServerWriteHandle* serverHandle = ExpectServerWriteHandleDeserialization();
 
+    FlushClient();
+
+    wgpuBufferMapAsync(buffer, WGPUMapMode_Write, 0, kBufferSize, ToMockBufferMapCallback, nullptr);
+
     // Mock an error callback.
     EXPECT_CALL(api, OnBufferMapAsync(apiBuffer, WGPUMapMode_Write, 0, kBufferSize, _, _))
         .WillOnce(InvokeWithoutArgs(
             [&]() { api.CallBufferMapAsyncCallback(apiBuffer, WGPUBufferMapAsyncStatus_Error); }));
 
-    // Since the mapping fails, the handle is immediately destroyed because it won't be written.
-    EXPECT_CALL(serverMemoryTransferService, OnWriteHandleDestroy(serverHandle)).Times(1);
 
     FlushClient();
 
     // The client receives an error callback.
     EXPECT_CALL(*mockBufferMapCallback, Call(WGPUBufferMapAsyncStatus_Error, _)).Times(1);
 
-    // Client receives the map failure and destroys the handle.
-    EXPECT_CALL(clientMemoryTransferService, OnWriteHandleDestroy(clientHandle)).Times(1);
-
     FlushServer();
 
     wgpuBufferUnmap(buffer);
@@ -646,111 +693,68 @@
     EXPECT_CALL(api, BufferUnmap(apiBuffer)).Times(1);
 
     FlushClient();
+
+    // The handle is destroyed once the buffer is destroyed.
+    EXPECT_CALL(clientMemoryTransferService, OnWriteHandleDestroy(clientHandle)).Times(1);
+    EXPECT_CALL(serverMemoryTransferService, OnWriteHandleDestroy(serverHandle)).Times(1);
 }
 
 // Test WriteHandle creation failure.
 TEST_F(WireMemoryTransferServiceTests, BufferMapWriteHandleCreationFailure) {
-    WGPUBuffer buffer;
-    WGPUBuffer apiBuffer;
-    std::tie(apiBuffer, buffer) = CreateBuffer();
-    FlushClient();
-
     // Mock a WriteHandle creation failure
     MockWriteHandleCreationFailure();
 
-    // Failed creation of a WriteHandle is a mapping failure and the client synchronously receives
-    // an error callback.
-    EXPECT_CALL(*mockBufferMapCallback, Call(WGPUBufferMapAsyncStatus_Error, _)).Times(1);
+    WGPUBufferDescriptor descriptor = {};
+    descriptor.size = kBufferSize;
+    descriptor.usage = WGPUBufferUsage_MapWrite;
 
-    wgpuBufferMapAsync(buffer, WGPUMapMode_Write, 0, kBufferSize, ToMockBufferMapCallback, nullptr);
+    wgpuDeviceCreateBuffer(device, &descriptor);
 }
 
 // Test MapWrite DeserializeWriteHandle failure.
 TEST_F(WireMemoryTransferServiceTests, BufferMapWriteDeserializeWriteHandleFailure) {
     WGPUBuffer buffer;
     WGPUBuffer apiBuffer;
-    std::tie(apiBuffer, buffer) = CreateBuffer();
-    FlushClient();
 
-    // The client should create and serialize a WriteHandle on mapping for writing.
-    ClientWriteHandle* clientHandle = ExpectWriteHandleCreation();
+    // The client should create and serialize a WriteHandle on buffer creation with MapWrite usage.
+    ClientWriteHandle* clientHandle = ExpectWriteHandleCreation(false);
     ExpectWriteHandleSerialization(clientHandle);
 
-    wgpuBufferMapAsync(buffer, WGPUMapMode_Write, 0, kBufferSize, ToMockBufferMapCallback, nullptr);
+    std::tie(apiBuffer, buffer) = CreateBuffer(WGPUBufferUsage_MapWrite);
 
     // Mock a deserialization failure.
     MockServerWriteHandleDeserializeFailure();
 
     FlushClient(false);
 
-    // The server hit a fatal failure and never returned the callback. The client callback is
-    // called when the wire is destructed.
-    EXPECT_CALL(*mockBufferMapCallback, Call(WGPUBufferMapAsyncStatus_DestroyedBeforeCallback, _))
-        .Times(1);
-
+    // The handle is destroyed once the buffer is destroyed.
     EXPECT_CALL(clientMemoryTransferService, OnWriteHandleDestroy(clientHandle)).Times(1);
 }
 
-// Test MapWrite handle Open failure.
-TEST_F(WireMemoryTransferServiceTests, BufferMapWriteHandleOpenFailure) {
+// Test MapWrite DeserializeDataUpdate failure.
+TEST_F(WireMemoryTransferServiceTests, BufferMapWriteDeserializeDataUpdateFailure) {
     WGPUBuffer buffer;
     WGPUBuffer apiBuffer;
-    std::tie(apiBuffer, buffer) = CreateBuffer();
-    FlushClient();
 
-    ClientWriteHandle* clientHandle = ExpectWriteHandleCreation();
+    ClientWriteHandle* clientHandle = ExpectWriteHandleCreation(false);
     ExpectWriteHandleSerialization(clientHandle);
 
-    wgpuBufferMapAsync(buffer, WGPUMapMode_Write, 0, kBufferSize, ToMockBufferMapCallback, nullptr);
+    std::tie(apiBuffer, buffer) = CreateBuffer(WGPUBufferUsage_MapWrite);
 
     // The server should then deserialize the WriteHandle from the client.
     ServerWriteHandle* serverHandle = ExpectServerWriteHandleDeserialization();
 
+    FlushClient();
+
+    wgpuBufferMapAsync(buffer, WGPUMapMode_Write, 0, kBufferSize, ToMockBufferMapCallback, nullptr);
+
     // Mock a successful callback.
     EXPECT_CALL(api, OnBufferMapAsync(apiBuffer, WGPUMapMode_Write, 0, kBufferSize, _, _))
         .WillOnce(InvokeWithoutArgs([&]() {
             api.CallBufferMapAsyncCallback(apiBuffer, WGPUBufferMapAsyncStatus_Success);
         }));
-    EXPECT_CALL(api, BufferGetMappedRange(apiBuffer, 0, kBufferSize))
-        .WillOnce(Return(&mMappedBufferContent));
-
-    FlushClient();
-
-    // Since the mapping succeeds, the client opens the WriteHandle.
-    // Mock a failure.
-    MockClientWriteHandleOpenFailure(clientHandle);
-
-    // Failing to open a handle is a fatal failure and the client receives a DEVICE_LOST callback.
-    EXPECT_CALL(*mockBufferMapCallback, Call(WGPUBufferMapAsyncStatus_DeviceLost, _)).Times(1);
-
-    // Since opening the handle fails, it gets destroyed immediately.
-    EXPECT_CALL(clientMemoryTransferService, OnWriteHandleDestroy(clientHandle)).Times(1);
-
-    FlushServer(false);
-
-    EXPECT_CALL(serverMemoryTransferService, OnWriteHandleDestroy(serverHandle)).Times(1);
-}
-
-// Test MapWrite DeserializeFlush failure.
-TEST_F(WireMemoryTransferServiceTests, BufferMapWriteDeserializeFlushFailure) {
-    WGPUBuffer buffer;
-    WGPUBuffer apiBuffer;
-    std::tie(apiBuffer, buffer) = CreateBuffer();
-    FlushClient();
-
-    ClientWriteHandle* clientHandle = ExpectWriteHandleCreation();
-    ExpectWriteHandleSerialization(clientHandle);
-
-    wgpuBufferMapAsync(buffer, WGPUMapMode_Write, 0, kBufferSize, ToMockBufferMapCallback, nullptr);
-
-    // The server should then deserialize the WriteHandle from the client.
-    ServerWriteHandle* serverHandle = ExpectServerWriteHandleDeserialization();
-
-    // Mock a successful callback.
-    EXPECT_CALL(api, OnBufferMapAsync(apiBuffer, WGPUMapMode_Write, 0, kBufferSize, _, _))
-        .WillOnce(InvokeWithoutArgs([&]() {
-            api.CallBufferMapAsyncCallback(apiBuffer, WGPUBufferMapAsyncStatus_Success);
-        }));
+    EXPECT_CALL(clientMemoryTransferService, OnWriteHandleGetData(clientHandle))
+        .WillOnce(Return(&mBufferContent));
     EXPECT_CALL(api, BufferGetMappedRange(apiBuffer, 0, kBufferSize))
         .WillOnce(Return(&mMappedBufferContent));
 
@@ -759,25 +763,23 @@
     // The client receives a success callback.
     EXPECT_CALL(*mockBufferMapCallback, Call(WGPUBufferMapAsyncStatus_Success, _)).Times(1);
 
-    // Since the mapping succeeds, the client opens the WriteHandle.
-    ExpectClientWriteHandleOpen(clientHandle, &mMappedBufferContent);
-
     FlushServer();
 
     // The client writes to the handle contents.
     mMappedBufferContent = mUpdatedBufferContent;
 
-    // The client will then flush and destroy the handle on Unmap()
-    ExpectClientWriteHandleSerializeFlush(clientHandle);
-    EXPECT_CALL(clientMemoryTransferService, OnWriteHandleDestroy(clientHandle)).Times(1);
+    // The client will then serialize data update
+    ExpectClientWriteHandleSerializeDataUpdate(clientHandle);
 
     wgpuBufferUnmap(buffer);
 
-    // The server deserializes the Flush message. Mock a deserialization failure.
-    MockServerWriteHandleDeserializeFlushFailure(serverHandle);
+    // The server deserializes the data update message. Mock a deserialization failure.
+    MockServerWriteHandleDeserializeDataUpdateFailure(serverHandle);
 
     FlushClient(false);
 
+    // The handle is destroyed once the buffer is destroyed.
+    EXPECT_CALL(clientMemoryTransferService, OnWriteHandleDestroy(clientHandle)).Times(1);
     EXPECT_CALL(serverMemoryTransferService, OnWriteHandleDestroy(serverHandle)).Times(1);
 }
 
@@ -785,22 +787,26 @@
 TEST_F(WireMemoryTransferServiceTests, BufferMapWriteDestroyBeforeUnmap) {
     WGPUBuffer buffer;
     WGPUBuffer apiBuffer;
-    std::tie(apiBuffer, buffer) = CreateBuffer();
-    FlushClient();
 
-    ClientWriteHandle* clientHandle = ExpectWriteHandleCreation();
+    ClientWriteHandle* clientHandle = ExpectWriteHandleCreation(false);
     ExpectWriteHandleSerialization(clientHandle);
 
-    wgpuBufferMapAsync(buffer, WGPUMapMode_Write, 0, kBufferSize, ToMockBufferMapCallback, nullptr);
+    std::tie(apiBuffer, buffer) = CreateBuffer(WGPUBufferUsage_MapWrite);
 
     // The server should then deserialize the WriteHandle from the client.
     ServerWriteHandle* serverHandle = ExpectServerWriteHandleDeserialization();
 
+    FlushClient();
+
+    wgpuBufferMapAsync(buffer, WGPUMapMode_Write, 0, kBufferSize, ToMockBufferMapCallback, nullptr);
+
     // Mock a successful callback.
     EXPECT_CALL(api, OnBufferMapAsync(apiBuffer, WGPUMapMode_Write, 0, kBufferSize, _, _))
         .WillOnce(InvokeWithoutArgs([&]() {
             api.CallBufferMapAsyncCallback(apiBuffer, WGPUBufferMapAsyncStatus_Success);
         }));
+    EXPECT_CALL(clientMemoryTransferService, OnWriteHandleGetData(clientHandle))
+        .WillOnce(Return(&mBufferContent));
     EXPECT_CALL(api, BufferGetMappedRange(apiBuffer, 0, kBufferSize))
         .WillOnce(Return(&mMappedBufferContent));
 
@@ -809,9 +815,6 @@
     // The client receives a successful callback.
     EXPECT_CALL(*mockBufferMapCallback, Call(WGPUBufferMapAsyncStatus_Success, _)).Times(1);
 
-    // Since the mapping succeeds, the client opens the WriteHandle.
-    ExpectClientWriteHandleOpen(clientHandle, &mMappedBufferContent);
-
     FlushServer();
 
     // The client writes to the handle contents.
@@ -820,7 +823,9 @@
     // THIS IS THE TEST: destroy the buffer before unmapping and check it destroyed the mapping
     // immediately, both in the client and server side.
     {
+        // The handle is destroyed once the buffer is destroyed.
         EXPECT_CALL(clientMemoryTransferService, OnWriteHandleDestroy(clientHandle)).Times(1);
+
         wgpuBufferDestroy(buffer);
 
         EXPECT_CALL(serverMemoryTransferService, OnWriteHandleDestroy(serverHandle)).Times(1);
@@ -838,11 +843,7 @@
 // Test successful buffer creation with mappedAtCreation = true.
 TEST_F(WireMemoryTransferServiceTests, MappedAtCreationSuccess) {
     // The client should create and serialize a WriteHandle on createBufferMapped.
-    ClientWriteHandle* clientHandle = ExpectWriteHandleCreation();
-
-    // Staging data is immediately available so the handle is Opened.
-    ExpectClientWriteHandleOpen(clientHandle, &mMappedBufferContent);
-
+    ClientWriteHandle* clientHandle = ExpectWriteHandleCreation(true);
     ExpectWriteHandleSerialization(clientHandle);
 
     // The server should then deserialize the WriteHandle from the client.
@@ -856,14 +857,15 @@
     // Update the mapped contents.
     mMappedBufferContent = mUpdatedBufferContent;
 
-    // When the client Unmaps the buffer, it will flush writes to the handle and destroy it.
-    ExpectClientWriteHandleSerializeFlush(clientHandle);
+    // When the client Unmaps the buffer, it will serialize data update writes to the handle and
+    // destroy it.
+    ExpectClientWriteHandleSerializeDataUpdate(clientHandle);
     EXPECT_CALL(clientMemoryTransferService, OnWriteHandleDestroy(clientHandle)).Times(1);
 
     wgpuBufferUnmap(buffer);
 
-    // The server deserializes the Flush message.
-    ExpectServerWriteHandleDeserializeFlush(serverHandle, mUpdatedBufferContent);
+    // The server deserializes the data update message.
+    ExpectServerWriteHandleDeserializeDataUpdate(serverHandle, mUpdatedBufferContent);
 
     // After the handle is updated it can be destroyed.
     EXPECT_CALL(serverMemoryTransferService, OnWriteHandleDestroy(serverHandle)).Times(1);
@@ -888,53 +890,32 @@
 // Test buffer creation with mappedAtCreation DeserializeWriteHandle failure.
 TEST_F(WireMemoryTransferServiceTests, MappedAtCreationDeserializeWriteHandleFailure) {
     // The client should create and serialize a WriteHandle on createBufferMapped.
-    ClientWriteHandle* clientHandle = ExpectWriteHandleCreation();
-
-    // Staging data is immediately available so the handle is Opened.
-    ExpectClientWriteHandleOpen(clientHandle, &mMappedBufferContent);
-
+    ClientWriteHandle* clientHandle = ExpectWriteHandleCreation(true);
     ExpectWriteHandleSerialization(clientHandle);
 
     // The server should then deserialize the WriteHandle from the client.
     MockServerWriteHandleDeserializeFailure();
 
-    WGPUBuffer buffer;
-    WGPUBuffer apiBuffer;
-    std::tie(apiBuffer, buffer) = CreateBufferMapped();
+    WGPUBufferDescriptor descriptor = {};
+    descriptor.size = sizeof(mBufferContent);
+    descriptor.mappedAtCreation = true;
+
+    WGPUBuffer apiBuffer = api.GetNewBuffer();
+
+    wgpuDeviceCreateBuffer(device, &descriptor);
+
+    EXPECT_CALL(api, DeviceCreateBuffer(apiDevice, _)).WillOnce(Return(apiBuffer));
+    // Now bufferGetMappedRange won't be called if deserialize writeHandle fails
+
     FlushClient(false);
 
     EXPECT_CALL(clientMemoryTransferService, OnWriteHandleDestroy(clientHandle)).Times(1);
 }
 
-// Test buffer creation with mappedAtCreation handle Open failure.
-TEST_F(WireMemoryTransferServiceTests, MappedAtCreationHandleOpenFailure) {
-    // The client should create a WriteHandle on createBufferMapped.
-    ClientWriteHandle* clientHandle = ExpectWriteHandleCreation();
-
-    // Staging data is immediately available so the handle is Opened.
-    // Mock a failure.
-    MockClientWriteHandleOpenFailure(clientHandle);
-
-    // Since synchronous opening of the handle failed, it is destroyed immediately.
-    // Note: The handle is not serialized because sychronously opening it failed.
-    EXPECT_CALL(clientMemoryTransferService, OnWriteHandleDestroy(clientHandle)).Times(1);
-
-    WGPUBufferDescriptor descriptor = {};
-    descriptor.size = sizeof(mBufferContent);
-    descriptor.mappedAtCreation = true;
-
-    WGPUBuffer buffer = wgpuDeviceCreateBuffer(device, &descriptor);
-    EXPECT_EQ(nullptr, wgpuBufferGetMappedRange(buffer, 0, sizeof(mBufferContent)));
-}
-
-// Test buffer creation with mappedAtCreation = true DeserializeFlush failure.
-TEST_F(WireMemoryTransferServiceTests, MappedAtCreationDeserializeFlushFailure) {
+// Test buffer creation with mappedAtCreation = true DeserializeDataUpdate failure.
+TEST_F(WireMemoryTransferServiceTests, MappedAtCreationDeserializeDataUpdateFailure) {
     // The client should create and serialize a WriteHandle on createBufferMapped.
-    ClientWriteHandle* clientHandle = ExpectWriteHandleCreation();
-
-    // Staging data is immediately available so the handle is Opened.
-    ExpectClientWriteHandleOpen(clientHandle, &mMappedBufferContent);
-
+    ClientWriteHandle* clientHandle = ExpectWriteHandleCreation(true);
     ExpectWriteHandleSerialization(clientHandle);
 
     // The server should then deserialize the WriteHandle from the client.
@@ -948,28 +929,27 @@
     // Update the mapped contents.
     mMappedBufferContent = mUpdatedBufferContent;
 
-    // When the client Unmaps the buffer, it will flush writes to the handle and destroy it.
-    ExpectClientWriteHandleSerializeFlush(clientHandle);
+    // When the client Unmaps the buffer, it will serialize data update writes to the handle and
+    // destroy it.
+    ExpectClientWriteHandleSerializeDataUpdate(clientHandle);
     EXPECT_CALL(clientMemoryTransferService, OnWriteHandleDestroy(clientHandle)).Times(1);
 
     wgpuBufferUnmap(buffer);
 
-    // The server deserializes the Flush message. Mock a deserialization failure.
-    MockServerWriteHandleDeserializeFlushFailure(serverHandle);
+    // The server deserializes the data update message. Mock a deserialization failure.
+    MockServerWriteHandleDeserializeDataUpdateFailure(serverHandle);
 
     FlushClient(false);
 
+    // Failed BufferUpdateMappedData cmd will early return so BufferUnmap is not processed.
+    // The server side writeHandle is destructed at buffer destruction.
     EXPECT_CALL(serverMemoryTransferService, OnWriteHandleDestroy(serverHandle)).Times(1);
 }
 
 // Test mappedAtCreation=true destroying the buffer before unmapping on the client side.
 TEST_F(WireMemoryTransferServiceTests, MappedAtCreationDestroyBeforeUnmap) {
     // The client should create and serialize a WriteHandle on createBufferMapped.
-    ClientWriteHandle* clientHandle = ExpectWriteHandleCreation();
-
-    // Staging data is immediately available so the handle is Opened.
-    ExpectClientWriteHandleOpen(clientHandle, &mMappedBufferContent);
-
+    ClientWriteHandle* clientHandle = ExpectWriteHandleCreation(true);
     ExpectWriteHandleSerialization(clientHandle);
 
     // The server should then deserialize the WriteHandle from the client.
@@ -1000,3 +980,77 @@
         FlushClient();
     }
 }
+
+// Test a buffer with mappedAtCreation and MapRead usage destroy WriteHandle on unmap and switch
+// data pointer to ReadHandle
+TEST_F(WireMemoryTransferServiceTests, MappedAtCreationAndMapReadSuccess) {
+    // The client should create and serialize a ReadHandle and a WriteHandle on createBufferMapped.
+    ClientReadHandle* clientReadHandle = ExpectReadHandleCreation();
+    ExpectReadHandleSerialization(clientReadHandle);
+    ClientWriteHandle* clientWriteHandle = ExpectWriteHandleCreation(true);
+    ExpectWriteHandleSerialization(clientWriteHandle);
+
+    // The server should then deserialize a ReadHandle and a WriteHandle from the client.
+    ServerReadHandle* serverReadHandle = ExpectServerReadHandleDeserialize();
+    ServerWriteHandle* serverWriteHandle = ExpectServerWriteHandleDeserialization();
+
+    WGPUBuffer buffer;
+    WGPUBuffer apiBuffer;
+    std::tie(apiBuffer, buffer) = CreateBufferMapped(WGPUBufferUsage_MapRead);
+    FlushClient();
+
+    // Update the mapped contents.
+    mMappedBufferContent = mUpdatedBufferContent;
+
+    // When the client Unmaps the buffer, it will serialize data update writes to the handle and
+    // destroy it.
+    ExpectClientWriteHandleSerializeDataUpdate(clientWriteHandle);
+    EXPECT_CALL(clientMemoryTransferService, OnWriteHandleDestroy(clientWriteHandle)).Times(1);
+    EXPECT_CALL(clientMemoryTransferService, OnReadHandleGetData(clientReadHandle))
+        .WillOnce(Return(&mBufferContent));
+    wgpuBufferUnmap(buffer);
+
+    // The server deserializes the data update message.
+    ExpectServerWriteHandleDeserializeDataUpdate(serverWriteHandle, mUpdatedBufferContent);
+    EXPECT_CALL(api, BufferUnmap(apiBuffer)).Times(1);
+    EXPECT_CALL(serverMemoryTransferService, OnWriteHandleDestroy(serverWriteHandle)).Times(1);
+    FlushClient();
+
+    // The ReadHandle will be destoryed on buffer destroy.
+    EXPECT_CALL(clientMemoryTransferService, OnReadHandleDestroy(clientReadHandle)).Times(1);
+    EXPECT_CALL(serverMemoryTransferService, OnReadHandleDestroy(serverReadHandle)).Times(1);
+}
+
+// Test WriteHandle preserves after unmap for a buffer with mappedAtCreation and MapWrite usage
+TEST_F(WireMemoryTransferServiceTests, MappedAtCreationAndMapWriteSuccess) {
+    // The client should create and serialize a WriteHandle on createBufferMapped.
+    ClientWriteHandle* clientHandle = ExpectWriteHandleCreation(true);
+
+    ExpectWriteHandleSerialization(clientHandle);
+
+    // The server should then deserialize the WriteHandle from the client.
+    ServerWriteHandle* serverHandle = ExpectServerWriteHandleDeserialization();
+
+    WGPUBuffer buffer;
+    WGPUBuffer apiBuffer;
+    std::tie(apiBuffer, buffer) = CreateBufferMapped(WGPUBufferUsage_MapWrite);
+    FlushClient();
+
+    // Update the mapped contents.
+    mMappedBufferContent = mUpdatedBufferContent;
+
+    // When the client Unmaps the buffer, it will serialize data update writes to the handle.
+    ExpectClientWriteHandleSerializeDataUpdate(clientHandle);
+
+    wgpuBufferUnmap(buffer);
+
+    // The server deserializes the data update message.
+    ExpectServerWriteHandleDeserializeDataUpdate(serverHandle, mUpdatedBufferContent);
+    EXPECT_CALL(api, BufferUnmap(apiBuffer)).Times(1);
+
+    FlushClient();
+
+    // The writeHandle is preserved after unmap and is destroyed once the buffer is destroyed.
+    EXPECT_CALL(clientMemoryTransferService, OnWriteHandleDestroy(clientHandle)).Times(1);
+    EXPECT_CALL(serverMemoryTransferService, OnWriteHandleDestroy(serverHandle)).Times(1);
+}
\ No newline at end of file