From a5f00c42ecb9a5b91d85d27f6860fe69d455369a Mon Sep 17 00:00:00 2001
From: Kevin Reid <kpreid@switchb.org>
Date: Sun, 4 Aug 2024 12:20:28 -0700
Subject: [PATCH] `impl AsyncExecutor for tokio::runtime::Handle` (#712)

This will allow Tokio-based async benching without necessarily having
access to the `Runtime` itself (e.g. inside of a `#[tokio::main]`
function); a `Handle` is always obtainable via `Handle::current()`.
---
 CHANGELOG.md                              |  4 ++++
 book/src/user_guide/benchmarking_async.md | 14 +++++++-------
 src/async_executor.rs                     | 12 ++++++++++++
 3 files changed, 23 insertions(+), 7 deletions(-)

diff --git a/CHANGELOG.md b/CHANGELOG.md
index 6cc2dd406..b79b7bb39 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -16,6 +16,10 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.
 
 - gnuplot version is now correctly detected when using certain Windows binaries/configurations that used to fail
 
+### Added
+
+- Async benchmarking with Tokio may be done via a `tokio::runtime::Handle`, not only a `tokio::runtime::Runtime`
+
 ## [0.5.1] - 2023-05-26
 
 ### Fixed
diff --git a/book/src/user_guide/benchmarking_async.md b/book/src/user_guide/benchmarking_async.md
index 2fda66409..ba96bceed 100644
--- a/book/src/user_guide/benchmarking_async.md
+++ b/book/src/user_guide/benchmarking_async.md
@@ -44,13 +44,13 @@ the same executor that you would use in production. If your executor is not list
 implement the `criterion::async_executor::AsyncExecutor` trait for it to add support, or send a pull
 request.
 
-| Crate     | Feature                       | Executor Struct                                       |
-| --------- | ----------------------------- | ----------------------------------------------------- |
-| Tokio     | "async_tokio"                 | `tokio::runtime::Runtime`, `&tokio::runtime::Runtime` |
-| async-std | "async_std" (note underscore) | `AsyncStdExecutor`                                    |
-| Smol      | "async_smol"                  | `SmolExecutor`                                        |
-| futures   | "async_futures"               | `FuturesExecutor`                                     |
-| Other     | "async"                       |                                                       |
+| Crate     | Feature                       | Executor Struct                                                    |
+| --------- | ----------------------------- | ------------------------------------------------------------------ |
+| Tokio     | "async_tokio"                 | In `tokio::runtime`, `Runtime`, `&Runtime`, `Handle`, or `&Handle` |
+| async-std | "async_std" (note underscore) | `AsyncStdExecutor`                                                 |
+| Smol      | "async_smol"                  | `SmolExecutor`                                                     |
+| futures   | "async_futures"               | `FuturesExecutor`                                                  |
+| Other     | "async"                       |                                                                    |
 
 ### Considerations when benchmarking async functions
 
diff --git a/src/async_executor.rs b/src/async_executor.rs
index 58877d54b..c70a3a09b 100644
--- a/src/async_executor.rs
+++ b/src/async_executor.rs
@@ -54,6 +54,18 @@ impl AsyncExecutor for &tokio::runtime::Runtime {
         (*self).block_on(future)
     }
 }
+#[cfg(feature = "async_tokio")]
+impl AsyncExecutor for tokio::runtime::Handle {
+    fn block_on<T>(&self, future: impl Future<Output = T>) -> T {
+        self.block_on(future)
+    }
+}
+#[cfg(feature = "async_tokio")]
+impl AsyncExecutor for &tokio::runtime::Handle {
+    fn block_on<T>(&self, future: impl Future<Output = T>) -> T {
+        (*self).block_on(future)
+    }
+}
 
 /// Runs futures on the 'async-std' crate's global executor
 #[cfg(feature = "async_std")]