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 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 pub(in crate::client::pool) fn new(spawn: impl Spawn) -> Self {
188 Self {
189 inner: Arc::new(spawn),
190 }
191 }
192
193 pub(in crate::client::pool) fn spawn(
195 &self,
196 driver: Pin<Box<dyn Future<Output = ()> + Send + 'static>>,
197 ) {
198 self.inner.spawn(driver);
199 }
200
201 #[cfg(test)]
203 pub(in crate::client::pool) fn ptr_eq(&self, other: &Self) -> bool {
204 Arc::ptr_eq(&self.inner, &other.inner)
205 }
206}
207
208impl fmt::Debug for DriverSpawner {
209 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
210 f.debug_tuple("DriverSpawner").field(&self.inner).finish()
211 }
212}
213
214pub(in crate::client::pool) trait Spawn:
219 fmt::Debug + Send + Sync + 'static
220{
221 fn spawn(&self, driver: Pin<Box<dyn Future<Output = ()> + Send + 'static>>);
223}
224
225#[cfg(feature = "rt-tokio")]
227#[derive(Debug)]
228struct TokioSpawn {
229 handle: tokio::runtime::Handle,
231}
232
233#[cfg(feature = "rt-tokio")]
234impl Spawn for TokioSpawn {
235 fn spawn(&self, driver: Pin<Box<dyn Future<Output = ()> + Send + 'static>>) {
236 drop(self.handle.spawn(driver));
237 }
238}
239
240struct FnSpawn<F>(F);
242
243impl<F> fmt::Debug for FnSpawn<F> {
244 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
245 f.write_str("FnSpawn")
246 }
247}
248
249impl<F> Spawn for FnSpawn<F>
250where
251 F: Fn(Pin<Box<dyn Future<Output = ()> + Send + 'static>>) + Send + Sync + 'static,
252{
253 fn spawn(&self, driver: Pin<Box<dyn Future<Output = ()> + Send + 'static>>) {
254 (self.0)(driver);
255 }
256}
257
258#[derive(Clone, Copy, Debug, Default, Eq, PartialEq)]
266#[non_exhaustive]
267pub enum ConnectionReuseScope {
268 Partition,
270 #[default]
272 NetworkInterface,
273 Pool,
275}
276
277#[derive(Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
284pub(in crate::client::pool) enum EligibilityGroup {
285 Partition(PartitionId),
287 NetworkInterface(Option<Arc<str>>),
289 Pool,
291}
292
293#[cfg(test)]
294mod tests {
295 use super::*;
296 #[cfg(feature = "rt-tokio")]
297 use std::sync::atomic::{AtomicBool, Ordering};
298
299 #[derive(Debug)]
300 struct TestSpawner;
301
302 impl Spawn for TestSpawner {
303 fn spawn(&self, driver: Pin<Box<dyn Future<Output = ()> + Send + 'static>>) {
304 drop(driver);
305 }
306 }
307
308 #[test]
309 fn anonymous_identity_is_reserved() {
310 assert!(PartitionId::ANONYMOUS.is_anonymous());
311 assert!(PartitionId::from_index(usize::MAX).is_anonymous());
312 assert!(!PartitionId::from_index(0).is_anonymous());
313 }
314
315 #[test]
316 fn partition_retains_placement() {
317 let spawner = DriverSpawner::new(TestSpawner);
318 let partition = Partition::new(PartitionId::from_index(7), spawner.clone());
319 let (id, retained, interface) = partition.into_parts();
320 assert_eq!(PartitionId::from_index(7), id);
321 assert_eq!(None, interface);
322 assert!(spawner.ptr_eq(&retained));
323
324 let driver = Box::pin(async {});
325 retained.spawn(driver);
326 }
327
328 #[test]
329 fn from_fn_receives_each_driver() {
330 let submitted = Arc::new(std::sync::atomic::AtomicUsize::new(0));
331 let observed = submitted.clone();
332 let spawner = DriverSpawner::from_fn(move |driver| {
333 observed.fetch_add(1, std::sync::atomic::Ordering::SeqCst);
334 drop(driver);
335 });
336
337 spawner.spawn(Box::pin(async {}));
338 spawner.spawn(Box::pin(async {}));
339 assert_eq!(2, submitted.load(std::sync::atomic::Ordering::SeqCst));
340 assert_eq!("DriverSpawner(FnSpawn)", format!("{spawner:?}"));
341 }
342
343 #[cfg(feature = "rt-tokio")]
344 #[test]
345 fn callback_spawner_submits_to_its_target_from_another_runtime() {
346 let owner = tokio::runtime::Builder::new_current_thread()
347 .build()
348 .unwrap();
349 let foreign = tokio::runtime::Builder::new_current_thread()
350 .build()
351 .unwrap();
352 let owner_id = owner.handle().id();
353 let handle = owner.handle().clone();
354 let spawner = DriverSpawner::from_fn(move |task| {
355 let task_handle = handle.spawn(task);
356 drop(task_handle);
357 });
358 let (completed, completion) = tokio::sync::oneshot::channel();
359
360 foreign.block_on(async {
361 spawner.spawn(Box::pin(async move {
362 completed
363 .send(tokio::runtime::Handle::current().id())
364 .unwrap();
365 }));
366 });
367 assert_eq!(owner_id, owner.block_on(completion).unwrap());
368 }
369
370 #[cfg(feature = "rt-tokio")]
371 #[test]
372 fn tokio_spawner_uses_captured_runtime() {
373 let owner = tokio::runtime::Builder::new_current_thread()
374 .build()
375 .unwrap();
376 let owner_id = owner.handle().id();
377 let ran = Arc::new(AtomicBool::new(false));
378 let task_ran = ran.clone();
379 let spawner = DriverSpawner::tokio(owner.handle().clone());
380
381 owner.block_on(async {
382 spawner.spawn(Box::pin(async move {
383 assert_eq!(owner_id, tokio::runtime::Handle::current().id());
384 task_ran.store(true, Ordering::SeqCst);
385 }));
386 for _ in 0..10 {
387 if ran.load(Ordering::SeqCst) {
388 break;
389 }
390 tokio::task::yield_now().await;
391 }
392 });
393 assert!(ran.load(Ordering::SeqCst));
394 }
395
396 #[cfg(feature = "rt-tokio")]
397 #[test]
398 fn tokio_spawner_accepts_work_from_a_foreign_runtime() {
399 let owner = tokio::runtime::Builder::new_current_thread()
400 .build()
401 .unwrap();
402 let foreign = tokio::runtime::Builder::new_current_thread()
403 .build()
404 .unwrap();
405 let owner_id = owner.handle().id();
406 let ran = Arc::new(AtomicBool::new(false));
407 let task_ran = ran.clone();
408 let spawner = DriverSpawner::tokio(owner.handle().clone());
409
410 foreign.block_on(async {
411 spawner.spawn(Box::pin(async move {
412 assert_eq!(owner_id, tokio::runtime::Handle::current().id());
413 task_ran.store(true, Ordering::SeqCst);
414 }));
415 });
416 owner.block_on(async {
417 for _ in 0..10 {
418 if ran.load(Ordering::SeqCst) {
419 break;
420 }
421 tokio::task::yield_now().await;
422 }
423 });
424 assert!(ran.load(Ordering::SeqCst));
425 }
426}