[WGPUFuture] Implement and use Futures in wire for requestAdapter.

Bug: dawn:1987, dawn:2060
Change-Id: Ia5030ce122b0da20ad08af3489500ffc8aac8684
Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/153921
Kokoro: Kokoro <noreply+kokoro@google.com>
Commit-Queue: Loko Kung <lokokung@google.com>
Reviewed-by: Austin Eng <enga@chromium.org>
Reviewed-by: Kai Ninomiya <kainino@chromium.org>
diff --git a/dawn.json b/dawn.json
index 9aa4089..6a5c97e 100644
--- a/dawn.json
+++ b/dawn.json
@@ -72,6 +72,24 @@
             {"name": "compatibility mode", "type": "bool", "default": "false", "tags": ["dawn", "emscripten"]}
         ]
     },
+    "request adapter callback": {
+        "category": "function pointer",
+        "args": [
+            {"name": "status", "type": "request adapter status"},
+            {"name": "adapter", "type": "adapter", "optional": true},
+            {"name": "message", "type": "char", "annotation": "const*", "length": "strlen", "optional": true},
+            {"name": "userdata", "type": "void *"}
+        ]
+    },
+    "request adapter callback info": {
+         "category": "structure",
+         "extensible": "in",
+         "members": [
+            {"name": "mode", "type": "callback mode"},
+            {"name": "callback", "type": "request adapter callback"},
+            {"name": "userdata", "type": "void *"}
+         ]
+    },
     "request adapter status": {
         "category": "enum",
         "emscripten_no_enum_table": true,
@@ -82,15 +100,6 @@
             {"value": 3, "name": "unknown"}
         ]
     },
-    "request adapter callback": {
-        "category": "function pointer",
-        "args": [
-            {"name": "status", "type": "request adapter status"},
-            {"name": "adapter", "type": "adapter", "optional": true},
-            {"name": "message", "type": "char", "annotation": "const*", "length": "strlen", "optional": true},
-            {"name": "userdata", "type": "void *"}
-        ]
-    },
     "adapter": {
         "category": "object",
         "no autolock": true,
@@ -578,7 +587,7 @@
             {"name": "mode", "type": "callback mode"},
             {"name": "callback", "type": "buffer map callback"},
             {"name": "userdata", "type": "void *"}
-	]
+        ]
     },
     "buffer map async status": {
         "category": "enum",
@@ -2009,6 +2018,16 @@
                     {"name": "callback", "type": "request adapter callback"},
                     {"name": "userdata", "type": "void *"}
                 ]
+            },
+            {
+                "name": "request adapter f",
+                "_comment": "TODO(crbug.com/dawn/2021): This is dawn/emscripten-only until we rename it to replace the old API. See bug for details.",
+                "tags": ["dawn", "emscripten"],
+                "returns": "future",
+                "args": [
+                    {"name": "options", "type": "request adapter options", "annotation": "const*", "optional": true, "no_default": true},
+                    {"name": "callback info", "type": "request adapter callback info"}
+                ]
             }
         ]
     },
diff --git a/dawn_wire.json b/dawn_wire.json
index 8815d41..cc541ed 100644
--- a/dawn_wire.json
+++ b/dawn_wire.json
@@ -99,7 +99,7 @@
         ],
         "instance request adapter": [
             { "name": "instance id", "type": "ObjectId", "id_type": "instance" },
-            { "name": "request serial", "type": "uint64_t" },
+            { "name": "future", "type": "future" },
             { "name": "adapter object handle", "type": "ObjectHandle", "handle_type": "adapter"},
             { "name": "options", "type": "request adapter options", "annotation": "const*", "optional": true }
         ],
@@ -164,7 +164,7 @@
         ],
         "instance request adapter callback": [
             { "name": "instance", "type": "ObjectHandle", "handle_type": "instance" },
-            { "name": "request serial", "type": "uint64_t" },
+            { "name": "future", "type": "future" },
             { "name": "status", "type": "request adapter status" },
             { "name": "message", "type": "char", "annotation": "const*", "length": "strlen", "optional": true },
             { "name": "properties", "type": "adapter properties", "annotation": "const*", "optional": "true" },
@@ -217,6 +217,7 @@
             "DeviceSetUncapturedErrorCallback",
             "DeviceSetLoggingCallback",
             "InstanceRequestAdapter",
+            "InstanceRequestAdapterF",
             "ShaderModuleGetCompilationInfo",
             "QuerySetGetType",
             "QuerySetGetCount",
diff --git a/src/dawn/native/Instance.cpp b/src/dawn/native/Instance.cpp
index d058ff5..2a9069b 100644
--- a/src/dawn/native/Instance.cpp
+++ b/src/dawn/native/Instance.cpp
@@ -240,6 +240,12 @@
     }
 }
 
+Future InstanceBase::APIRequestAdapterF(const RequestAdapterOptions* options,
+                                        const RequestAdapterCallbackInfo& callbackInfo) {
+    // TODO(dawn:1987) Implement this.
+    DAWN_UNREACHABLE();
+}
+
 Ref<AdapterBase> InstanceBase::CreateAdapter(Ref<PhysicalDeviceBase> physicalDevice,
                                              FeatureLevel featureLevel,
                                              const DawnTogglesDescriptor* requiredAdapterToggles,
diff --git a/src/dawn/native/Instance.h b/src/dawn/native/Instance.h
index 56b3e60..8a74906 100644
--- a/src/dawn/native/Instance.h
+++ b/src/dawn/native/Instance.h
@@ -76,6 +76,8 @@
     void APIRequestAdapter(const RequestAdapterOptions* options,
                            WGPURequestAdapterCallback callback,
                            void* userdata);
+    Future APIRequestAdapterF(const RequestAdapterOptions* options,
+                              const RequestAdapterCallbackInfo& callbackInfo);
 
     // Discovers and returns a vector of adapters.
     // All systems adapters that can be found are returned if no options are passed.
diff --git a/src/dawn/tests/unittests/wire/WireInstanceTests.cpp b/src/dawn/tests/unittests/wire/WireInstanceTests.cpp
index 5fdf01f..59a2594 100644
--- a/src/dawn/tests/unittests/wire/WireInstanceTests.cpp
+++ b/src/dawn/tests/unittests/wire/WireInstanceTests.cpp
@@ -29,6 +29,7 @@
 #include <vector>
 
 #include "dawn/tests/MockCallback.h"
+#include "dawn/tests/unittests/wire/WireFutureTest.h"
 #include "dawn/tests/unittests/wire/WireTest.h"
 
 #include "dawn/wire/WireClient.h"
@@ -42,6 +43,7 @@
 using testing::Invoke;
 using testing::InvokeWithoutArgs;
 using testing::MockCallback;
+using testing::NiceMock;
 using testing::NotNull;
 using testing::Return;
 using testing::SetArgPointee;
@@ -49,28 +51,6 @@
 using testing::WithArg;
 
 class WireInstanceBasicTest : public WireTest {};
-class WireInstanceTests : public WireTest {
-  protected:
-    void SetUp() override {
-        WireTest::SetUp();
-
-        auto reservation = GetWireClient()->ReserveInstance();
-        instance = wgpu::Instance::Acquire(reservation.instance);
-
-        apiInstance = api.GetNewInstance();
-        EXPECT_CALL(api, InstanceReference(apiInstance));
-        EXPECT_TRUE(
-            GetWireServer()->InjectInstance(apiInstance, reservation.id, reservation.generation));
-    }
-
-    void TearDown() override {
-        instance = nullptr;
-        WireTest::TearDown();
-    }
-
-    wgpu::Instance instance;
-    WGPUInstance apiInstance;
-};
 
 // Test that an Instance can be reserved and injected into the wire.
 TEST_F(WireInstanceBasicTest, ReserveAndInject) {
@@ -88,37 +68,55 @@
     FlushClient();
 }
 
-// Test that RequestAdapterOptions are passed from the client to the server.
-TEST_F(WireInstanceTests, RequestAdapterPassesOptions) {
-    MockCallback<WGPURequestAdapterCallback> cb;
-    auto* userdata = cb.MakeUserdata(this);
+using WireInstanceTestBase = WireFutureTestWithParams<WGPURequestAdapterCallback,
+                                                      WGPURequestAdapterCallbackInfo,
+                                                      wgpuInstanceRequestAdapter,
+                                                      wgpuInstanceRequestAdapterF>;
+class WireInstanceTests : public WireInstanceTestBase {
+  protected:
+    // Overriden version of wgpuInstanceRequestAdapter that defers to the API call based on the
+    // test callback mode.
+    void InstanceRequestAdapter(WGPUInstance i,
+                                WGPURequestAdapterOptions const* options,
+                                void* userdata = nullptr) {
+        CallImpl(userdata, i, options);
+    }
+};
 
-    for (wgpu::PowerPreference powerPreference :
-         {wgpu::PowerPreference::LowPower, wgpu::PowerPreference::HighPerformance}) {
-        wgpu::RequestAdapterOptions options = {};
+DAWN_INSTANTIATE_WIRE_FUTURE_TEST_P(WireInstanceTests);
+
+// Test that RequestAdapterOptions are passed from the client to the server.
+TEST_P(WireInstanceTests, RequestAdapterPassesOptions) {
+    for (WGPUPowerPreference powerPreference :
+         {WGPUPowerPreference_LowPower, WGPUPowerPreference_HighPerformance}) {
+        WGPURequestAdapterOptions options = {};
         options.powerPreference = powerPreference;
 
-        instance.RequestAdapter(&options, cb.Callback(), userdata);
+        InstanceRequestAdapter(instance, &options, nullptr);
 
         EXPECT_CALL(api, OnInstanceRequestAdapter(apiInstance, NotNull(), NotNull(), NotNull()))
             .WillOnce(WithArg<1>(Invoke([&](const WGPURequestAdapterOptions* apiOptions) {
                 EXPECT_EQ(apiOptions->powerPreference,
                           static_cast<WGPUPowerPreference>(options.powerPreference));
                 EXPECT_EQ(apiOptions->forceFallbackAdapter, options.forceFallbackAdapter);
+                api.CallInstanceRequestAdapterCallback(apiInstance, WGPURequestAdapterStatus_Error,
+                                                       nullptr, nullptr);
             })));
-        FlushClient();
-    }
 
-    // Delete the instance now, or it'll call the mock callback after it's deleted.
-    instance = nullptr;
+        FlushClient();
+        FlushFutures();
+        ExpectWireCallbacksWhen([&](auto& mockCb) {
+            EXPECT_CALL(mockCb, Call).Times(1);
+
+            FlushCallbacks();
+        });
+    }
 }
 
 // Test that RequestAdapter forwards the adapter information to the client.
-TEST_F(WireInstanceTests, RequestAdapterSuccess) {
-    wgpu::RequestAdapterOptions options = {};
-    MockCallback<WGPURequestAdapterCallback> cb;
-    auto* userdata = cb.MakeUserdata(this);
-    instance.RequestAdapter(&options, cb.Callback(), userdata);
+TEST_P(WireInstanceTests, RequestAdapterSuccess) {
+    WGPURequestAdapterOptions options = {};
+    InstanceRequestAdapter(instance, &options, nullptr);
 
     WGPUAdapterProperties fakeProperties = {};
     fakeProperties.vendorID = 0x134;
@@ -130,13 +128,14 @@
     fakeProperties.backendType = WGPUBackendType_D3D12;
     fakeProperties.adapterType = WGPUAdapterType_IntegratedGPU;
 
-    wgpu::SupportedLimits fakeLimits = {};
+    WGPUSupportedLimits fakeLimits = {};
+    fakeLimits.nextInChain = nullptr;
     fakeLimits.limits.maxTextureDimension1D = 433;
     fakeLimits.limits.maxVertexAttributes = 1243;
 
-    std::initializer_list<wgpu::FeatureName> fakeFeatures = {
-        wgpu::FeatureName::Depth32FloatStencil8,
-        wgpu::FeatureName::TextureCompressionBC,
+    std::initializer_list<WGPUFeatureName> fakeFeatures = {
+        WGPUFeatureName_Depth32FloatStencil8,
+        WGPUFeatureName_TextureCompressionBC,
     };
 
     // Expect the server to receive the message. Then, mock a fake reply.
@@ -148,7 +147,7 @@
 
             EXPECT_CALL(api, AdapterGetLimits(apiAdapter, NotNull()))
                 .WillOnce(WithArg<1>(Invoke([&](WGPUSupportedLimits* limits) {
-                    *reinterpret_cast<wgpu::SupportedLimits*>(limits) = fakeLimits;
+                    *limits = fakeLimits;
                     return true;
                 })));
 
@@ -157,63 +156,64 @@
 
             EXPECT_CALL(api, AdapterEnumerateFeatures(apiAdapter, NotNull()))
                 .WillOnce(WithArg<1>(Invoke([&](WGPUFeatureName* features) {
-                    for (wgpu::FeatureName feature : fakeFeatures) {
-                        *(features++) = static_cast<WGPUFeatureName>(feature);
+                    for (WGPUFeatureName feature : fakeFeatures) {
+                        *(features++) = feature;
                     }
                     return fakeFeatures.size();
                 })));
             api.CallInstanceRequestAdapterCallback(apiInstance, WGPURequestAdapterStatus_Success,
                                                    apiAdapter, nullptr);
         }));
+
     FlushClient();
+    FlushFutures();
 
     // Expect the callback in the client and all the adapter information to match.
-    EXPECT_CALL(cb, Call(WGPURequestAdapterStatus_Success, NotNull(), nullptr, this))
-        .WillOnce(WithArg<1>(Invoke([&](WGPUAdapter cAdapter) {
-            wgpu::Adapter adapter = wgpu::Adapter::Acquire(cAdapter);
+    ExpectWireCallbacksWhen([&](auto& mockCb) {
+        EXPECT_CALL(mockCb, Call(WGPURequestAdapterStatus_Success, NotNull(), nullptr, nullptr))
+            .WillOnce(WithArg<1>(Invoke([&](WGPUAdapter adapter) {
+                WGPUAdapterProperties properties = {};
+                wgpuAdapterGetProperties(adapter, &properties);
+                EXPECT_EQ(properties.vendorID, fakeProperties.vendorID);
+                EXPECT_STREQ(properties.vendorName, fakeProperties.vendorName);
+                EXPECT_STREQ(properties.architecture, fakeProperties.architecture);
+                EXPECT_EQ(properties.deviceID, fakeProperties.deviceID);
+                EXPECT_STREQ(properties.name, fakeProperties.name);
+                EXPECT_STREQ(properties.driverDescription, fakeProperties.driverDescription);
+                EXPECT_EQ(properties.backendType, fakeProperties.backendType);
+                EXPECT_EQ(properties.adapterType, fakeProperties.adapterType);
 
-            wgpu::AdapterProperties properties;
-            adapter.GetProperties(&properties);
-            const auto& rhs = *reinterpret_cast<wgpu::AdapterProperties*>(&fakeProperties);
-            EXPECT_EQ(properties.vendorID, rhs.vendorID);
-            EXPECT_STREQ(properties.vendorName, rhs.vendorName);
-            EXPECT_STREQ(properties.architecture, rhs.architecture);
-            EXPECT_EQ(properties.deviceID, rhs.deviceID);
-            EXPECT_STREQ(properties.name, rhs.name);
-            EXPECT_STREQ(properties.driverDescription, rhs.driverDescription);
-            EXPECT_EQ(properties.backendType, rhs.backendType);
-            EXPECT_EQ(properties.adapterType, rhs.adapterType);
+                WGPUSupportedLimits limits = {};
+                EXPECT_TRUE(wgpuAdapterGetLimits(adapter, &limits));
+                EXPECT_EQ(limits.limits.maxTextureDimension1D,
+                          fakeLimits.limits.maxTextureDimension1D);
+                EXPECT_EQ(limits.limits.maxVertexAttributes, fakeLimits.limits.maxVertexAttributes);
 
-            wgpu::SupportedLimits limits;
-            EXPECT_TRUE(adapter.GetLimits(&limits));
-            EXPECT_EQ(limits.limits.maxTextureDimension1D, fakeLimits.limits.maxTextureDimension1D);
-            EXPECT_EQ(limits.limits.maxVertexAttributes, fakeLimits.limits.maxVertexAttributes);
+                std::vector<WGPUFeatureName> features;
+                features.resize(wgpuAdapterEnumerateFeatures(adapter, nullptr));
+                ASSERT_EQ(features.size(), fakeFeatures.size());
+                EXPECT_EQ(wgpuAdapterEnumerateFeatures(adapter, &features[0]), features.size());
 
-            std::vector<wgpu::FeatureName> features;
-            features.resize(adapter.EnumerateFeatures(nullptr));
-            ASSERT_EQ(features.size(), fakeFeatures.size());
-            EXPECT_EQ(adapter.EnumerateFeatures(&features[0]), features.size());
+                std::unordered_set<WGPUFeatureName> featureSet(fakeFeatures);
+                for (WGPUFeatureName feature : features) {
+                    EXPECT_EQ(featureSet.erase(feature), 1u);
+                }
+            })));
 
-            std::unordered_set<wgpu::FeatureName> featureSet(fakeFeatures);
-            for (wgpu::FeatureName feature : features) {
-                EXPECT_EQ(featureSet.erase(feature), 1u);
-            }
-        })));
-    FlushServer();
+        FlushCallbacks();
+    });
 }
 
-// Test that features returned by the implementation that aren't supported
-// in the wire are not exposed.
-TEST_F(WireInstanceTests, RequestAdapterWireLacksFeatureSupport) {
-    wgpu::RequestAdapterOptions options = {};
-    MockCallback<WGPURequestAdapterCallback> cb;
-    auto* userdata = cb.MakeUserdata(this);
-    instance.RequestAdapter(&options, cb.Callback(), userdata);
+// Test that features returned by the implementation that aren't supported in the wire are not
+// exposed.
+TEST_P(WireInstanceTests, RequestAdapterWireLacksFeatureSupport) {
+    WGPURequestAdapterOptions options = {};
+    InstanceRequestAdapter(instance, &options, nullptr);
 
-    std::initializer_list<wgpu::FeatureName> fakeFeatures = {
-        wgpu::FeatureName::Depth32FloatStencil8,
+    std::initializer_list<WGPUFeatureName> fakeFeatures = {
+        WGPUFeatureName_Depth32FloatStencil8,
         // Some value that is not a valid feature
-        static_cast<wgpu::FeatureName>(-2),
+        static_cast<WGPUFeatureName>(-2),
     };
 
     // Expect the server to receive the message. Then, mock a fake reply.
@@ -240,36 +240,36 @@
 
             EXPECT_CALL(api, AdapterEnumerateFeatures(apiAdapter, NotNull()))
                 .WillOnce(WithArg<1>(Invoke([&](WGPUFeatureName* features) {
-                    for (wgpu::FeatureName feature : fakeFeatures) {
-                        *(features++) = static_cast<WGPUFeatureName>(feature);
+                    for (WGPUFeatureName feature : fakeFeatures) {
+                        *(features++) = feature;
                     }
                     return fakeFeatures.size();
                 })));
             api.CallInstanceRequestAdapterCallback(apiInstance, WGPURequestAdapterStatus_Success,
                                                    apiAdapter, nullptr);
         }));
+
     FlushClient();
+    FlushFutures();
 
     // Expect the callback in the client and all the adapter information to match.
-    EXPECT_CALL(cb, Call(WGPURequestAdapterStatus_Success, NotNull(), nullptr, this))
-        .WillOnce(WithArg<1>(Invoke([&](WGPUAdapter cAdapter) {
-            wgpu::Adapter adapter = wgpu::Adapter::Acquire(cAdapter);
+    ExpectWireCallbacksWhen([&](auto& mockCb) {
+        EXPECT_CALL(mockCb, Call(WGPURequestAdapterStatus_Success, NotNull(), nullptr, nullptr))
+            .WillOnce(WithArg<1>(Invoke([&](WGPUAdapter adapter) {
+                WGPUFeatureName feature;
+                ASSERT_EQ(wgpuAdapterEnumerateFeatures(adapter, nullptr), 1u);
+                wgpuAdapterEnumerateFeatures(adapter, &feature);
+                EXPECT_EQ(feature, WGPUFeatureName_Depth32FloatStencil8);
+            })));
 
-            wgpu::FeatureName feature;
-            ASSERT_EQ(adapter.EnumerateFeatures(nullptr), 1u);
-            adapter.EnumerateFeatures(&feature);
-
-            EXPECT_EQ(feature, wgpu::FeatureName::Depth32FloatStencil8);
-        })));
-    FlushServer();
+        FlushCallbacks();
+    });
 }
 
 // Test that RequestAdapter errors forward to the client.
-TEST_F(WireInstanceTests, RequestAdapterError) {
-    wgpu::RequestAdapterOptions options = {};
-    MockCallback<WGPURequestAdapterCallback> cb;
-    auto* userdata = cb.MakeUserdata(this);
-    instance.RequestAdapter(&options, cb.Callback(), userdata);
+TEST_P(WireInstanceTests, RequestAdapterError) {
+    WGPURequestAdapterOptions options = {};
+    InstanceRequestAdapter(instance, &options, nullptr);
 
     // Expect the server to receive the message. Then, mock an error.
     EXPECT_CALL(api, OnInstanceRequestAdapter(apiInstance, NotNull(), NotNull(), NotNull()))
@@ -277,36 +277,49 @@
             api.CallInstanceRequestAdapterCallback(apiInstance, WGPURequestAdapterStatus_Error,
                                                    nullptr, "Some error");
         }));
+
     FlushClient();
+    FlushFutures();
 
     // Expect the callback in the client.
-    EXPECT_CALL(cb, Call(WGPURequestAdapterStatus_Error, nullptr, StrEq("Some error"), this))
-        .Times(1);
-    FlushServer();
+    ExpectWireCallbacksWhen([&](auto& mockCb) {
+        EXPECT_CALL(mockCb,
+                    Call(WGPURequestAdapterStatus_Error, nullptr, StrEq("Some error"), nullptr))
+            .Times(1);
+
+        FlushCallbacks();
+    });
 }
 
-// Test that RequestAdapter receives unknown status if the instance is deleted
-// before the callback happens.
-TEST_F(WireInstanceTests, RequestAdapterInstanceDestroyedBeforeCallback) {
-    wgpu::RequestAdapterOptions options = {};
-    MockCallback<WGPURequestAdapterCallback> cb;
-    auto* userdata = cb.MakeUserdata(this);
-    instance.RequestAdapter(&options, cb.Callback(), userdata);
+// Test that RequestAdapter receives unknown status if the instance is deleted before the callback
+// happens.
+TEST_P(WireInstanceTests, DISABLED_RequestAdapterInstanceDestroyedBeforeCallback) {
+    // TODO(crbug.com/dawn/2061) This test does not currently pass because the callbacks aren't
+    // actually triggered by the destruction of the instance at the moment. Once we move the
+    // EventManager to be per-Instance, this test should pass.
+    WGPURequestAdapterOptions options = {};
+    InstanceRequestAdapter(instance, &options, nullptr);
 
-    EXPECT_CALL(cb, Call(WGPURequestAdapterStatus_Unknown, nullptr, NotNull(), this)).Times(1);
-    instance = nullptr;
+    ExpectWireCallbacksWhen([&](auto& mockCb) {
+        EXPECT_CALL(mockCb, Call(WGPURequestAdapterStatus_Unknown, nullptr, NotNull(), nullptr))
+            .Times(1);
+
+        wgpuInstanceRelease(instance);
+    });
 }
 
 // Test that RequestAdapter receives unknown status if the wire is disconnected
 // before the callback happens.
-TEST_F(WireInstanceTests, RequestAdapterWireDisconnectBeforeCallback) {
-    wgpu::RequestAdapterOptions options = {};
-    MockCallback<WGPURequestAdapterCallback> cb;
-    auto* userdata = cb.MakeUserdata(this);
-    instance.RequestAdapter(&options, cb.Callback(), userdata);
+TEST_P(WireInstanceTests, RequestAdapterWireDisconnectBeforeCallback) {
+    WGPURequestAdapterOptions options = {};
+    InstanceRequestAdapter(instance, &options, nullptr);
 
-    EXPECT_CALL(cb, Call(WGPURequestAdapterStatus_Unknown, nullptr, NotNull(), this)).Times(1);
-    GetWireClient()->Disconnect();
+    ExpectWireCallbacksWhen([&](auto& mockCb) {
+        EXPECT_CALL(mockCb, Call(WGPURequestAdapterStatus_Unknown, nullptr, NotNull(), nullptr))
+            .Times(1);
+
+        GetWireClient()->Disconnect();
+    });
 }
 
 }  // anonymous namespace
diff --git a/src/dawn/wire/client/Client.cpp b/src/dawn/wire/client/Client.cpp
index 0112574..52ebab8 100644
--- a/src/dawn/wire/client/Client.cpp
+++ b/src/dawn/wire/client/Client.cpp
@@ -61,8 +61,8 @@
 }
 
 Client::~Client() {
-    DestroyAllObjects();
     mEventManager->ShutDown();
+    DestroyAllObjects();
 }
 
 void Client::DestroyAllObjects() {
diff --git a/src/dawn/wire/client/EventManager.h b/src/dawn/wire/client/EventManager.h
index 277c088..8c891ce 100644
--- a/src/dawn/wire/client/EventManager.h
+++ b/src/dawn/wire/client/EventManager.h
@@ -46,13 +46,16 @@
 
 enum class EventType {
     MapAsync,
+    RequestAdapter,
     WorkDone,
 };
 
-// Implementations of TrackedEvents must implement the CompleteImpl and ReadyHook functions. In most
-// scenarios, the CompleteImpl function should call the callbacks while the ReadyHook should process
-// and copy memory (if necessary) from the wire deserialization buffer into a local copy that can be
-// readily used by the user callback.
+// Implementations of TrackedEvents must implement the GetType, CompleteImpl, and ReadyHook
+// functions. In most scenarios, the CompleteImpl function should call the callbacks while the
+// ReadyHook should process and copy memory (if necessary) from the wire deserialization buffer
+// into a local copy that can be readily used by the user callback. Specifically, the wire
+// deserialization data is guaranteed to be alive when the ReadyHook is called, but not when
+// CompleteImpl is called.
 class TrackedEvent : NonMovable {
   public:
     explicit TrackedEvent(WGPUCallbackMode mode);
diff --git a/src/dawn/wire/client/Instance.cpp b/src/dawn/wire/client/Instance.cpp
index aa36c1f..f88b467 100644
--- a/src/dawn/wire/client/Instance.cpp
+++ b/src/dawn/wire/client/Instance.cpp
@@ -27,10 +27,80 @@
 
 #include "dawn/wire/client/Instance.h"
 
+#include <memory>
+#include <string>
+#include <utility>
+
+#include "dawn/common/Log.h"
 #include "dawn/wire/client/Client.h"
 #include "dawn/wire/client/EventManager.h"
 
 namespace dawn::wire::client {
+namespace {
+
+class RequestAdapterEvent : public TrackedEvent {
+  public:
+    static constexpr EventType kType = EventType::RequestAdapter;
+
+    RequestAdapterEvent(const WGPURequestAdapterCallbackInfo& callbackInfo, Adapter* adapter)
+        : TrackedEvent(callbackInfo.mode),
+          mCallback(callbackInfo.callback),
+          mUserdata(callbackInfo.userdata),
+          mAdapter(adapter) {}
+
+    EventType GetType() override { return kType; }
+
+    void ReadyHook(WGPURequestAdapterStatus status,
+                   const char* message,
+                   const WGPUAdapterProperties* properties,
+                   const WGPUSupportedLimits* limits,
+                   uint32_t featuresCount,
+                   const WGPUFeatureName* features) {
+        DAWN_ASSERT(mAdapter != nullptr);
+        mStatus = status;
+        if (message != nullptr) {
+            mMessage = message;
+        }
+        if (status == WGPURequestAdapterStatus_Success) {
+            mAdapter->SetProperties(properties);
+            mAdapter->SetLimits(limits);
+            mAdapter->SetFeatures(features, featuresCount);
+        }
+    }
+
+  private:
+    void CompleteImpl(FutureID futureID, EventCompletionType completionType) override {
+        if (completionType == EventCompletionType::Shutdown) {
+            mStatus = WGPURequestAdapterStatus_Unknown;
+            mMessage = "GPU connection lost";
+        }
+        if (mStatus != WGPURequestAdapterStatus_Success && mAdapter != nullptr) {
+            // If there was an error, we may need to reclaim the adapter allocation, otherwise the
+            // adapter is returned to the user who owns it.
+            mAdapter->GetClient()->Free(mAdapter);
+            mAdapter = nullptr;
+        }
+        if (mCallback) {
+            mCallback(mStatus, ToAPI(mAdapter), mMessage ? mMessage->c_str() : nullptr, mUserdata);
+        }
+    }
+
+    WGPURequestAdapterCallback mCallback;
+    void* mUserdata;
+
+    // Note that the message is optional because we want to return nullptr when it wasn't set
+    // instead of a pointer to an empty string.
+    WGPURequestAdapterStatus mStatus;
+    std::optional<std::string> mMessage;
+
+    // The adapter is created when we call RequestAdapter(F). It is guaranteed to be alive
+    // throughout the duration of a RequestAdapterEvent because the Event essentially takes
+    // ownership of it until either an error occurs at which point the Event cleans it up, or it
+    // returns the adapter to the user who then takes ownership as the Event goes away.
+    Adapter* mAdapter = nullptr;
+};
+
+}  // anonymous namespace
 
 // Free-standing API functions
 
@@ -51,43 +121,38 @@
 
 // Instance
 
-Instance::~Instance() {
-    mRequestAdapterRequests.CloseAll([](RequestAdapterData* request) {
-        request->callback(WGPURequestAdapterStatus_Unknown, nullptr,
-                          "Instance destroyed before callback", request->userdata);
-    });
-}
-
-void Instance::CancelCallbacksForDisconnect() {
-    mRequestAdapterRequests.CloseAll([](RequestAdapterData* request) {
-        request->callback(WGPURequestAdapterStatus_Unknown, nullptr, "GPU connection lost",
-                          request->userdata);
-    });
-}
-
 void Instance::RequestAdapter(const WGPURequestAdapterOptions* options,
                               WGPURequestAdapterCallback callback,
                               void* userdata) {
-    Client* client = GetClient();
-    if (client->IsDisconnected()) {
-        callback(WGPURequestAdapterStatus_Error, nullptr, "GPU connection lost", userdata);
-        return;
-    }
+    WGPURequestAdapterCallbackInfo callbackInfo = {};
+    callbackInfo.mode = WGPUCallbackMode_AllowSpontaneous;
+    callbackInfo.callback = callback;
+    callbackInfo.userdata = userdata;
+    RequestAdapterF(options, callbackInfo);
+}
 
+WGPUFuture Instance::RequestAdapterF(const WGPURequestAdapterOptions* options,
+                                     const WGPURequestAdapterCallbackInfo& callbackInfo) {
+    Client* client = GetClient();
     Adapter* adapter = client->Make<Adapter>();
-    uint64_t serial = mRequestAdapterRequests.Add({callback, adapter->GetWireId(), userdata});
+    auto [futureIDInternal, tracked] = client->GetEventManager()->TrackEvent(
+        std::make_unique<RequestAdapterEvent>(callbackInfo, adapter));
+    if (!tracked) {
+        return {futureIDInternal};
+    }
 
     InstanceRequestAdapterCmd cmd;
     cmd.instanceId = GetWireId();
-    cmd.requestSerial = serial;
+    cmd.future = {futureIDInternal};
     cmd.adapterObjectHandle = adapter->GetWireHandle();
     cmd.options = options;
 
     client->SerializeCommand(cmd);
+    return {futureIDInternal};
 }
 
 bool Client::DoInstanceRequestAdapterCallback(Instance* instance,
-                                              uint64_t requestSerial,
+                                              WGPUFuture future,
                                               WGPURequestAdapterStatus status,
                                               const char* message,
                                               const WGPUAdapterProperties* properties,
@@ -98,39 +163,20 @@
     if (instance == nullptr) {
         return true;
     }
-    return instance->OnRequestAdapterCallback(requestSerial, status, message, properties, limits,
+    return instance->OnRequestAdapterCallback(future, status, message, properties, limits,
                                               featuresCount, features);
 }
 
-bool Instance::OnRequestAdapterCallback(uint64_t requestSerial,
+bool Instance::OnRequestAdapterCallback(WGPUFuture future,
                                         WGPURequestAdapterStatus status,
                                         const char* message,
                                         const WGPUAdapterProperties* properties,
                                         const WGPUSupportedLimits* limits,
                                         uint32_t featuresCount,
                                         const WGPUFeatureName* features) {
-    RequestAdapterData request;
-    if (!mRequestAdapterRequests.Acquire(requestSerial, &request)) {
-        return false;
-    }
-
-    Client* client = GetClient();
-    Adapter* adapter = client->Get<Adapter>(request.adapterObjectId);
-
-    // If the return status is a failure we should give a null adapter to the callback and
-    // free the allocation.
-    if (status != WGPURequestAdapterStatus_Success) {
-        client->Free(adapter);
-        request.callback(status, nullptr, message, request.userdata);
-        return true;
-    }
-
-    adapter->SetProperties(properties);
-    adapter->SetLimits(limits);
-    adapter->SetFeatures(features, featuresCount);
-
-    request.callback(status, ToAPI(adapter), message, request.userdata);
-    return true;
+    return GetClient()->GetEventManager()->SetFutureReady<RequestAdapterEvent>(
+               future.id, status, message, properties, limits, featuresCount, features) ==
+           WireResult::Success;
 }
 
 void Instance::ProcessEvents() {
diff --git a/src/dawn/wire/client/Instance.h b/src/dawn/wire/client/Instance.h
index 4186392..9c9f92d 100644
--- a/src/dawn/wire/client/Instance.h
+++ b/src/dawn/wire/client/Instance.h
@@ -42,14 +42,13 @@
 class Instance final : public ObjectBase {
   public:
     using ObjectBase::ObjectBase;
-    ~Instance() override;
-
-    void CancelCallbacksForDisconnect() override;
 
     void RequestAdapter(const WGPURequestAdapterOptions* options,
                         WGPURequestAdapterCallback callback,
                         void* userdata);
-    bool OnRequestAdapterCallback(uint64_t requestSerial,
+    WGPUFuture RequestAdapterF(const WGPURequestAdapterOptions* options,
+                               const WGPURequestAdapterCallbackInfo& callbackInfo);
+    bool OnRequestAdapterCallback(WGPUFuture future,
                                   WGPURequestAdapterStatus status,
                                   const char* message,
                                   const WGPUAdapterProperties* properties,
@@ -59,14 +58,6 @@
 
     void ProcessEvents();
     WGPUWaitStatus WaitAny(size_t count, WGPUFutureWaitInfo* infos, uint64_t timeoutNS);
-
-  private:
-    struct RequestAdapterData {
-        WGPURequestAdapterCallback callback = nullptr;
-        ObjectId adapterObjectId;
-        void* userdata = nullptr;
-    };
-    RequestTracker<RequestAdapterData> mRequestAdapterRequests;
 };
 
 }  // namespace dawn::wire::client
diff --git a/src/dawn/wire/server/Server.h b/src/dawn/wire/server/Server.h
index d6bce51..e20a300 100644
--- a/src/dawn/wire/server/Server.h
+++ b/src/dawn/wire/server/Server.h
@@ -144,7 +144,7 @@
     using CallbackUserdata::CallbackUserdata;
 
     ObjectHandle instance;
-    uint64_t requestSerial;
+    WGPUFuture future;
     ObjectId adapterObjectId;
 };
 
diff --git a/src/dawn/wire/server/ServerInstance.cpp b/src/dawn/wire/server/ServerInstance.cpp
index 1f4b5fa..446e089 100644
--- a/src/dawn/wire/server/ServerInstance.cpp
+++ b/src/dawn/wire/server/ServerInstance.cpp
@@ -34,7 +34,7 @@
 namespace dawn::wire::server {
 
 WireResult Server::DoInstanceRequestAdapter(Known<WGPUInstance> instance,
-                                            uint64_t requestSerial,
+                                            WGPUFuture future,
                                             ObjectHandle adapterHandle,
                                             const WGPURequestAdapterOptions* options) {
     Known<WGPUAdapter> adapter;
@@ -42,7 +42,7 @@
 
     auto userdata = MakeUserdata<RequestAdapterUserdata>();
     userdata->instance = instance.AsHandle();
-    userdata->requestSerial = requestSerial;
+    userdata->future = future;
     userdata->adapterObjectId = adapter.id;
 
     mProcs.instanceRequestAdapter(instance->handle, options,
@@ -57,7 +57,7 @@
                                       const char* message) {
     ReturnInstanceRequestAdapterCallbackCmd cmd = {};
     cmd.instance = data->instance;
-    cmd.requestSerial = data->requestSerial;
+    cmd.future = data->future;
     cmd.status = status;
     cmd.message = message;