dope_session/pool/client/
mod.rs1use std::collections::VecDeque;
2use std::marker::PhantomData;
3use std::sync::Arc;
4use std::sync::atomic::AtomicBool;
5use std::time::Instant;
6
7use dope_core::driver::{Backend, DriverConfig, PoolDriver};
8use dope_runtime::runtime::Runtime;
9
10use crate::CodecLayer;
11use crate::protocol::client::ClientProtocol;
12use dope_core::profile::Profile;
13use dope_transport::{Tcp, Transport};
14
15pub mod connect;
16pub mod connect_source;
17pub mod recv;
18pub mod state;
19pub mod write;
20
21use self::connect_source::{ConnectAction, ConnectSource};
22use self::state::{CLIENT_IOV_CAP, ClientSlotState};
23use super::Direction;
24use super::Pool;
25use super::core::PoolCore;
26use super::slot::{ConnFlags, INGRESS_BUF_CAP};
27use crate::SlotId;
28
29pub type ClientSession<P> =
30 crate::session::ClientSessionGeneric<P, INGRESS_BUF_CAP, CLIENT_IOV_CAP>;
31
32pub struct Client<P: ClientProtocol, T: Transport, S: ConnectSource<T>> {
33 pub(crate) protocol: P,
34 pub(crate) connect_source: S,
35 pub(crate) socket_opts: dope_transport::config::StreamSocketConfig,
36 pub(crate) connecting_slots: VecDeque<u16>,
37 transport: PhantomData<T>,
38}
39
40impl<P: ClientProtocol, T: Transport, S: ConnectSource<T>> Client<P, T, S> {
41 pub fn new(protocol: P, connect_source: S) -> Self {
42 Self {
43 protocol,
44 connect_source,
45 socket_opts: dope_transport::config::StreamSocketConfig::default(),
46 connecting_slots: VecDeque::with_capacity(16),
47 transport: PhantomData,
48 }
49 }
50
51 #[inline(always)]
52 pub fn protocol(&self) -> &P {
53 &self.protocol
54 }
55
56 #[inline(always)]
57 pub fn protocol_mut(&mut self) -> &mut P {
58 &mut self.protocol
59 }
60
61 #[inline(always)]
62 pub fn connect_source(&self) -> &S {
63 &self.connect_source
64 }
65
66 #[inline(always)]
67 pub fn connect_source_mut(&mut self) -> &mut S {
68 &mut self.connect_source
69 }
70
71 #[inline(always)]
72 pub(crate) fn has_pending(&self) -> bool {
73 !self.connecting_slots.is_empty()
74 }
75}
76
77impl<P, T, S, B> Direction<B> for Client<P, T, S>
78where
79 P: ClientProtocol,
80 T: Transport,
81 T::Addr: Clone,
82 S: ConnectSource<T>,
83 B: PoolDriver,
84{
85 type Transport = T;
86 type SlotUser = ClientSlotState<P, B>;
87 type CodecLayer = P::CodecLayer;
88
89 #[inline(always)]
90 fn has_pending(&self) -> bool {
91 Self::has_pending(self)
92 }
93
94 #[inline]
95 fn tick(
96 &mut self,
97 core: &mut PoolCore<Self::Transport, Self::CodecLayer, Self::SlotUser, B>,
98 driver: &mut B::Driver,
99 ) {
100 let now = Instant::now();
101 let max_new_connects = core.free_conns.len();
102 for _ in 0..max_new_connects {
103 match self.connect_source.poll_connect(now) {
104 ConnectAction::Connect { addr, tag } => {
105 if !self.spawn_connect::<B>(core, driver, addr, tag) {
106 self.connect_source.on_connect_outcome(tag, false, now);
107 break;
108 }
109 }
110 ConnectAction::Backoff { .. } | ConnectAction::Idle => break,
111 }
112 }
113 core.flush_recv_rearm(driver);
114 }
115}
116
117impl<P, T, S, F, B> Pool<Client<P, T, S>, F, B>
118where
119 P: ClientProtocol,
120 T: Transport,
121 T::Addr: Clone,
122 S: ConnectSource<T>,
123 F: Profile,
124 B: PoolDriver,
125{
126 pub fn new(
127 direction: Client<P, T, S>,
128 max_conn: usize,
129 slot_id_base: usize,
130 ) -> std::io::Result<Self> {
131 assert!(max_conn > 0 && max_conn <= u16::MAX as usize);
132 let core =
133 PoolCore::<T, P::CodecLayer, ClientSlotState<P, B>, B>::new(max_conn, slot_id_base)?;
134 Ok(Self {
135 direction,
136 core,
137 profile: PhantomData,
138 })
139 }
140
141 pub fn slot_id_range(&self) -> std::ops::Range<usize> {
142 let base = self.core.slot_id_base;
143 base..(base + self.core.slots.len())
144 }
145
146 pub fn conn_slot_count(&self) -> usize {
147 self.core.slots.len()
148 }
149
150 pub fn direction_mut(&mut self) -> &mut Client<P, T, S> {
151 &mut self.direction
152 }
153
154 pub fn live_conns(&self) -> impl Iterator<Item = SlotId> + '_ {
155 self.core
156 .slots
157 .iter()
158 .enumerate()
159 .filter(|(_, slot)| slot.conn.flags.contains(ConnFlags::LIVE))
160 .map(|(i, slot)| SlotId::from_parts(i as u32, slot.conn.generation))
161 }
162
163 pub fn is_live(&self, conn_id: SlotId) -> bool {
164 self.core.resolve_live_slot(conn_id).is_ok()
165 }
166
167 pub fn session_mut(&mut self, conn_id: SlotId) -> Option<&mut ClientSession<P>> {
168 let slot_idx = self.core.resolve_live_slot(conn_id).ok()?;
169 Some(&mut self.core.slots[slot_idx].user.get_mut().session)
170 }
171
172 pub fn session_with_codec_mut(
179 &mut self,
180 conn_id: SlotId,
181 ) -> Option<(
182 &mut ClientSession<P>,
183 &mut <P::CodecLayer as CodecLayer>::ConnState,
184 )> {
185 let slot_idx = self.core.resolve_live_slot(conn_id).ok()?;
186 let entry = &mut self.core.slots[slot_idx];
187 let codec_state = entry.codec_state.get_mut();
188 let session = &mut entry.user.get_mut().session;
189 Some((session, codec_state))
190 }
191}
192
193#[derive(Clone, Debug, Default)]
194pub struct TcpClientConfig {
195 pub max_conn: usize,
196 pub socket_opts: dope_transport::config::StreamSocketConfig,
197 pub shutdown: Option<Arc<AtomicBool>>,
198 pub cpu_id: Option<u16>,
199}
200
201impl<P, S, F, B> Pool<Client<P, Tcp, S>, F, B>
202where
203 P: ClientProtocol,
204 S: ConnectSource<Tcp>,
205 F: Profile,
206 B: PoolDriver,
207{
208 pub fn build(
209 protocol: P,
210 connect_source: S,
211 cfg: TcpClientConfig,
212 ) -> std::io::Result<Runtime<Self>> {
213 let shutdown = cfg.shutdown.clone();
214 let max_conn = cfg.max_conn;
215 let cpu_id = cfg.cpu_id;
216 assert!(max_conn > 0 && max_conn <= u16::MAX as usize);
217
218 let driver_cfg = <<B as Backend>::Config as DriverConfig>::for_tcp_profile(
219 F::RING_ENTRIES,
220 max_conn,
221 crate::pool::slot::INGRESS_BUF_CAP,
222 )
223 .with_cpu_id(cpu_id);
224 let mut driver = <B as Backend>::new_driver(driver_cfg)?;
225 let pool = Self::build_raw(protocol, connect_source, cfg, &mut driver, 0)?;
226 Ok(Runtime::new(pool, driver).with_shutdown(shutdown))
227 }
228
229 pub fn build_raw(
230 protocol: P,
231 connect_source: S,
232 cfg: TcpClientConfig,
233 _driver: &mut B::Driver,
234 slot_id_base: usize,
235 ) -> std::io::Result<Self> {
236 let TcpClientConfig {
237 max_conn,
238 socket_opts,
239 shutdown: _,
240 cpu_id: _,
241 } = cfg;
242 assert!(max_conn > 0 && max_conn <= u16::MAX as usize);
243 let mut direction = Client::new(protocol, connect_source);
244 direction.socket_opts = socket_opts;
245 Self::new(direction, max_conn, slot_id_base)
246 }
247}
248
249impl<P, T, S, F, B> super::PoolTick<B> for Pool<Client<P, T, S>, F, B>
250where
251 P: ClientProtocol,
252 T: Transport,
253 T::Addr: Clone,
254 S: ConnectSource<T>,
255 F: Profile,
256 B: PoolDriver,
257{
258 #[inline(always)]
259 fn tick(&mut self, driver: &mut B::Driver) {
260 self.direction.tick(&mut self.core, driver);
261 self.poll_connect_requests(driver);
262 let _ = self.pump_outbound(driver);
263 }
264}