Move client-side OnCompletion callbacks to the server.

We need callbacks to be processed server-side so that callback
ordering can be made consistent.

Bug: dawn:516
Change-Id: Ie5590ca33fce6bda431f93ae9ff8e832468109c1
Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/27481
Commit-Queue: Austin Eng <enga@chromium.org>
Reviewed-by: Kai Ninomiya <kainino@chromium.org>
diff --git a/src/dawn_wire/client/ClientDoers.cpp b/src/dawn_wire/client/ClientDoers.cpp
index 7fdabc0..c13f715 100644
--- a/src/dawn_wire/client/ClientDoers.cpp
+++ b/src/dawn_wire/client/ClientDoers.cpp
@@ -70,4 +70,16 @@
         return true;
     }
 
+    bool Client::DoFenceOnCompletionCallback(Fence* fence,
+                                             uint64_t requestSerial,
+                                             WGPUFenceCompletionStatus status) {
+        // The fence might have been deleted or recreated so this isn't an error.
+        if (fence == nullptr) {
+            return true;
+        }
+
+        fence->OnCompletionCallback(requestSerial, status);
+        return true;
+    }
+
 }}  // namespace dawn_wire::client
diff --git a/src/dawn_wire/client/Fence.cpp b/src/dawn_wire/client/Fence.cpp
index d9e1fa7..329998f 100644
--- a/src/dawn_wire/client/Fence.cpp
+++ b/src/dawn_wire/client/Fence.cpp
@@ -14,6 +14,7 @@
 
 #include "dawn_wire/client/Fence.h"
 
+#include "dawn_wire/client/Client.h"
 #include "dawn_wire/client/Device.h"
 
 namespace dawn_wire { namespace client {
@@ -21,67 +22,61 @@
     Fence::~Fence() {
         // Callbacks need to be fired in all cases, as they can handle freeing resources
         // so we call them with "Unknown" status.
-        for (auto& request : mRequests.IterateAll()) {
-            request.completionCallback(WGPUFenceCompletionStatus_Unknown, request.userdata);
+        for (auto& it : mOnCompletionRequests) {
+            if (it.second.callback) {
+                it.second.callback(WGPUFenceCompletionStatus_Unknown, it.second.userdata);
+            }
         }
-        mRequests.Clear();
+        mOnCompletionRequests.clear();
     }
 
     void Fence::Initialize(Queue* queue, const WGPUFenceDescriptor* descriptor) {
         mQueue = queue;
 
-        uint64_t initialValue = descriptor != nullptr ? descriptor->initialValue : 0u;
-        mSignaledValue = initialValue;
-        mCompletedValue = initialValue;
-    }
-
-    void Fence::CheckPassedFences() {
-        for (auto& request : mRequests.IterateUpTo(mCompletedValue)) {
-            request.completionCallback(WGPUFenceCompletionStatus_Success, request.userdata);
-        }
-        mRequests.ClearUpTo(mCompletedValue);
+        mCompletedValue = descriptor != nullptr ? descriptor->initialValue : 0u;
     }
 
     void Fence::OnCompletion(uint64_t value,
                              WGPUFenceOnCompletionCallback callback,
                              void* userdata) {
-        if (value > mSignaledValue) {
-            device->InjectError(WGPUErrorType_Validation,
-                                "Value greater than fence signaled value");
-            callback(WGPUFenceCompletionStatus_Error, userdata);
-            return;
-        }
+        uint32_t serial = mOnCompletionRequestSerial++;
+        ASSERT(mOnCompletionRequests.find(serial) == mOnCompletionRequests.end());
 
-        if (value <= mCompletedValue) {
-            callback(WGPUFenceCompletionStatus_Success, userdata);
-            return;
-        }
+        FenceOnCompletionCmd cmd;
+        cmd.fenceId = this->id;
+        cmd.value = value;
+        cmd.requestSerial = serial;
 
-        Fence::OnCompletionData request;
-        request.completionCallback = callback;
-        request.userdata = userdata;
-        mRequests.Enqueue(std::move(request), value);
+        mOnCompletionRequests[serial] = {callback, userdata};
+
+        this->device->GetClient()->SerializeCommand(cmd);
     }
 
     void Fence::OnUpdateCompletedValueCallback(uint64_t value) {
         mCompletedValue = value;
-        CheckPassedFences();
+    }
+
+    bool Fence::OnCompletionCallback(uint64_t requestSerial, WGPUFenceCompletionStatus status) {
+        auto requestIt = mOnCompletionRequests.find(requestSerial);
+        if (requestIt == mOnCompletionRequests.end()) {
+            return false;
+        }
+
+        // Remove the request data so that the callback cannot be called again.
+        // ex.) inside the callback: if the fence is deleted, all callbacks reject.
+        OnCompletionData request = std::move(requestIt->second);
+        mOnCompletionRequests.erase(requestIt);
+
+        request.callback(status, request.userdata);
+        return true;
     }
 
     uint64_t Fence::GetCompletedValue() const {
         return mCompletedValue;
     }
 
-    uint64_t Fence::GetSignaledValue() const {
-        return mSignaledValue;
-    }
-
     Queue* Fence::GetQueue() const {
         return mQueue;
     }
 
-    void Fence::SetSignaledValue(uint64_t value) {
-        mSignaledValue = value;
-    }
-
 }}  // namespace dawn_wire::client
diff --git a/src/dawn_wire/client/Fence.h b/src/dawn_wire/client/Fence.h
index 107b4e7..58d89c3 100644
--- a/src/dawn_wire/client/Fence.h
+++ b/src/dawn_wire/client/Fence.h
@@ -33,22 +33,20 @@
         void CheckPassedFences();
         void OnCompletion(uint64_t value, WGPUFenceOnCompletionCallback callback, void* userdata);
         void OnUpdateCompletedValueCallback(uint64_t value);
+        bool OnCompletionCallback(uint64_t requestSerial, WGPUFenceCompletionStatus status);
 
         uint64_t GetCompletedValue() const;
-        uint64_t GetSignaledValue() const;
         Queue* GetQueue() const;
 
-        void SetSignaledValue(uint64_t value);
-
       private:
         struct OnCompletionData {
-            WGPUFenceOnCompletionCallback completionCallback = nullptr;
+            WGPUFenceOnCompletionCallback callback = nullptr;
             void* userdata = nullptr;
         };
         Queue* mQueue = nullptr;
-        uint64_t mSignaledValue = 0;
         uint64_t mCompletedValue = 0;
-        SerialMap<OnCompletionData> mRequests;
+        uint64_t mOnCompletionRequestSerial = 0;
+        std::map<uint64_t, OnCompletionData> mOnCompletionRequests;
     };
 
 }}  // namespace dawn_wire::client
diff --git a/src/dawn_wire/client/Queue.cpp b/src/dawn_wire/client/Queue.cpp
index ad11673..52de14c 100644
--- a/src/dawn_wire/client/Queue.cpp
+++ b/src/dawn_wire/client/Queue.cpp
@@ -33,29 +33,6 @@
         return ToAPI(fence);
     }
 
-    void Queue::Signal(WGPUFence cFence, uint64_t signalValue) {
-        Fence* fence = FromAPI(cFence);
-        if (fence->GetQueue() != this) {
-            device->InjectError(WGPUErrorType_Validation,
-                                "Fence must be signaled on the queue on which it was created.");
-            return;
-        }
-        if (signalValue <= fence->GetSignaledValue()) {
-            device->InjectError(WGPUErrorType_Validation,
-                                "Fence value less than or equal to signaled value");
-            return;
-        }
-
-        fence->SetSignaledValue(signalValue);
-
-        QueueSignalCmd cmd;
-        cmd.self = ToAPI(this);
-        cmd.fence = cFence;
-        cmd.signalValue = signalValue;
-
-        device->GetClient()->SerializeCommand(cmd);
-    }
-
     void Queue::WriteBuffer(WGPUBuffer cBuffer,
                             uint64_t bufferOffset,
                             const void* data,
diff --git a/src/dawn_wire/client/Queue.h b/src/dawn_wire/client/Queue.h
index 866bccd..9e50348 100644
--- a/src/dawn_wire/client/Queue.h
+++ b/src/dawn_wire/client/Queue.h
@@ -29,7 +29,6 @@
         using ObjectBase::ObjectBase;
 
         WGPUFence CreateFence(const WGPUFenceDescriptor* descriptor);
-        void Signal(WGPUFence fence, uint64_t signalValue);
         void WriteBuffer(WGPUBuffer cBuffer, uint64_t bufferOffset, const void* data, size_t size);
         void WriteTexture(const WGPUTextureCopyView* destination,
                           const void* data,
diff --git a/src/dawn_wire/server/Server.h b/src/dawn_wire/server/Server.h
index 8049e80..2859714 100644
--- a/src/dawn_wire/server/Server.h
+++ b/src/dawn_wire/server/Server.h
@@ -48,6 +48,12 @@
         uint64_t value;
     };
 
+    struct FenceOnCompletionUserdata {
+        Server* server;
+        ObjectHandle fence;
+        uint64_t requestSerial;
+    };
+
     class Server : public ServerBase {
       public:
         Server(WGPUDevice device,
@@ -77,6 +83,7 @@
         static void ForwardPopErrorScope(WGPUErrorType type, const char* message, void* userdata);
         static void ForwardBufferMapAsync(WGPUBufferMapAsyncStatus status, void* userdata);
         static void ForwardFenceCompletedValue(WGPUFenceCompletionStatus status, void* userdata);
+        static void ForwardFenceOnCompletion(WGPUFenceCompletionStatus status, void* userdata);
 
         // Error callbacks
         void OnUncapturedError(WGPUErrorType type, const char* message);
@@ -87,6 +94,8 @@
         void OnBufferMapAsyncCallback(WGPUBufferMapAsyncStatus status, MapUserdata* userdata);
         void OnFenceCompletedValueUpdated(WGPUFenceCompletionStatus status,
                                           FenceCompletionUserdata* userdata);
+        void OnFenceOnCompletion(WGPUFenceCompletionStatus status,
+                                 FenceOnCompletionUserdata* userdata);
 
 #include "dawn_wire/server/ServerPrototypes_autogen.inc"
 
diff --git a/src/dawn_wire/server/ServerFence.cpp b/src/dawn_wire/server/ServerFence.cpp
index 1a4105c..cea561e 100644
--- a/src/dawn_wire/server/ServerFence.cpp
+++ b/src/dawn_wire/server/ServerFence.cpp
@@ -38,4 +38,42 @@
         SerializeCommand(cmd);
     }
 
+    bool Server::DoFenceOnCompletion(ObjectId fenceId, uint64_t value, uint64_t requestSerial) {
+        // The null object isn't valid as `self`
+        if (fenceId == 0) {
+            return false;
+        }
+
+        auto* fence = FenceObjects().Get(fenceId);
+        if (fence == nullptr) {
+            return false;
+        }
+
+        FenceOnCompletionUserdata* userdata = new FenceOnCompletionUserdata;
+        userdata->server = this;
+        userdata->fence = ObjectHandle{fenceId, fence->generation};
+        userdata->requestSerial = requestSerial;
+
+        mProcs.fenceOnCompletion(fence->handle, value, ForwardFenceOnCompletion, userdata);
+        return true;
+    }
+
+    // static
+    void Server::ForwardFenceOnCompletion(WGPUFenceCompletionStatus status, void* userdata) {
+        auto* data = reinterpret_cast<FenceOnCompletionUserdata*>(userdata);
+        data->server->OnFenceOnCompletion(status, data);
+    }
+
+    void Server::OnFenceOnCompletion(WGPUFenceCompletionStatus status,
+                                     FenceOnCompletionUserdata* userdata) {
+        std::unique_ptr<FenceOnCompletionUserdata> data{userdata};
+
+        ReturnFenceOnCompletionCallbackCmd cmd;
+        cmd.fence = data->fence;
+        cmd.requestSerial = data->requestSerial;
+        cmd.status = status;
+
+        SerializeCommand(cmd);
+    }
+
 }}  // namespace dawn_wire::server
diff --git a/src/dawn_wire/server/ServerQueue.cpp b/src/dawn_wire/server/ServerQueue.cpp
index 0c5a6b8..b6d2903 100644
--- a/src/dawn_wire/server/ServerQueue.cpp
+++ b/src/dawn_wire/server/ServerQueue.cpp
@@ -21,7 +21,6 @@
         if (cFence == nullptr) {
             return false;
         }
-
         mProcs.queueSignal(cSelf, cFence, signalValue);
 
         ObjectId fenceId = FenceObjectIdTable().Get(cFence);