1pub mod bootstrap;
2pub mod completion;
3pub mod control;
4pub mod datagram;
5pub mod ext;
6pub mod profile;
7pub mod ready;
8pub mod recv;
9pub mod route;
10pub mod submission;
11pub mod timer;
12pub mod token;
13
14use std::cell::Cell;
15use std::error::Error;
16use std::fmt::{self, Display, Formatter};
17use std::io::{self, ErrorKind, Result};
18use std::marker::{PhantomData, PhantomPinned};
19use std::pin::{Pin, pin};
20use std::ptr::from_ref;
21use std::time::Instant;
22
23use o3::cell::{BrandToken, RegionToken};
24use o3::collections::CellQueue;
25use o3::marker::ThreadBound;
26use profile::DriverProfile;
27use ready::{Arena, ReadyHandle, ReadyKey, ReadySlot};
28use token::{SlotIndex, Token, TokenCapacity};
29
30use crate::backend::ops::buffers::BufferBackend;
31use crate::backend::{Backend, RecvBuffer};
32use crate::io::fd::{Fd, FdGuard, FdSlot};
33use crate::platform::Platform;
34use crate::platform::raw::file::{FileLimit, lock_memory_best_effort};
35
36type Invariant<'d> = PhantomData<fn(&'d ()) -> &'d ()>;
37
38struct Shared {
39 arena: Box<Arena>,
40 returned_buffers: CellQueue<RecvBuffer>,
41 turn_clock: Cell<Instant>,
42}
43
44pub struct Driver {
45 shared: Shared,
46 backend: Backend,
47 timer_slots: usize,
48 _pin: PhantomPinned,
49}
50
51pub struct DriverRef<'d> {
52 shared: &'d Shared,
53 _brand: Invariant<'d>,
54}
55
56pub struct Scope<'d> {
57 driver: DriverRef<'d>,
58 backend: &'d mut Backend,
59 region: RegionToken<'d>,
60 token: BrandToken<'d>,
61 timer: &'d timer::Timer<'d>,
62 _timer_owner: Box<timer::Timer<'d>>,
63}
64
65pub struct DriverContext<'a, 'd> {
66 driver: DriverRef<'d>,
67 backend: &'a mut Backend,
68 region: &'a mut RegionToken<'d>,
69 timer: &'d timer::Timer<'d>,
70}
71
72pub trait StorageFactory: 'static {
73 type Output<'d>: 'd;
74
75 fn build<'d>(self, driver: &mut DriverContext<'_, 'd>) -> Self::Output<'d>;
76}
77
78impl StorageFactory for () {
79 type Output<'d> = ();
80
81 fn build<'d>(self, _driver: &mut DriverContext<'_, 'd>) -> Self::Output<'d> {}
82}
83
84impl<A: StorageFactory, B: StorageFactory> StorageFactory for (A, B) {
85 type Output<'d> = (A::Output<'d>, B::Output<'d>);
86
87 fn build<'d>(self, driver: &mut DriverContext<'_, 'd>) -> Self::Output<'d> {
88 let first = self.0.build(&mut driver.reborrow());
89 let second = self.1.build(driver);
90 (first, second)
91 }
92}
93
94impl Driver {
95 pub fn init_process() -> Result<()> {
96 lock_memory_best_effort();
97 FileLimit::get()?.raise()
98 }
99
100 pub(crate) fn from_state(
101 state: Backend,
102 fixed_slots: usize,
103 dynamic_slots: usize,
104 timer_slots: usize,
105 recv_buffers: usize,
106 ) -> Result<Self> {
107 Ok(Self {
108 shared: Shared {
109 arena: Arena::new(fixed_slots, dynamic_slots)?,
110 returned_buffers: CellQueue::with_capacity(recv_buffers),
111 turn_clock: Cell::new(Instant::now()),
112 },
113 backend: state,
114 timer_slots,
115 _pin: PhantomPinned,
116 })
117 }
118
119 pub fn scope<R>(self: Pin<&mut Self>, f: impl for<'d> FnOnce(Scope<'d>) -> R) -> R {
120 let this = unsafe { self.get_unchecked_mut() as *mut Self };
123 BrandToken::scope_with_region(move |token, region| {
124 let this = unsafe { &mut *this };
127 let timer_owner = Box::new(timer::Timer::with_capacity(
128 this.timer_slots,
129 ®ion,
130 DriverRef::new(&this.shared),
131 ));
132 let timer = &raw const *timer_owner;
133 let timer = unsafe { &*timer };
136 f(Scope {
137 driver: DriverRef::new(&this.shared),
138 backend: &mut this.backend,
139 region,
140 token,
141 timer,
142 _timer_owner: timer_owner,
143 })
144 })
145 }
146
147 #[doc(hidden)]
148 pub fn scope_with_storage<S, R>(
149 self: Pin<&mut Self>,
150 factory: S,
151 f: impl for<'scope, 'd> FnOnce(Scope<'d>, Pin<&'d S::Output<'d>>) -> R,
152 ) -> R
153 where
154 S: StorageFactory,
155 {
156 self.scope(move |mut scope| {
157 let value = factory.build(&mut scope.context());
158 let storage = pin!(value);
159 let storage = from_ref(storage.as_ref().get_ref());
160 let storage = unsafe { Pin::new_unchecked(&*storage) };
163 f(scope, storage)
164 })
165 }
166}
167
168impl<'d> DriverRef<'d> {
169 fn new(shared: &'d Shared) -> Self {
170 Self {
171 shared,
172 _brand: PhantomData,
173 }
174 }
175
176 fn arena(self) -> &'d Arena {
177 &self.shared.arena
178 }
179
180 pub(crate) fn fixed_ready(self, slot: FdSlot) -> ReadyHandle<'d> {
181 self.arena().fixed_slot(slot)
182 }
183
184 pub(crate) fn fixed_fd_slot(self, raw: u32) -> Option<FdSlot> {
185 self.arena().fd_slot(raw)
186 }
187
188 pub fn make_ready_slot(self, target: Token) -> Result<ReadySlot<'d>> {
189 self.arena().make_slot(target)
190 }
191
192 pub fn make_ready_slot_reserving(self, target: Token, reserve: usize) -> Result<ReadySlot<'d>> {
193 self.arena().make_slot_reserving(target, reserve)
194 }
195
196 pub fn make_ready_slots<I>(self, targets: I) -> Result<Box<[ReadySlot<'d>]>>
197 where
198 I: IntoIterator<Item = Token>,
199 I::IntoIter: ExactSizeIterator,
200 {
201 self.arena().make_slots(targets)
202 }
203
204 pub fn activate_ready(self, key: ReadyKey<'d>) {
205 self.arena().activate(key);
206 }
207
208 #[doc(hidden)]
209 pub fn arm_recv_credit(self, key: ReadyKey<'d>, target: Token) -> bool {
210 self.arena().arm_recv_credit(key, target)
211 }
212
213 #[doc(hidden)]
214 pub fn release_recv_credit(self, key: ReadyKey<'d>, target: Token) {
215 self.arena().release_recv_credit(key, target);
216 }
217
218 #[doc(hidden)]
219 pub fn take_recv_credit(self, key: ReadyKey<'d>, target: Token) -> bool {
220 self.arena().take_recv_credit(key, target)
221 }
222
223 pub fn drain_ready(self, activate: impl FnMut(Token)) {
224 self.arena().drain(activate);
225 }
226
227 pub fn has_ready(self) -> bool {
228 self.arena().has_ready()
229 }
230
231 pub fn turn_now(self) -> Instant {
238 self.shared.turn_clock.get()
239 }
240
241 pub(crate) fn return_buffer(self, buffer: RecvBuffer) {
242 assert!(
243 self.shared.returned_buffers.push_back(buffer).is_ok(),
244 "dope: provided-buffer return queue overflow"
245 );
246 }
247}
248
249impl Copy for DriverRef<'_> {}
250
251impl Clone for DriverRef<'_> {
252 fn clone(&self) -> Self {
253 *self
254 }
255}
256
257impl PartialEq for DriverRef<'_> {
258 fn eq(&self, other: &Self) -> bool {
259 core::ptr::eq(self.shared, other.shared)
260 }
261}
262
263impl Eq for DriverRef<'_> {}
264
265impl<'d> Scope<'d> {
266 pub fn context(&mut self) -> DriverContext<'_, 'd> {
267 DriverContext {
268 driver: self.driver,
269 backend: &mut *self.backend,
270 region: &mut self.region,
271 timer: self.timer,
272 }
273 }
274
275 pub fn token(&mut self) -> &mut BrandToken<'d> {
276 &mut self.token
277 }
278
279 pub fn token_and_context(&mut self) -> (&mut BrandToken<'d>, DriverContext<'_, 'd>) {
280 (
281 &mut self.token,
282 DriverContext {
283 driver: self.driver,
284 backend: &mut *self.backend,
285 region: &mut self.region,
286 timer: self.timer,
287 },
288 )
289 }
290
291 pub fn driver_ref(&self) -> DriverRef<'d> {
292 self.driver
293 }
294}
295
296impl<'a, 'd> DriverContext<'a, 'd> {
297 pub fn reborrow(&mut self) -> DriverContext<'_, 'd> {
298 DriverContext {
299 driver: self.driver,
300 backend: &mut *self.backend,
301 region: &mut *self.region,
302 timer: self.timer,
303 }
304 }
305
306 pub fn region_token(&mut self) -> &mut RegionToken<'d> {
307 self.region
308 }
309
310 #[doc(hidden)]
311 pub fn region_token_ref(&self) -> &RegionToken<'d> {
312 self.region
313 }
314
315 pub fn driver_ref(&self) -> DriverRef<'d> {
316 self.driver
317 }
318
319 pub fn timer(&self) -> &'d timer::Timer<'d> {
320 self.timer
321 }
322
323 pub fn turn_now(&self) -> Instant {
325 self.driver.turn_now()
326 }
327
328 #[doc(hidden)]
334 pub fn refresh_turn_clock(&mut self) -> Instant {
335 let now = Instant::now();
336 self.driver.shared.turn_clock.set(now);
337 now
338 }
339
340 pub(crate) fn backend(&mut self) -> &mut Backend {
341 self.backend
342 }
343
344 pub(crate) fn backend_ref(&self) -> &Backend {
345 self.backend
346 }
347
348 pub(crate) fn release_buffer(&mut self, buffer: RecvBuffer) {
349 <Backend as BufferBackend>::release_buffer(self.backend(), buffer);
350 }
351
352 pub(crate) fn flush_returned_buffers(&mut self) {
353 while let Some(buffer) = self.driver.shared.returned_buffers.pop_front() {
354 self.release_buffer(buffer);
355 }
356 }
357 pub fn guard(&mut self, fd: Fd<'d>) -> FdGuard<'_, 'd> {
358 let (slot, driver, retire_slot) = fd.into_parts();
359 FdGuard::new(self.backend(), slot, driver, retire_slot)
360 }
361}
362
363#[derive(Clone, Copy, Debug, PartialEq, Eq)]
364pub struct Config {
365 pub ring_entries: u32,
366 pub cq_entries: u32,
367 pub fixed_file_slots: u32,
368 pub accept_slots: u32,
369 pub recv: recv::Config,
370 pub defer_taskrun: bool,
371 pub ready_slots: usize,
372 pub timer_slots: usize,
373}
374
375impl Default for Config {
376 fn default() -> Self {
377 Self {
378 ring_entries: 1024,
379 cq_entries: 2048,
380 fixed_file_slots: 65536,
381 accept_slots: 65536,
382 recv: recv::Config {
383 len: 4096,
384 entries: 128,
385 },
386 defer_taskrun: false,
387 ready_slots: 65536,
388 timer_slots: 65536,
389 }
390 }
391}
392
393impl Config {
394 const MAX_ENTRIES: u32 = 32768;
395
396 pub fn fixed_file_slots(&self) -> u32 {
397 self.fixed_file_slots
398 }
399
400 fn sized_sq(max_connections: u32, outbound_reserve: u32) -> u32 {
401 max_connections
402 .saturating_add(outbound_reserve)
403 .next_power_of_two()
404 .clamp(64, Self::MAX_ENTRIES)
405 }
406
407 pub fn for_profile<P: DriverProfile>() -> Self {
408 Self {
409 ring_entries: P::RING_ENTRIES,
410 cq_entries: P::CQ_ENTRIES,
411 fixed_file_slots: P::FIXED_FILE_SLOTS,
412 accept_slots: P::FIXED_FILE_SLOTS.saturating_sub(P::OUTBOUND_RESERVE),
413 recv: recv::Config {
414 len: P::RECV_BUF_LEN,
415 entries: P::RECV_BUF_ENTRIES,
416 },
417 defer_taskrun: P::DEFER_TASKRUN,
418 ready_slots: P::READY_SLOTS,
419 timer_slots: P::READY_SLOTS,
420 }
421 }
422
423 pub fn for_tcp_profile<P: DriverProfile>(max_connections: usize) -> Self {
424 let max_connections = u32::try_from(max_connections).unwrap_or(u32::MAX);
425 let accept_slots =
426 max_connections.min(P::FIXED_FILE_SLOTS.saturating_sub(P::OUTBOUND_RESERVE));
427 Self {
428 ring_entries: Self::sized_sq(accept_slots, P::OUTBOUND_RESERVE).min(P::RING_ENTRIES),
429 cq_entries: P::CQ_ENTRIES,
430 fixed_file_slots: accept_slots.saturating_add(P::OUTBOUND_RESERVE),
431 accept_slots,
432 recv: recv::Config::for_accept(accept_slots, P::RECV_BUF_LEN, Self::MAX_ENTRIES),
433 defer_taskrun: P::DEFER_TASKRUN,
434 ready_slots: P::READY_SLOTS,
435 timer_slots: P::READY_SLOTS,
436 }
437 }
438
439 pub fn for_quic_udp(recv_buf_entries: u32, recv_buf_len: u32) -> Self {
440 Self {
441 ring_entries: 256,
442 cq_entries: 1024,
443 fixed_file_slots: 16,
444 accept_slots: 0,
445 recv: recv::Config {
446 len: recv_buf_len as usize,
447 entries: recv_buf_entries.min(u16::MAX as u32) as u16,
448 },
449 defer_taskrun: false,
450 ready_slots: 1024,
451 timer_slots: 1024,
452 }
453 }
454
455 pub fn with_recv(mut self, len: usize, entries: u16) -> Self {
456 self.recv.apply_overrides(len, entries);
457 self
458 }
459
460 pub(crate) fn validate(&self) -> Result<()> {
461 if self.timer_slots > u32::MAX as usize {
462 return Err(io::Error::new(
463 ErrorKind::InvalidInput,
464 "dope: timer capacity exceeds u32 slots",
465 ));
466 }
467 if self.accept_slots > self.fixed_file_slots {
468 return Err(io::Error::new(
469 ErrorKind::InvalidInput,
470 "dope: accept slots exceed fixed-file capacity",
471 ));
472 }
473 if TokenCapacity::new(self.fixed_file_slots as usize).is_none() {
474 return Err(io::Error::new(
475 ErrorKind::InvalidInput,
476 "dope: fixed-file capacity exceeds token slots",
477 ));
478 }
479 Backend::snapshot()?
480 .check_slots(self.fixed_file_slots)
481 .map_err(io::Error::from)
482 }
483}
484
485#[derive(Debug, Clone, Copy)]
486pub struct PushError;
487
488impl Display for PushError {
489 fn fmt(&self, formatter: &mut Formatter<'_>) -> fmt::Result {
490 formatter.write_str("dope: SQE push failed")
491 }
492}
493
494impl Error for PushError {}
495
496impl From<PushError> for io::Error {
497 fn from(_: PushError) -> Self {
498 io::Error::from(ErrorKind::WouldBlock)
499 }
500}
501
502pub struct OutboundReservation<'d> {
503 base: u32,
504 capacity: u32,
505 _brand: Invariant<'d>,
506 _thread: ThreadBound,
507}
508
509#[derive(Clone, Copy)]
510pub struct OutboundSlot<'d> {
511 fd: FdSlot,
512 _brand: Invariant<'d>,
513}
514
515impl<'d> OutboundSlot<'d> {
516 pub fn bind(self, driver: DriverRef<'d>) -> Fd<'d> {
517 Fd::from_range_slot(self.fd, driver)
518 }
519}
520
521impl<'d> OutboundReservation<'d> {
522 pub(crate) fn new(base: u32, capacity: u32) -> Self {
523 Self {
524 base,
525 capacity,
526 _brand: PhantomData,
527 _thread: ThreadBound::NEW,
528 }
529 }
530
531 pub fn empty() -> Self {
532 Self {
533 base: 0,
534 capacity: 0,
535 _brand: PhantomData,
536 _thread: ThreadBound::NEW,
537 }
538 }
539
540 pub fn slot(&self, local: SlotIndex) -> Option<OutboundSlot<'d>> {
541 if local.raw() >= self.capacity {
542 return None;
543 }
544 let index = SlotIndex::try_new(self.base.checked_add(local.raw())?)?;
545 Some(OutboundSlot {
546 fd: FdSlot::from_index(index),
547 _brand: PhantomData,
548 })
549 }
550
551 pub(crate) fn into_range(self) -> (u32, u32) {
552 (self.base, self.capacity)
553 }
554}