aws_smithy_http_client/client/pool/
partition.rs1use std::fmt;
13use std::future::Future;
14use std::pin::Pin;
15use std::sync::Arc;
16
17#[derive(Clone)]
19pub struct Partition {
20 id: PartitionId,
22 spawner: DriverSpawner,
24 interface: Option<Arc<str>>,
26}
27
28impl Partition {
29 pub fn new(id: PartitionId, spawner: DriverSpawner) -> Self {
31 Self {
32 id,
33 spawner,
34 interface: None,
35 }
36 }
37
38 #[cfg(any(
47 target_os = "android",
48 target_os = "fuchsia",
49 target_os = "illumos",
50 target_os = "ios",
51 target_os = "linux",
52 target_os = "macos",
53 target_os = "solaris",
54 target_os = "tvos",
55 target_os = "visionos",
56 target_os = "watchos",
57 ))]
58 #[cfg_attr(
59 docsrs,
60 doc(cfg(any(
61 target_os = "android",
62 target_os = "fuchsia",
63 target_os = "illumos",
64 target_os = "ios",
65 target_os = "linux",
66 target_os = "macos",
67 target_os = "solaris",
68 target_os = "tvos",
69 target_os = "visionos",
70 target_os = "watchos",
71 )))
72 )]
73 pub fn interface(mut self, interface: impl Into<String>) -> Self {
74 self.interface = Some(Arc::from(interface.into()));
75 self
76 }
77
78 #[cfg(any(
80 target_os = "android",
81 target_os = "fuchsia",
82 target_os = "illumos",
83 target_os = "ios",
84 target_os = "linux",
85 target_os = "macos",
86 target_os = "solaris",
87 target_os = "tvos",
88 target_os = "visionos",
89 target_os = "watchos",
90 ))]
91 pub(super) fn interface_name(&self) -> Option<&str> {
92 self.interface.as_deref()
93 }
94
95 pub(super) fn id(&self) -> PartitionId {
97 self.id
98 }
99
100 pub(super) fn into_parts(self) -> (PartitionId, DriverSpawner, Option<Arc<str>>) {
102 (self.id, self.spawner, self.interface)
103 }
104}
105
106impl fmt::Debug for Partition {
107 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
108 f.debug_struct("Partition")
109 .field("id", &self.id)
110 .field("spawner", &self.spawner)
111 .field("interface", &self.interface)
112 .finish()
113 }
114}
115
116#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
118pub struct PartitionId(usize);
119
120impl PartitionId {
121 pub const ANONYMOUS: Self = Self(usize::MAX);
123
124 pub const fn from_index(index: usize) -> Self {
129 Self(index)
130 }
131
132 pub const fn is_anonymous(self) -> bool {
134 self.0 == Self::ANONYMOUS.0
135 }
136}
137
138#[derive(Clone)]
153pub struct DriverSpawner {
154 inner: Arc<dyn Spawn>,
155}
156
157impl DriverSpawner {
158 #[cfg(feature = "rt-tokio")]
164 #[cfg_attr(docsrs, doc(cfg(feature = "rt-tokio")))]
165 pub fn tokio(handle: tokio::runtime::Handle) -> Self {
166 Self::new(TokioSpawn { handle })
167 }
168
169 #[cfg(feature = "test-util")]
178 #[doc(hidden)]
179 pub fn from_fn<F>(spawn: F) -> Self
180 where
181 F: Fn(Pin<Box<dyn Future<Output = ()> + Send + 'static>>) + Send + Sync + 'static,
182 {
183 Self::new(FnSpawn(spawn))
184 }
185
186 #[cfg_attr(
188 not(any(feature = "rt-tokio", feature = "test-util", test)),
189 allow(
190 dead_code,
191 reason = "no public constructor exists without a runtime feature"
192 )
193 )]
194 pub(in crate::client::pool) fn new(spawn: impl Spawn) -> Self {
195 Self {
196 inner: Arc::new(spawn),
197 }
198 }
199
200 pub(in crate::client::pool) fn spawn(
202 &self,
203 driver: Pin<Box<dyn Future<Output = ()> + Send + 'static>>,
204 ) {
205 self.inner.spawn(driver);
206 }
207
208 #[cfg(test)]
210 pub(in crate::client::pool) fn ptr_eq(&self, other: &Self) -> bool {
211 Arc::ptr_eq(&self.inner, &other.inner)
212 }
213}
214
215impl fmt::Debug for DriverSpawner {
216 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
217 f.debug_tuple("DriverSpawner").field(&self.inner).finish()
218 }
219}
220
221pub(in crate::client::pool) trait Spawn:
226 fmt::Debug + Send + Sync + 'static
227{
228 fn spawn(&self, driver: Pin<Box<dyn Future<Output = ()> + Send + 'static>>);
230}
231
232#[cfg(feature = "rt-tokio")]
234#[derive(Debug)]
235struct TokioSpawn {
236 handle: tokio::runtime::Handle,
238}
239
240#[cfg(feature = "rt-tokio")]
241impl Spawn for TokioSpawn {
242 fn spawn(&self, driver: Pin<Box<dyn Future<Output = ()> + Send + 'static>>) {
243 drop(self.handle.spawn(driver));
244 }
245}
246
247#[cfg(feature = "test-util")]
249struct FnSpawn<F>(F);
250
251#[cfg(feature = "test-util")]
252impl<F> fmt::Debug for FnSpawn<F> {
253 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
254 f.write_str("FnSpawn")
255 }
256}
257
258#[cfg(feature = "test-util")]
259impl<F> Spawn for FnSpawn<F>
260where
261 F: Fn(Pin<Box<dyn Future<Output = ()> + Send + 'static>>) + Send + Sync + 'static,
262{
263 fn spawn(&self, driver: Pin<Box<dyn Future<Output = ()> + Send + 'static>>) {
264 (self.0)(driver);
265 }
266}
267
268#[derive(Clone, Copy, Debug, Default, Eq, PartialEq)]
276#[non_exhaustive]
277pub enum ConnectionReuseScope {
278 Partition,
280 #[default]
282 NetworkInterface,
283 Pool,
285}
286
287#[derive(Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
294pub(in crate::client::pool) enum EligibilityGroup {
295 Partition(PartitionId),
297 NetworkInterface(Option<Arc<str>>),
299 Pool,
301}
302
303#[cfg(test)]
304mod tests {
305 use super::*;
306 #[cfg(feature = "rt-tokio")]
307 use std::sync::atomic::{AtomicBool, Ordering};
308
309 #[derive(Debug)]
310 struct TestSpawner;
311
312 impl Spawn for TestSpawner {
313 fn spawn(&self, driver: Pin<Box<dyn Future<Output = ()> + Send + 'static>>) {
314 drop(driver);
315 }
316 }
317
318 #[test]
319 fn anonymous_identity_is_reserved() {
320 assert!(PartitionId::ANONYMOUS.is_anonymous());
321 assert!(PartitionId::from_index(usize::MAX).is_anonymous());
322 assert!(!PartitionId::from_index(0).is_anonymous());
323 }
324
325 #[test]
326 fn partition_retains_placement() {
327 let spawner = DriverSpawner::new(TestSpawner);
328 let partition = Partition::new(PartitionId::from_index(7), spawner.clone());
329 let (id, retained, interface) = partition.into_parts();
330 assert_eq!(PartitionId::from_index(7), id);
331 assert_eq!(None, interface);
332 assert!(spawner.ptr_eq(&retained));
333
334 let driver = Box::pin(async {});
335 retained.spawn(driver);
336 }
337
338 #[cfg(feature = "test-util")]
339 #[test]
340 fn from_fn_receives_each_driver() {
341 let submitted = Arc::new(std::sync::atomic::AtomicUsize::new(0));
342 let observed = submitted.clone();
343 let spawner = DriverSpawner::from_fn(move |driver| {
344 observed.fetch_add(1, std::sync::atomic::Ordering::SeqCst);
345 drop(driver);
346 });
347
348 spawner.spawn(Box::pin(async {}));
349 spawner.spawn(Box::pin(async {}));
350 assert_eq!(2, submitted.load(std::sync::atomic::Ordering::SeqCst));
351 assert_eq!("DriverSpawner(FnSpawn)", format!("{spawner:?}"));
352 }
353
354 #[cfg(feature = "rt-tokio")]
355 #[test]
356 fn tokio_spawner_uses_captured_runtime() {
357 let owner = tokio::runtime::Builder::new_current_thread()
358 .build()
359 .unwrap();
360 let owner_id = owner.handle().id();
361 let ran = Arc::new(AtomicBool::new(false));
362 let task_ran = ran.clone();
363 let spawner = DriverSpawner::tokio(owner.handle().clone());
364
365 owner.block_on(async {
366 spawner.spawn(Box::pin(async move {
367 assert_eq!(owner_id, tokio::runtime::Handle::current().id());
368 task_ran.store(true, Ordering::SeqCst);
369 }));
370 for _ in 0..10 {
371 if ran.load(Ordering::SeqCst) {
372 break;
373 }
374 tokio::task::yield_now().await;
375 }
376 });
377 assert!(ran.load(Ordering::SeqCst));
378 }
379
380 #[cfg(feature = "rt-tokio")]
381 #[test]
382 fn tokio_spawner_accepts_work_from_a_foreign_runtime() {
383 let owner = tokio::runtime::Builder::new_current_thread()
384 .build()
385 .unwrap();
386 let foreign = tokio::runtime::Builder::new_current_thread()
387 .build()
388 .unwrap();
389 let owner_id = owner.handle().id();
390 let ran = Arc::new(AtomicBool::new(false));
391 let task_ran = ran.clone();
392 let spawner = DriverSpawner::tokio(owner.handle().clone());
393
394 foreign.block_on(async {
395 spawner.spawn(Box::pin(async move {
396 assert_eq!(owner_id, tokio::runtime::Handle::current().id());
397 task_ran.store(true, Ordering::SeqCst);
398 }));
399 });
400 owner.block_on(async {
401 for _ in 0..10 {
402 if ran.load(Ordering::SeqCst) {
403 break;
404 }
405 tokio::task::yield_now().await;
406 }
407 });
408 assert!(ran.load(Ordering::SeqCst));
409 }
410}