Implement RequestAdapterF and RequestDeviceF in native/wire

 - implement RequestAdapterF and RequestDeviceF in native
 - implement RequestDeviceF in dawn wire
 - rewrite legacy non-future methods on top of the future ones
 - Updates wire and native adpater/device creation tests to
   test all future callback modes, as well as the legacy path

Bug: dawn:1987
Change-Id: I70495d2d59a2bedd90e39d177a4c6a0666e2fdbd
Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/166220
Reviewed-by: Shrek Shao <shrekshao@google.com>
Commit-Queue: Austin Eng <enga@chromium.org>
Reviewed-by: Loko Kung <lokokung@google.com>
Kokoro: Kokoro <noreply+kokoro@google.com>
diff --git a/src/dawn/dawn.json b/src/dawn/dawn.json
index 73424db..e4f23b1 100644
--- a/src/dawn/dawn.json
+++ b/src/dawn/dawn.json
@@ -145,6 +145,16 @@
                 ]
             },
             {
+                "name": "request device 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": "device descriptor", "annotation": "const*", "optional": true, "no_default": true},
+                    {"name": "callback info", "type": "request device callback info"}
+                ]
+            },
+            {
                 "name": "create device",
                 "tags": ["dawn"],
                 "returns": "device",
@@ -2891,6 +2901,15 @@
             {"name": "userdata", "type": "void *"}
         ]
     },
+    "request device callback info": {
+         "category": "structure",
+         "extensible": "in",
+         "members": [
+            {"name": "mode", "type": "callback mode"},
+            {"name": "callback", "type": "request device callback"},
+            {"name": "userdata", "type": "void *"}
+         ]
+    },
 
     "request device status": {
         "category": "enum",
diff --git a/src/dawn/dawn_wire.json b/src/dawn/dawn_wire.json
index d0ded98..f37d6ac 100644
--- a/src/dawn/dawn_wire.json
+++ b/src/dawn/dawn_wire.json
@@ -108,7 +108,8 @@
         ],
         "adapter request device": [
             { "name": "adapter id", "type": "ObjectId", "id_type": "adapter" },
-            { "name": "request serial", "type": "uint64_t" },
+            { "name": "event manager handle", "type": "ObjectHandle" },
+            { "name": "future", "type": "future" },
             { "name": "device object handle", "type": "ObjectHandle", "handle_type": "device"},
             { "name": "descriptor", "type": "device descriptor", "annotation": "const*" }
         ]
@@ -177,8 +178,8 @@
             { "name": "features", "type": "feature name", "annotation": "const*", "length": "features count"}
         ],
         "adapter request device callback": [
-            { "name": "adapter", "type": "ObjectHandle", "handle_type": "adapter" },
-            { "name": "request serial", "type": "uint64_t" },
+            { "name": "event manager", "type": "ObjectHandle" },
+            { "name": "future", "type": "future" },
             { "name": "status", "type": "request device status" },
             { "name": "message", "type": "char", "annotation": "const*", "length": "strlen", "optional": true },
             { "name": "limits", "type": "supported limits", "annotation": "const*", "optional": "true" },
@@ -203,6 +204,7 @@
             "AdapterHasFeature",
             "AdapterEnumerateFeatures",
             "AdapterRequestDevice",
+            "AdapterRequestDeviceF",
             "BufferMapAsync",
             "BufferMapAsyncF",
             "BufferGetConstMappedRange",
diff --git a/src/dawn/native/Adapter.cpp b/src/dawn/native/Adapter.cpp
index 32f4f49..e34ad50 100644
--- a/src/dawn/native/Adapter.cpp
+++ b/src/dawn/native/Adapter.cpp
@@ -262,6 +262,49 @@
     callback(status, ToAPI(ReturnToAPI(std::move(device))), nullptr, userdata);
 }
 
+Future AdapterBase::APIRequestDeviceF(const DeviceDescriptor* descriptor,
+                                      const RequestDeviceCallbackInfo& callbackInfo) {
+    struct RequestDeviceEvent final : public EventManager::TrackedEvent {
+        WGPURequestDeviceCallback mCallback;
+        void* mUserdata;
+        ResultOrError<Ref<DeviceBase>> mDeviceOrError;
+
+        RequestDeviceEvent(const RequestDeviceCallbackInfo& callbackInfo,
+                           ResultOrError<Ref<DeviceBase>> deviceOrError)
+            : TrackedEvent(callbackInfo.mode, TrackedEvent::Completed{}),
+              mCallback(callbackInfo.callback),
+              mUserdata(callbackInfo.userdata),
+              mDeviceOrError(std::move(deviceOrError)) {
+            CompleteIfSpontaneous();
+        }
+
+        ~RequestDeviceEvent() override { EnsureComplete(EventCompletionType::Shutdown); }
+
+        void Complete(EventCompletionType completionType) override {
+            if (mDeviceOrError.IsError()) {
+                std::unique_ptr<ErrorData> errorData = mDeviceOrError.AcquireError();
+                mCallback(WGPURequestDeviceStatus_Error, nullptr,
+                          errorData->GetFormattedMessage().c_str(), mUserdata);
+                return;
+            }
+            Ref<DeviceBase> device = mDeviceOrError.AcquireSuccess();
+            WGPURequestDeviceStatus status = device == nullptr ? WGPURequestDeviceStatus_Unknown
+                                                               : WGPURequestDeviceStatus_Success;
+            mCallback(status, ToAPI(device.Detach()), nullptr, mUserdata);
+        }
+    };
+
+    constexpr DeviceDescriptor kDefaultDescriptor = {};
+    if (descriptor == nullptr) {
+        descriptor = &kDefaultDescriptor;
+    }
+
+    FutureID futureID = mPhysicalDevice->GetInstance()->GetEventManager()->TrackEvent(
+        callbackInfo.mode,
+        AcquireRef(new RequestDeviceEvent(callbackInfo, CreateDevice(descriptor))));
+    return {futureID};
+}
+
 const TogglesState& AdapterBase::GetTogglesState() const {
     return mTogglesState;
 }
diff --git a/src/dawn/native/Adapter.h b/src/dawn/native/Adapter.h
index 6170358..2f8a405 100644
--- a/src/dawn/native/Adapter.h
+++ b/src/dawn/native/Adapter.h
@@ -59,6 +59,8 @@
     void APIRequestDevice(const DeviceDescriptor* descriptor,
                           WGPURequestDeviceCallback callback,
                           void* userdata);
+    Future APIRequestDeviceF(const DeviceDescriptor* descriptor,
+                             const RequestDeviceCallbackInfo& callbackInfo);
     DeviceBase* APICreateDevice(const DeviceDescriptor* descriptor = nullptr);
     ResultOrError<Ref<DeviceBase>> CreateDevice(const DeviceDescriptor* rawDescriptor);
 
diff --git a/src/dawn/native/EventManager.cpp b/src/dawn/native/EventManager.cpp
index 37f21cc..aeaada8 100644
--- a/src/dawn/native/EventManager.cpp
+++ b/src/dawn/native/EventManager.cpp
@@ -437,6 +437,14 @@
                                          ExecutionSerial completionSerial)
     : mCallbackMode(callbackMode), mCompletionData(QueueAndSerial{queue, completionSerial}) {}
 
+EventManager::TrackedEvent::TrackedEvent(wgpu::CallbackMode callbackMode, Completed tag)
+    : TrackedEvent(callbackMode, [] {
+          // Make a system event that is already signaled.
+          // Note that this won't make a real OS event because the OS
+          // event is lazily created when waited on.
+          return SystemEvent::CreateSignaled();
+      }()) {}
+
 EventManager::TrackedEvent::~TrackedEvent() {
     DAWN_ASSERT(mCompleted);
 }
diff --git a/src/dawn/native/EventManager.h b/src/dawn/native/EventManager.h
index fec82b4..c0d4668 100644
--- a/src/dawn/native/EventManager.h
+++ b/src/dawn/native/EventManager.h
@@ -108,8 +108,8 @@
 // completed) will be cleaned up at that time.
 class EventManager::TrackedEvent : public RefCounted {
   protected:
-    // Note: TrackedEvents are (currently) only for Device events. Events like RequestAdapter and
-    // RequestDevice complete immediately in dawn native, so should never need to be tracked.
+    // Create an event from a SystemEvent. Note that events like RequestAdapter and
+    // RequestDevice complete immediately in dawn native, and may use an already-completed event.
     TrackedEvent(wgpu::CallbackMode callbackMode, Ref<SystemEvent> completionEvent);
 
     // Create a TrackedEvent from a queue completion serial.
@@ -117,6 +117,10 @@
                  QueueBase* queue,
                  ExecutionSerial completionSerial);
 
+    struct Completed {};
+    // Create a TrackedEvent that is already completed.
+    TrackedEvent(wgpu::CallbackMode callbackMode, Completed tag);
+
   public:
     // Subclasses must implement this to complete the event (if not completed) with
     // EventCompletionType::Shutdown.
diff --git a/src/dawn/native/Instance.cpp b/src/dawn/native/Instance.cpp
index 56f53f7..d720bae 100644
--- a/src/dawn/native/Instance.cpp
+++ b/src/dawn/native/Instance.cpp
@@ -251,19 +251,50 @@
 void InstanceBase::APIRequestAdapter(const RequestAdapterOptions* options,
                                      WGPURequestAdapterCallback callback,
                                      void* userdata) {
-    auto adapters = EnumerateAdapters(options);
-    if (adapters.empty()) {
-        callback(WGPURequestAdapterStatus_Unavailable, nullptr, "No supported adapters.", userdata);
-    } else {
-        callback(WGPURequestAdapterStatus_Success, ToAPI(ReturnToAPI(std::move(adapters[0]))),
-                 nullptr, userdata);
-    }
+    APIRequestAdapterF(
+        options, RequestAdapterCallbackInfo{nullptr, wgpu::CallbackMode::AllowSpontaneous, callback,
+                                            userdata});
 }
 
 Future InstanceBase::APIRequestAdapterF(const RequestAdapterOptions* options,
                                         const RequestAdapterCallbackInfo& callbackInfo) {
-    // TODO(dawn:1987) Implement this.
-    DAWN_UNREACHABLE();
+    struct RequestAdapterEvent final : public EventManager::TrackedEvent {
+        WGPURequestAdapterCallback mCallback;
+        void* mUserdata;
+        Ref<AdapterBase> mAdapter;
+
+        RequestAdapterEvent(const RequestAdapterCallbackInfo& callbackInfo,
+                            Ref<AdapterBase> adapter)
+            : TrackedEvent(callbackInfo.mode, TrackedEvent::Completed{}),
+              mCallback(callbackInfo.callback),
+              mUserdata(callbackInfo.userdata),
+              mAdapter(std::move(adapter)) {
+            CompleteIfSpontaneous();
+        }
+
+        ~RequestAdapterEvent() override { EnsureComplete(EventCompletionType::Shutdown); }
+
+        void Complete(EventCompletionType completionType) override {
+            WGPUAdapter adapter = ToAPI(ReturnToAPI(std::move(mAdapter)));
+            if (adapter == nullptr) {
+                mCallback(WGPURequestAdapterStatus_Unavailable, nullptr, "No supported adapters",
+                          mUserdata);
+            } else {
+                mCallback(WGPURequestAdapterStatus_Success, adapter, nullptr, mUserdata);
+            }
+        }
+    };
+
+    static constexpr RequestAdapterOptions kDefaultOptions = {};
+    if (options == nullptr) {
+        options = &kDefaultOptions;
+    }
+    auto adapters = EnumerateAdapters(options);
+
+    FutureID futureID = GetEventManager()->TrackEvent(
+        callbackInfo.mode, AcquireRef(new RequestAdapterEvent(
+                               callbackInfo, adapters.empty() ? nullptr : std::move(adapters[0]))));
+    return {futureID};
 }
 
 Ref<AdapterBase> InstanceBase::CreateAdapter(Ref<PhysicalDeviceBase> physicalDevice,
diff --git a/src/dawn/native/SystemEvent.cpp b/src/dawn/native/SystemEvent.cpp
index e08ff7f..6d2a3a6 100644
--- a/src/dawn/native/SystemEvent.cpp
+++ b/src/dawn/native/SystemEvent.cpp
@@ -123,6 +123,13 @@
 
 // SystemEvent
 
+// static
+Ref<SystemEvent> SystemEvent::CreateSignaled() {
+    auto ev = AcquireRef(new SystemEvent());
+    ev->Signal();
+    return ev;
+}
+
 bool SystemEvent::IsSignaled() const {
     return mSignaled.load(std::memory_order_acquire);
 }
diff --git a/src/dawn/native/SystemEvent.h b/src/dawn/native/SystemEvent.h
index 2a3d0c5..dfce08c 100644
--- a/src/dawn/native/SystemEvent.h
+++ b/src/dawn/native/SystemEvent.h
@@ -104,6 +104,8 @@
 
 class SystemEvent : public RefCounted {
   public:
+    static Ref<SystemEvent> CreateSignaled();
+
     bool IsSignaled() const;
     void Signal();
 
diff --git a/src/dawn/tests/end2end/AdapterCreationTests.cpp b/src/dawn/tests/end2end/AdapterCreationTests.cpp
index 22cc222..3461561 100644
--- a/src/dawn/tests/end2end/AdapterCreationTests.cpp
+++ b/src/dawn/tests/end2end/AdapterCreationTests.cpp
@@ -26,6 +26,7 @@
 // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 
 #include <memory>
+#include <optional>
 #include <string>
 #include <utility>
 
@@ -43,7 +44,7 @@
 using testing::MockCallback;
 using testing::SaveArg;
 
-class AdapterCreationTest : public ::testing::Test {
+class AdapterCreationTest : public ::testing::TestWithParam<std::optional<wgpu::CallbackMode>> {
   protected:
     void SetUp() override {
         dawnProcSetProcs(&native::GetProcs());
@@ -74,6 +75,35 @@
         dawnProcSetProcs(nullptr);
     }
 
+    void RequestAdapter(const wgpu::Instance& inst,
+                        const wgpu::RequestAdapterOptions* options,
+                        WGPURequestAdapterCallback callback,
+                        void* userdata) {
+        if (GetParam() == std::nullopt) {
+            // Legacy RequestAdapter. It should call the callback immediately.
+            inst.RequestAdapter(options, callback, userdata);
+            return;
+        }
+
+        wgpu::Future future =
+            inst.RequestAdapterF(options, {nullptr, *GetParam(), callback, userdata});
+        switch (*GetParam()) {
+            case wgpu::CallbackMode::WaitAnyOnly: {
+                // Callback should complete as soon as poll once.
+                wgpu::FutureWaitInfo waitInfo = {future};
+                EXPECT_EQ(inst.WaitAny(1, &waitInfo, 0), wgpu::WaitStatus::Success);
+                ASSERT_TRUE(waitInfo.completed);
+                break;
+            }
+            case wgpu::CallbackMode::AllowSpontaneous:
+                // Callback should already be called.
+                break;
+            case wgpu::CallbackMode::AllowProcessEvents:
+                inst.ProcessEvents();
+                break;
+        }
+    }
+
     wgpu::Instance instance;
     bool anyAdapterAvailable = false;
     bool swiftShaderAvailable = false;
@@ -81,8 +111,15 @@
     bool integratedGPUAvailable = false;
 };
 
+INSTANTIATE_TEST_SUITE_P(
+    ,
+    AdapterCreationTest,
+    ::testing::ValuesIn(std::initializer_list<std::optional<wgpu::CallbackMode>>{
+        wgpu::CallbackMode::WaitAnyOnly, wgpu::CallbackMode::AllowProcessEvents,
+        wgpu::CallbackMode::AllowSpontaneous, std::nullopt}));
+
 // Test that requesting the default adapter works
-TEST_F(AdapterCreationTest, DefaultAdapter) {
+TEST_P(AdapterCreationTest, DefaultAdapter) {
     wgpu::RequestAdapterOptions options = {};
 
     MockCallback<WGPURequestAdapterCallback> cb;
@@ -90,14 +127,14 @@
     WGPUAdapter cAdapter = nullptr;
     EXPECT_CALL(cb, Call(WGPURequestAdapterStatus_Success, _, nullptr, this))
         .WillOnce(SaveArg<1>(&cAdapter));
-    instance.RequestAdapter(&options, cb.Callback(), cb.MakeUserdata(this));
+    RequestAdapter(instance, &options, cb.Callback(), cb.MakeUserdata(this));
 
     wgpu::Adapter adapter = wgpu::Adapter::Acquire(cAdapter);
     EXPECT_EQ(adapter != nullptr, anyAdapterAvailable);
 }
 
 // Test that passing nullptr for the options gets the default adapter
-TEST_F(AdapterCreationTest, NullGivesDefaultAdapter) {
+TEST_P(AdapterCreationTest, NullGivesDefaultAdapter) {
     wgpu::RequestAdapterOptions options = {};
 
     MockCallback<WGPURequestAdapterCallback> cb;
@@ -105,21 +142,21 @@
     WGPUAdapter cAdapter = nullptr;
     EXPECT_CALL(cb, Call(WGPURequestAdapterStatus_Success, _, nullptr, this))
         .WillOnce(SaveArg<1>(&cAdapter));
-    instance.RequestAdapter(&options, cb.Callback(), cb.MakeUserdata(this));
+    RequestAdapter(instance, &options, cb.Callback(), cb.MakeUserdata(this));
 
     wgpu::Adapter adapter = wgpu::Adapter::Acquire(cAdapter);
     EXPECT_EQ(adapter != nullptr, anyAdapterAvailable);
 
     EXPECT_CALL(cb, Call(WGPURequestAdapterStatus_Success, _, nullptr, this + 1))
         .WillOnce(SaveArg<1>(&cAdapter));
-    instance.RequestAdapter(nullptr, cb.Callback(), cb.MakeUserdata(this + 1));
+    RequestAdapter(instance, nullptr, cb.Callback(), cb.MakeUserdata(this + 1));
 
     wgpu::Adapter adapter2 = wgpu::Adapter::Acquire(cAdapter);
     EXPECT_EQ(adapter2 != nullptr, anyAdapterAvailable);
 }
 
 // Test that requesting the fallback adapter returns SwiftShader.
-TEST_F(AdapterCreationTest, FallbackAdapter) {
+TEST_P(AdapterCreationTest, FallbackAdapter) {
     wgpu::RequestAdapterOptions options = {};
     options.forceFallbackAdapter = true;
 
@@ -133,7 +170,7 @@
         EXPECT_CALL(cb, Call(WGPURequestAdapterStatus_Unavailable, nullptr, _, this))
             .WillOnce(SaveArg<1>(&cAdapter));
     }
-    instance.RequestAdapter(&options, cb.Callback(), cb.MakeUserdata(this));
+    RequestAdapter(instance, &options, cb.Callback(), cb.MakeUserdata(this));
 
     wgpu::Adapter adapter = wgpu::Adapter::Acquire(cAdapter);
     EXPECT_EQ(adapter != nullptr, swiftShaderAvailable);
@@ -147,7 +184,7 @@
 }
 
 // Test that requesting a high performance GPU works
-TEST_F(AdapterCreationTest, PreferHighPerformance) {
+TEST_P(AdapterCreationTest, PreferHighPerformance) {
     wgpu::RequestAdapterOptions options = {};
     options.powerPreference = wgpu::PowerPreference::HighPerformance;
 
@@ -161,7 +198,7 @@
         EXPECT_CALL(cb, Call(WGPURequestAdapterStatus_Unavailable, nullptr, _, this))
             .WillOnce(SaveArg<1>(&cAdapter));
     }
-    instance.RequestAdapter(&options, cb.Callback(), cb.MakeUserdata(this));
+    RequestAdapter(instance, &options, cb.Callback(), cb.MakeUserdata(this));
 
     wgpu::Adapter adapter = wgpu::Adapter::Acquire(cAdapter);
     EXPECT_EQ(adapter != nullptr, anyAdapterAvailable);
@@ -176,7 +213,7 @@
 }
 
 // Test that requesting a low power GPU works
-TEST_F(AdapterCreationTest, PreferLowPower) {
+TEST_P(AdapterCreationTest, PreferLowPower) {
     wgpu::RequestAdapterOptions options = {};
     options.powerPreference = wgpu::PowerPreference::LowPower;
 
@@ -190,7 +227,7 @@
         EXPECT_CALL(cb, Call(WGPURequestAdapterStatus_Unavailable, nullptr, _, this))
             .WillOnce(SaveArg<1>(&cAdapter));
     }
-    instance.RequestAdapter(&options, cb.Callback(), cb.MakeUserdata(this));
+    RequestAdapter(instance, &options, cb.Callback(), cb.MakeUserdata(this));
 
     wgpu::Adapter adapter = wgpu::Adapter::Acquire(cAdapter);
     EXPECT_EQ(adapter != nullptr, anyAdapterAvailable);
@@ -205,7 +242,7 @@
 }
 
 // Test that requesting a Compatibility adapter is supported.
-TEST_F(AdapterCreationTest, Compatibility) {
+TEST_P(AdapterCreationTest, Compatibility) {
     wgpu::RequestAdapterOptions options = {};
     options.compatibilityMode = true;
 
@@ -214,7 +251,7 @@
     WGPUAdapter cAdapter = nullptr;
     EXPECT_CALL(cb, Call(WGPURequestAdapterStatus_Success, _, nullptr, this))
         .WillOnce(SaveArg<1>(&cAdapter));
-    instance.RequestAdapter(&options, cb.Callback(), cb.MakeUserdata(this));
+    RequestAdapter(instance, &options, cb.Callback(), cb.MakeUserdata(this));
 
     wgpu::Adapter adapter = wgpu::Adapter::Acquire(cAdapter);
     EXPECT_EQ(adapter != nullptr, anyAdapterAvailable);
@@ -225,7 +262,7 @@
 }
 
 // Test that requesting a Non-Compatibility adapter is supported and is default.
-TEST_F(AdapterCreationTest, NonCompatibility) {
+TEST_P(AdapterCreationTest, NonCompatibility) {
     wgpu::RequestAdapterOptions options = {};
 
     MockCallback<WGPURequestAdapterCallback> cb;
@@ -233,7 +270,7 @@
     WGPUAdapter cAdapter = nullptr;
     EXPECT_CALL(cb, Call(WGPURequestAdapterStatus_Success, _, nullptr, this))
         .WillOnce(SaveArg<1>(&cAdapter));
-    instance.RequestAdapter(&options, cb.Callback(), cb.MakeUserdata(this));
+    RequestAdapter(instance, &options, cb.Callback(), cb.MakeUserdata(this));
 
     wgpu::Adapter adapter = wgpu::Adapter::Acquire(cAdapter);
     EXPECT_EQ(adapter != nullptr, anyAdapterAvailable);
@@ -244,7 +281,7 @@
 }
 
 // Test that GetInstance() returns the correct Instance.
-TEST_F(AdapterCreationTest, GetInstance) {
+TEST_P(AdapterCreationTest, GetInstance) {
     wgpu::RequestAdapterOptions options = {};
 
     MockCallback<WGPURequestAdapterCallback> cb;
@@ -252,7 +289,7 @@
     WGPUAdapter cAdapter = nullptr;
     EXPECT_CALL(cb, Call(WGPURequestAdapterStatus_Success, _, nullptr, this))
         .WillOnce(SaveArg<1>(&cAdapter));
-    instance.RequestAdapter(&options, cb.Callback(), cb.MakeUserdata(this));
+    RequestAdapter(instance, &options, cb.Callback(), cb.MakeUserdata(this));
 
     wgpu::Adapter adapter = wgpu::Adapter::Acquire(cAdapter);
     EXPECT_EQ(adapter != nullptr, anyAdapterAvailable);
@@ -262,7 +299,7 @@
 
 // Test that calling AdapterGetProperties returns separate allocations for strings.
 // However, the string contents are equivalent.
-TEST_F(AdapterCreationTest, PropertiesUnique) {
+TEST_P(AdapterCreationTest, PropertiesUnique) {
     wgpu::RequestAdapterOptions options = {};
 
     MockCallback<WGPURequestAdapterCallback> cb;
@@ -270,7 +307,7 @@
     WGPUAdapter cAdapter = nullptr;
     EXPECT_CALL(cb, Call(WGPURequestAdapterStatus_Success, _, nullptr, this))
         .WillOnce(SaveArg<1>(&cAdapter));
-    instance.RequestAdapter(&options, cb.Callback(), cb.MakeUserdata(this));
+    RequestAdapter(instance, &options, cb.Callback(), cb.MakeUserdata(this));
 
     wgpu::Adapter adapter = wgpu::Adapter::Acquire(cAdapter);
     EXPECT_EQ(adapter != nullptr, anyAdapterAvailable);
@@ -294,7 +331,7 @@
 }
 
 // Test move assignment of the adapter properties.
-TEST_F(AdapterCreationTest, PropertiesMoveAssign) {
+TEST_P(AdapterCreationTest, PropertiesMoveAssign) {
     wgpu::RequestAdapterOptions options = {};
 
     MockCallback<WGPURequestAdapterCallback> cb;
@@ -302,7 +339,7 @@
     WGPUAdapter cAdapter = nullptr;
     EXPECT_CALL(cb, Call(WGPURequestAdapterStatus_Success, _, nullptr, this))
         .WillOnce(SaveArg<1>(&cAdapter));
-    instance.RequestAdapter(&options, cb.Callback(), cb.MakeUserdata(this));
+    RequestAdapter(instance, &options, cb.Callback(), cb.MakeUserdata(this));
 
     wgpu::Adapter adapter = wgpu::Adapter::Acquire(cAdapter);
     EXPECT_EQ(adapter != nullptr, anyAdapterAvailable);
@@ -351,7 +388,7 @@
 }
 
 // Test move construction of the adapter properties.
-TEST_F(AdapterCreationTest, PropertiesMoveConstruct) {
+TEST_P(AdapterCreationTest, PropertiesMoveConstruct) {
     wgpu::RequestAdapterOptions options = {};
 
     MockCallback<WGPURequestAdapterCallback> cb;
@@ -359,7 +396,7 @@
     WGPUAdapter cAdapter = nullptr;
     EXPECT_CALL(cb, Call(WGPURequestAdapterStatus_Success, _, nullptr, this))
         .WillOnce(SaveArg<1>(&cAdapter));
-    instance.RequestAdapter(&options, cb.Callback(), cb.MakeUserdata(this));
+    RequestAdapter(instance, &options, cb.Callback(), cb.MakeUserdata(this));
 
     wgpu::Adapter adapter = wgpu::Adapter::Acquire(cAdapter);
     EXPECT_EQ(adapter != nullptr, anyAdapterAvailable);
@@ -406,7 +443,7 @@
 }
 
 // Test that the adapter properties can outlive the adapter.
-TEST_F(AdapterCreationTest, PropertiesOutliveAdapter) {
+TEST_P(AdapterCreationTest, PropertiesOutliveAdapter) {
     wgpu::RequestAdapterOptions options = {};
 
     MockCallback<WGPURequestAdapterCallback> cb;
@@ -414,7 +451,7 @@
     WGPUAdapter cAdapter = nullptr;
     EXPECT_CALL(cb, Call(WGPURequestAdapterStatus_Success, _, nullptr, this))
         .WillOnce(SaveArg<1>(&cAdapter));
-    instance.RequestAdapter(&options, cb.Callback(), cb.MakeUserdata(this));
+    RequestAdapter(instance, &options, cb.Callback(), cb.MakeUserdata(this));
 
     wgpu::Adapter adapter = wgpu::Adapter::Acquire(cAdapter);
     EXPECT_EQ(adapter != nullptr, anyAdapterAvailable);
diff --git a/src/dawn/tests/unittests/native/DeviceCreationTests.cpp b/src/dawn/tests/unittests/native/DeviceCreationTests.cpp
index 6c76506..3ed0d71 100644
--- a/src/dawn/tests/unittests/native/DeviceCreationTests.cpp
+++ b/src/dawn/tests/unittests/native/DeviceCreationTests.cpp
@@ -26,6 +26,7 @@
 // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 
 #include <memory>
+#include <optional>
 #include <vector>
 
 #include "dawn/dawn_proc.h"
@@ -272,8 +273,51 @@
     }
 }
 
+class DeviceCreationFutureTest
+    : public DeviceCreationTest,
+      public ::testing::WithParamInterface<std::optional<wgpu::CallbackMode>> {
+  protected:
+    void RequestDevice(const Adapter& a,
+                       const wgpu::DeviceDescriptor* descriptor,
+                       WGPURequestDeviceCallback callback,
+                       void* userdata) {
+        wgpu::Adapter wgpuAdapter(a.Get());
+        if (GetParam() == std::nullopt) {
+            // Legacy RequestDevice. It should call the callback immediately.
+            wgpuAdapter.RequestDevice(descriptor, callback, userdata);
+            return;
+        }
+
+        wgpu::Future future =
+            wgpuAdapter.RequestDeviceF(descriptor, {nullptr, *GetParam(), callback, userdata});
+        wgpu::Instance wgpuInstance(instance->Get());
+        switch (*GetParam()) {
+            case wgpu::CallbackMode::WaitAnyOnly: {
+                // Callback should complete as soon as poll once.
+                wgpu::FutureWaitInfo waitInfo = {future};
+                EXPECT_EQ(wgpuInstance.WaitAny(1, &waitInfo, 0), wgpu::WaitStatus::Success);
+                ASSERT_TRUE(waitInfo.completed);
+                break;
+            }
+            case wgpu::CallbackMode::AllowSpontaneous:
+                // Callback should already be called.
+                break;
+            case wgpu::CallbackMode::AllowProcessEvents:
+                wgpuInstance.ProcessEvents();
+                break;
+        }
+    }
+};
+
+INSTANTIATE_TEST_SUITE_P(
+    ,
+    DeviceCreationFutureTest,
+    ::testing::ValuesIn(std::initializer_list<std::optional<wgpu::CallbackMode>>{
+        wgpu::CallbackMode::WaitAnyOnly, wgpu::CallbackMode::AllowProcessEvents,
+        wgpu::CallbackMode::AllowSpontaneous, std::nullopt}));
+
 // Test successful call to RequestDevice with descriptor
-TEST_F(DeviceCreationTest, RequestDeviceSuccess) {
+TEST_P(DeviceCreationFutureTest, RequestDeviceSuccess) {
     WGPUDevice cDevice;
     {
         MockCallback<WGPURequestDeviceCallback> cb;
@@ -281,7 +325,7 @@
             .WillOnce(SaveArg<1>(&cDevice));
 
         wgpu::DeviceDescriptor desc = {};
-        adapter.RequestDevice(&desc, cb.Callback(), cb.MakeUserdata(this));
+        RequestDevice(adapter, &desc, cb.Callback(), cb.MakeUserdata(this));
     }
 
     wgpu::Device device = wgpu::Device::Acquire(cDevice);
@@ -289,14 +333,14 @@
 }
 
 // Test successful call to RequestDevice with a null descriptor
-TEST_F(DeviceCreationTest, RequestDeviceNullDescriptorSuccess) {
+TEST_P(DeviceCreationFutureTest, RequestDeviceNullDescriptorSuccess) {
     WGPUDevice cDevice;
     {
         MockCallback<WGPURequestDeviceCallback> cb;
         EXPECT_CALL(cb, Call(WGPURequestDeviceStatus_Success, NotNull(), nullptr, this))
             .WillOnce(SaveArg<1>(&cDevice));
 
-        adapter.RequestDevice(nullptr, cb.Callback(), cb.MakeUserdata(this));
+        RequestDevice(adapter, nullptr, cb.Callback(), cb.MakeUserdata(this));
     }
 
     wgpu::Device device = wgpu::Device::Acquire(cDevice);
@@ -304,7 +348,7 @@
 }
 
 // Test failing call to RequestDevice with invalid feature
-TEST_F(DeviceCreationTest, RequestDeviceFailure) {
+TEST_P(DeviceCreationFutureTest, RequestDeviceFailure) {
     MockCallback<WGPURequestDeviceCallback> cb;
     EXPECT_CALL(cb, Call(WGPURequestDeviceStatus_Error, nullptr, NotNull(), this)).Times(1);
 
@@ -313,7 +357,7 @@
     desc.requiredFeatures = &invalidFeature;
     desc.requiredFeatureCount = 1;
 
-    adapter.RequestDevice(&desc, cb.Callback(), cb.MakeUserdata(this));
+    RequestDevice(adapter, &desc, cb.Callback(), cb.MakeUserdata(this));
 }
 
 }  // anonymous namespace
diff --git a/src/dawn/tests/unittests/wire/WireAdapterTests.cpp b/src/dawn/tests/unittests/wire/WireAdapterTests.cpp
index 469ef93..1f6b889 100644
--- a/src/dawn/tests/unittests/wire/WireAdapterTests.cpp
+++ b/src/dawn/tests/unittests/wire/WireAdapterTests.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"
@@ -49,11 +50,24 @@
 using testing::StrEq;
 using testing::WithArg;
 
-class WireAdapterTests : public WireTest {
+using WireAdapterTestBase = WireFutureTestWithParams<WGPURequestDeviceCallback,
+                                                     WGPURequestDeviceCallbackInfo,
+                                                     wgpuAdapterRequestDevice,
+                                                     wgpuAdapterRequestDeviceF>;
+
+class WireAdapterTests : public WireAdapterTestBase {
   protected:
+    // Overriden version of wgpuAdapterRequestDevice that defers to the API call based on the
+    // test callback mode.
+    void AdapterRequestDevice(const wgpu::Adapter& a,
+                              const wgpu::DeviceDescriptor* descriptor,
+                              void* userdata = nullptr) {
+        CallImpl(userdata, a.Get(), reinterpret_cast<WGPUDeviceDescriptor const*>(descriptor));
+    }
+
     // Bootstrap the tests and create a fake adapter.
     void SetUp() override {
-        WireTest::SetUp();
+        WireAdapterTestBase::SetUp();
 
         WGPURequestAdapterOptions options = {};
         MockCallback<WGPURequestAdapterCallback> cb;
@@ -102,83 +116,72 @@
 
     void TearDown() override {
         adapter = nullptr;
-        WireTest::TearDown();
+        WireAdapterTestBase::TearDown();
     }
 
     WGPUAdapter apiAdapter;
     wgpu::Adapter adapter;
 };
+DAWN_INSTANTIATE_WIRE_FUTURE_TEST_P(WireAdapterTests);
 
-// Test that the DeviceDescriptor is passed from the client to the server.
-TEST_F(WireAdapterTests, RequestDevicePassesDescriptor) {
-    MockCallback<WGPURequestDeviceCallback> cb;
-    auto* userdata = cb.MakeUserdata(this);
+// Test that an empty DeviceDescriptor is passed from the client to the server.
+TEST_P(WireAdapterTests, RequestDeviceEmptyDescriptor) {
+    wgpu::DeviceDescriptor desc = {};
+    AdapterRequestDevice(adapter, &desc);
 
-    // Test an empty descriptor
-    {
-        wgpu::DeviceDescriptor desc = {};
-        adapter.RequestDevice(&desc, cb.Callback(), userdata);
+    EXPECT_CALL(api, OnAdapterRequestDevice(apiAdapter, NotNull(), NotNull(), NotNull()))
+        .WillOnce(WithArg<1>(Invoke([&](const WGPUDeviceDescriptor* apiDesc) {
+            EXPECT_EQ(apiDesc->label, nullptr);
+            EXPECT_EQ(apiDesc->requiredFeatureCount, 0u);
+            EXPECT_EQ(apiDesc->requiredLimits, nullptr);
 
-        EXPECT_CALL(api, OnAdapterRequestDevice(apiAdapter, NotNull(), NotNull(), NotNull()))
-            .WillOnce(WithArg<1>(Invoke([](const WGPUDeviceDescriptor* apiDesc) {
-                EXPECT_EQ(apiDesc->label, nullptr);
-                EXPECT_EQ(apiDesc->requiredFeatureCount, 0u);
-                EXPECT_EQ(apiDesc->requiredLimits, nullptr);
-            })));
-        FlushClient();
-    }
+            // Call the callback so the test doesn't wait indefinitely.
+            api.CallAdapterRequestDeviceCallback(apiAdapter, WGPURequestDeviceStatus_Error, nullptr,
+                                                 nullptr);
+        })));
+    FlushClient();
+    FlushFutures();
+    ExpectWireCallbacksWhen([&](auto& mockCb) {
+        EXPECT_CALL(mockCb, Call).Times(1);
 
-    // Test a non-empty descriptor
-    {
-        wgpu::RequiredLimits limits = {};
-        limits.limits.maxStorageTexturesPerShaderStage = 5;
+        FlushCallbacks();
+    });
+}
 
-        std::vector<wgpu::FeatureName> features = {wgpu::FeatureName::TextureCompressionETC2,
-                                                   wgpu::FeatureName::TextureCompressionASTC};
+// Test that a null DeviceDescriptor is passed from the client to the server as an empty one.
+TEST_P(WireAdapterTests, RequestDeviceNullDescriptor) {
+    AdapterRequestDevice(adapter, nullptr);
 
-        wgpu::DeviceDescriptor desc = {};
-        desc.label = "hello device";
-        desc.requiredLimits = &limits;
-        desc.requiredFeatureCount = features.size();
-        desc.requiredFeatures = features.data();
+    EXPECT_CALL(api, OnAdapterRequestDevice(apiAdapter, NotNull(), NotNull(), NotNull()))
+        .WillOnce(WithArg<1>(Invoke([&](const WGPUDeviceDescriptor* apiDesc) {
+            EXPECT_EQ(apiDesc->label, nullptr);
+            EXPECT_EQ(apiDesc->requiredFeatureCount, 0u);
+            EXPECT_EQ(apiDesc->requiredLimits, nullptr);
 
-        adapter.RequestDevice(&desc, cb.Callback(), userdata);
+            // Call the callback so the test doesn't wait indefinitely.
+            api.CallAdapterRequestDeviceCallback(apiAdapter, WGPURequestDeviceStatus_Error, nullptr,
+                                                 nullptr);
+        })));
+    FlushClient();
+    FlushFutures();
+    ExpectWireCallbacksWhen([&](auto& mockCb) {
+        EXPECT_CALL(mockCb, Call).Times(1);
 
-        EXPECT_CALL(api, OnAdapterRequestDevice(apiAdapter, NotNull(), NotNull(), NotNull()))
-            .WillOnce(WithArg<1>(Invoke([&](const WGPUDeviceDescriptor* apiDesc) {
-                EXPECT_STREQ(apiDesc->label, desc.label);
-
-                ASSERT_EQ(apiDesc->requiredFeatureCount, features.size());
-                for (uint32_t i = 0; i < features.size(); ++i) {
-                    EXPECT_EQ(apiDesc->requiredFeatures[i],
-                              static_cast<WGPUFeatureName>(features[i]));
-                }
-
-                ASSERT_NE(apiDesc->requiredLimits, nullptr);
-                EXPECT_EQ(apiDesc->requiredLimits->nextInChain, nullptr);
-                EXPECT_EQ(apiDesc->requiredLimits->limits.maxStorageTexturesPerShaderStage,
-                          limits.limits.maxStorageTexturesPerShaderStage);
-            })));
-        FlushClient();
-    }
-
-    // Delete the adapter now, or it'll call the mock callback after it's deleted.
-    adapter = nullptr;
+        FlushCallbacks();
+    });
 }
 
 static void DeviceLostCallback(WGPUDeviceLostReason reason, const char* message, void* userdata) {}
 
 // Test that the DeviceDescriptor is not allowed to pass a device lost callback from the client to
 // the server.
-TEST_F(WireAdapterTests, RequestDeviceAssertsOnLostCallbackPointer) {
-    MockCallback<WGPURequestDeviceCallback> cb;
-    auto* userdata = cb.MakeUserdata(this);
-
+TEST_P(WireAdapterTests, RequestDeviceAssertsOnLostCallbackPointer) {
+    int userdata = 1337;
     wgpu::DeviceDescriptor desc = {};
     desc.deviceLostCallback = DeviceLostCallback;
-    desc.deviceLostUserdata = userdata;
+    desc.deviceLostUserdata = &userdata;
 
-    adapter.RequestDevice(&desc, cb.Callback(), userdata);
+    AdapterRequestDevice(adapter, &desc);
 
     EXPECT_CALL(api, OnAdapterRequestDevice(apiAdapter, NotNull(), NotNull(), NotNull()))
         .WillOnce(WithArg<1>(Invoke([&](const WGPUDeviceDescriptor* apiDesc) {
@@ -187,18 +190,22 @@
             // The callback should not be passed through to the server.
             ASSERT_EQ(apiDesc->deviceLostCallback, nullptr);
             ASSERT_EQ(apiDesc->deviceLostUserdata, nullptr);
+
+            // Call the callback so the test doesn't wait indefinitely.
+            api.CallAdapterRequestDeviceCallback(apiAdapter, WGPURequestDeviceStatus_Error, nullptr,
+                                                 nullptr);
         })));
     FlushClient();
+    FlushFutures();
+    ExpectWireCallbacksWhen([&](auto& mockCb) {
+        EXPECT_CALL(mockCb, Call).Times(1);
 
-    // Delete the adapter now, or it'll call the mock callback after it's deleted.
-    adapter = nullptr;
+        FlushCallbacks();
+    });
 }
 
 // Test that RequestDevice forwards the device information to the client.
-TEST_F(WireAdapterTests, RequestDeviceSuccess) {
-    MockCallback<WGPURequestDeviceCallback> cb;
-    auto* userdata = cb.MakeUserdata(this);
-
+TEST_P(WireAdapterTests, RequestDeviceSuccess) {
     wgpu::SupportedLimits fakeLimits = {};
     fakeLimits.limits.maxTextureDimension1D = 433;
     fakeLimits.limits.maxVertexAttributes = 1243;
@@ -209,14 +216,13 @@
     };
 
     wgpu::DeviceDescriptor desc = {};
-    adapter.RequestDevice(&desc, cb.Callback(), userdata);
+    AdapterRequestDevice(adapter, &desc, this);
 
     // Expect the server to receive the message. Then, mock a fake reply.
     WGPUDevice apiDevice = api.GetNewDevice();
     // The backend device should not be known by the wire server.
     EXPECT_FALSE(GetWireServer()->IsDeviceKnown(apiDevice));
 
-    wgpu::Device device;
     EXPECT_CALL(api, OnAdapterRequestDevice(apiAdapter, NotNull(), NotNull(), NotNull()))
         .WillOnce(InvokeWithoutArgs([&] {
             // Set on device creation to forward callbacks to the client.
@@ -251,29 +257,35 @@
             // After the callback is called, the backend device is now known by the server.
             EXPECT_TRUE(GetWireServer()->IsDeviceKnown(apiDevice));
         }));
+
     FlushClient();
+    FlushFutures();
 
+    wgpu::Device device;
     // Expect the callback in the client and all the device information to match.
-    EXPECT_CALL(cb, Call(WGPURequestDeviceStatus_Success, NotNull(), nullptr, this))
-        .WillOnce(WithArg<1>(Invoke([&](WGPUDevice cDevice) {
-            device = wgpu::Device::Acquire(cDevice);
+    ExpectWireCallbacksWhen([&](auto& mockCb) {
+        EXPECT_CALL(mockCb, Call(WGPURequestDeviceStatus_Success, NotNull(), nullptr, this))
+            .WillOnce(WithArg<1>(Invoke([&](WGPUDevice cDevice) {
+                device = wgpu::Device::Acquire(cDevice);
 
-            wgpu::SupportedLimits limits;
-            EXPECT_TRUE(device.GetLimits(&limits));
-            EXPECT_EQ(limits.limits.maxTextureDimension1D, fakeLimits.limits.maxTextureDimension1D);
-            EXPECT_EQ(limits.limits.maxVertexAttributes, fakeLimits.limits.maxVertexAttributes);
+                wgpu::SupportedLimits limits;
+                EXPECT_TRUE(device.GetLimits(&limits));
+                EXPECT_EQ(limits.limits.maxTextureDimension1D,
+                          fakeLimits.limits.maxTextureDimension1D);
+                EXPECT_EQ(limits.limits.maxVertexAttributes, fakeLimits.limits.maxVertexAttributes);
 
-            std::vector<wgpu::FeatureName> features;
-            features.resize(device.EnumerateFeatures(nullptr));
-            ASSERT_EQ(features.size(), fakeFeatures.size());
-            EXPECT_EQ(device.EnumerateFeatures(&features[0]), features.size());
+                std::vector<wgpu::FeatureName> features;
+                features.resize(device.EnumerateFeatures(nullptr));
+                ASSERT_EQ(features.size(), fakeFeatures.size());
+                EXPECT_EQ(device.EnumerateFeatures(&features[0]), features.size());
 
-            std::unordered_set<wgpu::FeatureName> featureSet(fakeFeatures);
-            for (wgpu::FeatureName feature : features) {
-                EXPECT_EQ(featureSet.erase(feature), 1u);
-            }
-        })));
-    FlushServer();
+                std::unordered_set<wgpu::FeatureName> featureSet(fakeFeatures);
+                for (wgpu::FeatureName feature : features) {
+                    EXPECT_EQ(featureSet.erase(feature), 1u);
+                }
+            })));
+        FlushCallbacks();
+    });
 
     // Test that callbacks can propagate from server to client.
     MockCallback<WGPUErrorCallback> errorCb;
@@ -283,7 +295,7 @@
 
     EXPECT_CALL(errorCb, Call(WGPUErrorType_Validation, StrEq("Some error message"), this))
         .Times(1);
-    FlushServer();
+    FlushCallbacks();
 
     device = nullptr;
     // Cleared when the device is destroyed.
@@ -301,10 +313,7 @@
 
 // Test that features requested that the implementation supports, but not the
 // wire reject the callback.
-TEST_F(WireAdapterTests, RequestFeatureUnsupportedByWire) {
-    MockCallback<WGPURequestDeviceCallback> cb;
-    auto* userdata = cb.MakeUserdata(this);
-
+TEST_P(WireAdapterTests, RequestFeatureUnsupportedByWire) {
     std::initializer_list<wgpu::FeatureName> fakeFeatures = {
         // Some value that is not a valid feature
         static_cast<wgpu::FeatureName>(-2),
@@ -312,7 +321,7 @@
     };
 
     wgpu::DeviceDescriptor desc = {};
-    adapter.RequestDevice(&desc, cb.Callback(), userdata);
+    AdapterRequestDevice(adapter, &desc, this);
 
     // Expect the server to receive the message. Then, mock a fake reply.
     // The reply contains features that the device implementation supports, but the
@@ -341,19 +350,19 @@
                                                  apiDevice, nullptr);
         }));
     FlushClient();
+    FlushFutures();
 
     // Expect an error callback since the feature is not supported.
-    EXPECT_CALL(cb, Call(WGPURequestDeviceStatus_Error, nullptr, NotNull(), this)).Times(1);
-    FlushServer();
+    ExpectWireCallbacksWhen([&](auto& mockCb) {
+        EXPECT_CALL(mockCb, Call(WGPURequestDeviceStatus_Error, nullptr, NotNull(), this)).Times(1);
+        FlushCallbacks();
+    });
 }
 
 // Test that RequestDevice errors forward to the client.
-TEST_F(WireAdapterTests, RequestDeviceError) {
-    MockCallback<WGPURequestDeviceCallback> cb;
-    auto* userdata = cb.MakeUserdata(this);
-
+TEST_P(WireAdapterTests, RequestDeviceError) {
     wgpu::DeviceDescriptor desc = {};
-    adapter.RequestDevice(&desc, cb.Callback(), userdata);
+    AdapterRequestDevice(adapter, &desc, this);
 
     // Expect the server to receive the message. Then, mock an error.
     EXPECT_CALL(api, OnAdapterRequestDevice(apiAdapter, NotNull(), NotNull(), NotNull()))
@@ -362,38 +371,98 @@
                                                  "Request device failed");
         }));
     FlushClient();
+    FlushFutures();
 
     // Expect the callback in the client.
-    EXPECT_CALL(cb,
-                Call(WGPURequestDeviceStatus_Error, nullptr, StrEq("Request device failed"), this))
-        .Times(1);
-    FlushServer();
+    ExpectWireCallbacksWhen([&](auto& mockCb) {
+        EXPECT_CALL(mockCb, Call(WGPURequestDeviceStatus_Error, nullptr,
+                                 StrEq("Request device failed"), this))
+            .Times(1);
+        FlushCallbacks();
+    });
 }
 
-// Test that RequestDevice receives unknown status if the adapter is deleted
+// Test that RequestDevice can complete successfully even if the adapter is deleted
 // before the callback happens.
-TEST_F(WireAdapterTests, RequestDeviceAdapterDestroyedBeforeCallback) {
-    MockCallback<WGPURequestDeviceCallback> cb;
-    auto* userdata = cb.MakeUserdata(this);
-
+TEST_P(WireAdapterTests, RequestDeviceAdapterDestroyedBeforeCallback) {
     wgpu::DeviceDescriptor desc = {};
-    adapter.RequestDevice(&desc, cb.Callback(), userdata);
-
-    EXPECT_CALL(cb, Call(WGPURequestDeviceStatus_Unknown, nullptr, NotNull(), this)).Times(1);
+    AdapterRequestDevice(adapter, &desc, this);
     adapter = nullptr;
+
+    wgpu::SupportedLimits fakeLimits = {};
+    fakeLimits.limits.maxTextureDimension1D = 433;
+    fakeLimits.limits.maxVertexAttributes = 1243;
+
+    std::initializer_list<wgpu::FeatureName> fakeFeatures = {
+        wgpu::FeatureName::Depth32FloatStencil8,
+        wgpu::FeatureName::TextureCompressionBC,
+    };
+
+    // Mock a reply from the server.
+    WGPUDevice apiDevice = api.GetNewDevice();
+    EXPECT_CALL(api, OnAdapterRequestDevice(apiAdapter, NotNull(), NotNull(), NotNull()))
+        .WillOnce(InvokeWithoutArgs([&] {
+            // Set on device creation to forward callbacks to the client.
+            EXPECT_CALL(api, OnDeviceSetUncapturedErrorCallback(apiDevice, NotNull(), NotNull()))
+                .Times(1);
+            EXPECT_CALL(api, OnDeviceSetLoggingCallback(apiDevice, NotNull(), NotNull())).Times(1);
+            EXPECT_CALL(api, OnDeviceSetDeviceLostCallback(apiDevice, NotNull(), NotNull()))
+                .Times(1);
+
+            EXPECT_CALL(api, DeviceGetLimits(apiDevice, NotNull()))
+                .WillOnce(WithArg<1>(Invoke([&](WGPUSupportedLimits* limits) {
+                    *reinterpret_cast<wgpu::SupportedLimits*>(limits) = fakeLimits;
+                    return true;
+                })));
+
+            EXPECT_CALL(api, DeviceEnumerateFeatures(apiDevice, nullptr))
+                .WillOnce(Return(fakeFeatures.size()));
+
+            EXPECT_CALL(api, DeviceEnumerateFeatures(apiDevice, NotNull()))
+                .WillOnce(WithArg<1>(Invoke([&](WGPUFeatureName* features) {
+                    for (wgpu::FeatureName feature : fakeFeatures) {
+                        *(features++) = static_cast<WGPUFeatureName>(feature);
+                    }
+                    return fakeFeatures.size();
+                })));
+
+            api.CallAdapterRequestDeviceCallback(apiAdapter, WGPURequestDeviceStatus_Success,
+                                                 apiDevice, nullptr);
+        }));
+    EXPECT_CALL(api, AdapterRelease(apiAdapter));
+    FlushClient();
+    FlushFutures();
+
+    wgpu::Device device;
+    // Expect the callback in the client.
+    ExpectWireCallbacksWhen([&](auto& mockCb) {
+        EXPECT_CALL(mockCb, Call(WGPURequestDeviceStatus_Success, NotNull(), nullptr, this))
+            .WillOnce(WithArg<1>(
+                Invoke([&](WGPUDevice cDevice) { device = wgpu::Device::Acquire(cDevice); })));
+        FlushCallbacks();
+    });
+
+    device = nullptr;
+    // Cleared when the device is destroyed.
+    EXPECT_CALL(api, OnDeviceSetUncapturedErrorCallback(apiDevice, nullptr, nullptr)).Times(1);
+    EXPECT_CALL(api, OnDeviceSetLoggingCallback(apiDevice, nullptr, nullptr)).Times(1);
+    EXPECT_CALL(api, OnDeviceSetDeviceLostCallback(apiDevice, nullptr, nullptr)).Times(1);
+    EXPECT_CALL(api, DeviceRelease(apiDevice));
+    FlushClient();
 }
 
 // Test that RequestDevice receives unknown status if the wire is disconnected
 // before the callback happens.
-TEST_F(WireAdapterTests, RequestDeviceWireDisconnectedBeforeCallback) {
-    MockCallback<WGPURequestDeviceCallback> cb;
-    auto* userdata = cb.MakeUserdata(this);
-
+TEST_P(WireAdapterTests, RequestDeviceWireDisconnectedBeforeCallback) {
     wgpu::DeviceDescriptor desc = {};
-    adapter.RequestDevice(&desc, cb.Callback(), userdata);
+    AdapterRequestDevice(adapter, &desc, this);
 
-    EXPECT_CALL(cb, Call(WGPURequestDeviceStatus_Unknown, nullptr, NotNull(), this)).Times(1);
-    GetWireClient()->Disconnect();
+    ExpectWireCallbacksWhen([&](auto& mockCb) {
+        EXPECT_CALL(mockCb, Call(WGPURequestDeviceStatus_Unknown, nullptr, NotNull(), this))
+            .Times(1);
+
+        GetWireClient()->Disconnect();
+    });
 }
 
 }  // anonymous namespace
diff --git a/src/dawn/wire/client/Adapter.cpp b/src/dawn/wire/client/Adapter.cpp
index 3a85769..fabaffc 100644
--- a/src/dawn/wire/client/Adapter.cpp
+++ b/src/dawn/wire/client/Adapter.cpp
@@ -27,10 +27,76 @@
 
 #include "dawn/wire/client/Adapter.h"
 
+#include <memory>
+#include <string>
+
 #include "dawn/common/Log.h"
 #include "dawn/wire/client/Client.h"
 
 namespace dawn::wire::client {
+namespace {
+
+class RequestDeviceEvent : public TrackedEvent {
+  public:
+    static constexpr EventType kType = EventType::RequestDevice;
+
+    RequestDeviceEvent(const WGPURequestDeviceCallbackInfo& callbackInfo, Device* device)
+        : TrackedEvent(callbackInfo.mode),
+          mCallback(callbackInfo.callback),
+          mUserdata(callbackInfo.userdata),
+          mDevice(device) {}
+
+    EventType GetType() override { return kType; }
+
+    void ReadyHook(WGPURequestDeviceStatus status,
+                   const char* message,
+                   const WGPUSupportedLimits* limits,
+                   uint32_t featuresCount,
+                   const WGPUFeatureName* features) {
+        DAWN_ASSERT(mDevice != nullptr);
+        mStatus = status;
+        if (message != nullptr) {
+            mMessage = message;
+        }
+        if (status == WGPURequestDeviceStatus_Success) {
+            mDevice->SetLimits(limits);
+            mDevice->SetFeatures(features, featuresCount);
+        }
+    }
+
+  private:
+    void CompleteImpl(FutureID futureID, EventCompletionType completionType) override {
+        if (completionType == EventCompletionType::Shutdown) {
+            mStatus = WGPURequestDeviceStatus_Unknown;
+            mMessage = "GPU connection lost";
+        }
+        if (mStatus != WGPURequestDeviceStatus_Success && mDevice != nullptr) {
+            // If there was an error, we may need to reclaim the device allocation, otherwise the
+            // device is returned to the user who owns it.
+            mDevice->GetClient()->Free(mDevice);
+            mDevice = nullptr;
+        }
+        if (mCallback) {
+            mCallback(mStatus, ToAPI(mDevice), mMessage ? mMessage->c_str() : nullptr, mUserdata);
+        }
+    }
+
+    WGPURequestDeviceCallback 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.
+    WGPURequestDeviceStatus mStatus;
+    std::optional<std::string> mMessage;
+
+    // The device is created when we call RequestDevice(F). It is guaranteed to be alive
+    // throughout the duration of a RequestDeviceEvent 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 device to the user who then takes ownership as the Event goes away.
+    Device* mDevice = nullptr;
+};
+
+}  // anonymous namespace
 
 Adapter::~Adapter() {
     mRequestDeviceRequests.CloseAll([](RequestDeviceData* request) {
@@ -155,16 +221,22 @@
 void Adapter::RequestDevice(const WGPUDeviceDescriptor* descriptor,
                             WGPURequestDeviceCallback callback,
                             void* userdata) {
-    Client* client = GetClient();
-    if (client->IsDisconnected()) {
-        callback(WGPURequestDeviceStatus_Error, nullptr, "GPU connection lost", userdata);
-        return;
-    }
+    WGPURequestDeviceCallbackInfo callbackInfo = {};
+    callbackInfo.mode = WGPUCallbackMode_AllowSpontaneous;
+    callbackInfo.callback = callback;
+    callbackInfo.userdata = userdata;
+    RequestDeviceF(descriptor, callbackInfo);
+}
 
-    // The descriptor is passed so that the deviceLostCallback can be tracked client-side and called
-    // when the device is lost.
+WGPUFuture Adapter::RequestDeviceF(const WGPUDeviceDescriptor* descriptor,
+                                   const WGPURequestDeviceCallbackInfo& callbackInfo) {
+    Client* client = GetClient();
     Device* device = client->Make<Device>(GetEventManagerHandle(), descriptor);
-    uint64_t serial = mRequestDeviceRequests.Add({callback, device->GetWireId(), userdata});
+    auto [futureIDInternal, tracked] =
+        GetEventManager().TrackEvent(std::make_unique<RequestDeviceEvent>(callbackInfo, device));
+    if (!tracked) {
+        return {futureIDInternal};
+    }
 
     // Ensure the device lost callback isn't serialized as part of the command, as it cannot be
     // passed between processes.
@@ -177,55 +249,25 @@
 
     AdapterRequestDeviceCmd cmd;
     cmd.adapterId = GetWireId();
-    cmd.requestSerial = serial;
+    cmd.eventManagerHandle = GetEventManagerHandle();
+    cmd.future = {futureIDInternal};
     cmd.deviceObjectHandle = device->GetWireHandle();
     cmd.descriptor = &wireDescriptor;
 
     client->SerializeCommand(cmd);
+    return {futureIDInternal};
 }
 
-bool Client::DoAdapterRequestDeviceCallback(Adapter* adapter,
-                                            uint64_t requestSerial,
+bool Client::DoAdapterRequestDeviceCallback(ObjectHandle eventManager,
+                                            WGPUFuture future,
                                             WGPURequestDeviceStatus status,
                                             const char* message,
                                             const WGPUSupportedLimits* limits,
                                             uint32_t featuresCount,
                                             const WGPUFeatureName* features) {
-    // May have been deleted or recreated so this isn't an error.
-    if (adapter == nullptr) {
-        return true;
-    }
-    return adapter->OnRequestDeviceCallback(requestSerial, status, message, limits, featuresCount,
-                                            features);
-}
-
-bool Adapter::OnRequestDeviceCallback(uint64_t requestSerial,
-                                      WGPURequestDeviceStatus status,
-                                      const char* message,
-                                      const WGPUSupportedLimits* limits,
-                                      uint32_t featuresCount,
-                                      const WGPUFeatureName* features) {
-    RequestDeviceData request;
-    if (!mRequestDeviceRequests.Acquire(requestSerial, &request)) {
-        return false;
-    }
-
-    Client* client = GetClient();
-    Device* device = client->Get<Device>(request.deviceObjectId);
-
-    // If the return status is a failure we should give a null device to the callback and
-    // free the allocation.
-    if (status != WGPURequestDeviceStatus_Success) {
-        client->Free(device);
-        request.callback(status, nullptr, message, request.userdata);
-        return true;
-    }
-
-    device->SetLimits(limits);
-    device->SetFeatures(features, featuresCount);
-
-    request.callback(status, ToAPI(device), message, request.userdata);
-    return true;
+    return GetEventManager(eventManager)
+               .SetFutureReady<RequestDeviceEvent>(future.id, status, message, limits,
+                                                   featuresCount, features) == WireResult::Success;
 }
 
 WGPUInstance Adapter::GetInstance() const {
diff --git a/src/dawn/wire/client/Adapter.h b/src/dawn/wire/client/Adapter.h
index b625698..79021e9 100644
--- a/src/dawn/wire/client/Adapter.h
+++ b/src/dawn/wire/client/Adapter.h
@@ -57,13 +57,8 @@
     void RequestDevice(const WGPUDeviceDescriptor* descriptor,
                        WGPURequestDeviceCallback callback,
                        void* userdata);
-
-    bool OnRequestDeviceCallback(uint64_t requestSerial,
-                                 WGPURequestDeviceStatus status,
-                                 const char* message,
-                                 const WGPUSupportedLimits* limits,
-                                 uint32_t featuresCount,
-                                 const WGPUFeatureName* features);
+    WGPUFuture RequestDeviceF(const WGPUDeviceDescriptor* descriptor,
+                              const WGPURequestDeviceCallbackInfo& callbackInfo);
 
     // Unimplementable. Only availale in dawn_native.
     WGPUInstance GetInstance() const;
diff --git a/src/dawn/wire/client/EventManager.h b/src/dawn/wire/client/EventManager.h
index 6577f9e..071b825 100644
--- a/src/dawn/wire/client/EventManager.h
+++ b/src/dawn/wire/client/EventManager.h
@@ -48,6 +48,7 @@
 enum class EventType {
     MapAsync,
     RequestAdapter,
+    RequestDevice,
     WorkDone,
 };
 
diff --git a/src/dawn/wire/server/Server.h b/src/dawn/wire/server/Server.h
index 96f1523..f78f2d3 100644
--- a/src/dawn/wire/server/Server.h
+++ b/src/dawn/wire/server/Server.h
@@ -146,7 +146,6 @@
 struct RequestAdapterUserdata : CallbackUserdata {
     using CallbackUserdata::CallbackUserdata;
 
-    ObjectHandle instance;
     ObjectHandle eventManager;
     WGPUFuture future;
     ObjectId adapterObjectId;
@@ -155,8 +154,8 @@
 struct RequestDeviceUserdata : CallbackUserdata {
     using CallbackUserdata::CallbackUserdata;
 
-    ObjectHandle adapter;
-    uint64_t requestSerial;
+    ObjectHandle eventManager;
+    WGPUFuture future;
     ObjectId deviceObjectId;
 };
 
diff --git a/src/dawn/wire/server/ServerAdapter.cpp b/src/dawn/wire/server/ServerAdapter.cpp
index 4a5626e..a95e78c 100644
--- a/src/dawn/wire/server/ServerAdapter.cpp
+++ b/src/dawn/wire/server/ServerAdapter.cpp
@@ -33,15 +33,16 @@
 namespace dawn::wire::server {
 
 WireResult Server::DoAdapterRequestDevice(Known<WGPUAdapter> adapter,
-                                          uint64_t requestSerial,
+                                          ObjectHandle eventManager,
+                                          WGPUFuture future,
                                           ObjectHandle deviceHandle,
                                           const WGPUDeviceDescriptor* descriptor) {
     Known<WGPUDevice> device;
     WIRE_TRY(DeviceObjects().Allocate(&device, deviceHandle, AllocationState::Reserved));
 
     auto userdata = MakeUserdata<RequestDeviceUserdata>();
-    userdata->adapter = adapter.AsHandle();
-    userdata->requestSerial = requestSerial;
+    userdata->eventManager = eventManager;
+    userdata->future = future;
     userdata->deviceObjectId = device.id;
 
     mProcs.adapterRequestDevice(adapter->handle, descriptor,
@@ -55,8 +56,8 @@
                                      WGPUDevice device,
                                      const char* message) {
     ReturnAdapterRequestDeviceCallbackCmd cmd = {};
-    cmd.adapter = data->adapter;
-    cmd.requestSerial = data->requestSerial;
+    cmd.eventManager = data->eventManager;
+    cmd.future = data->future;
     cmd.status = status;
     cmd.message = message;
 
diff --git a/src/dawn/wire/server/ServerInstance.cpp b/src/dawn/wire/server/ServerInstance.cpp
index 660c339..24a69a2 100644
--- a/src/dawn/wire/server/ServerInstance.cpp
+++ b/src/dawn/wire/server/ServerInstance.cpp
@@ -42,7 +42,6 @@
     WIRE_TRY(AdapterObjects().Allocate(&adapter, adapterHandle, AllocationState::Reserved));
 
     auto userdata = MakeUserdata<RequestAdapterUserdata>();
-    userdata->instance = instance.AsHandle();
     userdata->eventManager = eventManager;
     userdata->future = future;
     userdata->adapterObjectId = adapter.id;