Skip to main content

DialPool

Struct DialPool 

Source
pub struct DialPool { /* private fields */ }
Expand description

A bounded, permanent set of threads that own this role’s blocking TCP dials.

§Why the dial needs a thread at all

The connect is a blocking syscall and every caller is a task. On the exec backend a task runs on a cooperative callback-band worker shared with every other future on its band, so connecting inline parks the band for the whole attempt — measured exactly there (gdb all-thread dump, host-linux realtime-pva-ioc): one unanswering name server starved every future on Medium for ~40 s per attempt. So the connect goes to a thread and the caller parks on a oneshot instead.

§Why the threads are permanent

The obvious shape — one transient thread per dial — is unbounded in thread creations, and creations are what cost on RTEMS (see MAX_DIAL_WORKERS). A search engine whose name server is down redials roughly every 10 s for as long as the IOC runs, so “transient, one per attempt” is a leak with no ceiling. Making the workers permanent and reusing them removes the family rather than capping it: after the first dial of each concurrency level there is nothing left to create.

§What a worker owes the socket it opens

A worker is the single finalizer for every socket it opens. If the caller gave up (timed out, or its future was dropped) the oneshot send fails and the returned TcpStream is dropped right there, closing the fresh socket. A worker that reaches a request whose caller is already gone skips the connect entirely, so a backlog built up behind a blackholed peer costs no sockets at all.

§Where the timeout is not

The worker issues a plain blocking TcpStream::connect — the CA client’s proven on-target dial, C parity with tcpiiu.cpp’s blocking ::connect(), and a thread that owns its blocking needs no poll machinery. The application-level bound belongs to the awaiting side, which holds the oneshot::Receiver this returns and is free to wrap it in runtime::task::timeout. Do not add a bound here: the two are deliberately split, and collapsing them puts the application deadline back inside a syscall that cannot honour it.

Implementations§

Source§

impl DialPool

Source

pub const fn new(name_prefix: &'static str, priority: ThreadPriority) -> Self

Declare a role’s dial pool. const so it can be a static: a pool is per-role and lives as long as the process, so a caller needs no Arc and no lazy initialiser.

Source

pub fn worker_count(&self) -> usize

Threads this pool has created — never more than MAX_DIAL_WORKERS.

The bound made observable: this is the number the per-attempt shape grew without limit.

Source

pub fn queue_depth(&self) -> (usize, usize)

Requests waiting for a worker, and workers currently inside a dial.

The other half of the bound: worker_count() alone cannot distinguish “four workers, nothing queued” from “four workers, every one pinned and a fifth dial waiting” — which is the state MAX_DIAL_WORKERS exists to produce and the only state in which the queueing it documents is observable.

Source

pub fn dial( &'static self, target: SocketAddr, ) -> Result<Receiver<Result<TcpStream>>>

Submit a dial. The returned receiver resolves with whatever the worker’s connect returned.

The error is a thread-creation failure, and only that: it is returned before the request is queued, so a caller that sees it knows no dial is pending on its behalf.

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

Source§

impl<T> Instrument for T

Source§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided Span, returning an Instrumented wrapper. Read more
Source§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an Instrumented wrapper. Read more
Source§

impl<T, U> Into<U> for T
where U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

Source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
Source§

impl<T> WithSubscriber for T

Source§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a WithDispatch wrapper. Read more
Source§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a WithDispatch wrapper. Read more