1use crate::dir::Dir;
2use crate::error::{Error, Result};
3use crate::events::{lock, Chan, Event, EventKind, Hub, Logger};
4use crate::file::File;
5use crate::frame::{self, Frame, FLAG_ERROR, FLAG_NOTIFY, FLAG_RESPONSE, MAX_PAYLOAD_BYTES};
6use crate::wire::*;
7use prost::Message;
8use std::collections::{HashMap, HashSet};
9use std::fmt;
10use std::io;
11use std::net::Shutdown;
12use std::os::unix::net::UnixStream;
13use std::path::{Path, PathBuf};
14use std::sync::atomic::{AtomicBool, AtomicU64, AtomicUsize, Ordering};
15use std::sync::{Arc, Condvar, Mutex, OnceLock};
16use std::thread::JoinHandle;
17use std::time::{Duration, Instant};
18
19#[derive(Clone)]
21pub struct Options {
22 pub pool_size: usize,
24 pub client_name: String,
25 pub logger: Option<Logger>,
29 pub call_timeout: Duration,
36 pub max_inflight: usize,
40}
41
42pub const DEFAULT_MAX_INFLIGHT: usize = 32;
44const MAX_INFLIGHT_CEILING: usize = 1024;
45
46pub type ClientOptions = Options;
48
49impl Default for Options {
50 fn default() -> Options {
51 Options {
52 pool_size: 4,
53 client_name: concat!("mountos-native-rust/", env!("CARGO_PKG_VERSION")).into(),
54 logger: None,
55 call_timeout: Duration::ZERO,
56 max_inflight: DEFAULT_MAX_INFLIGHT,
57 }
58 }
59}
60
61impl fmt::Debug for Options {
62 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
63 f.debug_struct("Options")
64 .field("pool_size", &self.pool_size)
65 .field("client_name", &self.client_name)
66 .field("logger", &self.logger.as_ref().map(|_| "<fn>"))
67 .field("call_timeout", &self.call_timeout)
68 .field("max_inflight", &self.max_inflight)
69 .finish()
70 }
71}
72
73fn aborted(msg: &str) -> Error {
74 Error::Io(io::Error::new(io::ErrorKind::ConnectionAborted, msg.to_string()))
75}
76
77fn join_other(h: JoinHandle<()>) {
79 if h.thread().id() != std::thread::current().id() {
80 let _ = h.join();
81 }
82}
83
84fn required_capability(m: u16) -> Option<&'static str> {
86 match m {
87 method::LS => Some("ls_stat"),
88 method::STAT_BATCH | method::READ_BATCH | method::WRITE_BATCH | method::READ_FILE | method::WRITE_FILE => {
89 Some("bulk_io")
90 }
91 method::REMOVE_ALL => Some("remove_all"),
92 method::WATCH => Some("watch"),
93 method::WARM_OPEN | method::WARM_READ => Some("warm"),
94 _ => None,
95 }
96}
97
98fn write_timeout(e: Error) -> Error {
100 match e {
101 Error::Io(e) if matches!(e.kind(), io::ErrorKind::WouldBlock | io::ErrorKind::TimedOut) => {
102 Error::Timeout(format!("send: {e}"))
103 }
104 e => e,
105 }
106}
107
108static CONN_SEQ: AtomicU64 = AtomicU64::new(1);
109
110struct Waiter {
112 method: u16,
113 watch: Option<Arc<Chan>>,
115 out: Mutex<Option<Result<Frame>>>,
116 cv: Condvar,
117}
118
119impl Waiter {
120 fn set(&self, r: Result<Frame>) {
121 *lock(&self.out) = Some(r);
122 self.cv.notify_one();
123 }
124
125 fn wait(&self, deadline: Option<Instant>) -> Option<Result<Frame>> {
127 let mut g = lock(&self.out);
128 loop {
129 if let Some(r) = g.take() {
130 return Some(r);
131 }
132 g = match deadline {
133 None => self.cv.wait(g).unwrap_or_else(|e| e.into_inner()),
134 Some(d) => {
135 let left = d.saturating_duration_since(Instant::now());
136 if left.is_zero() {
137 return None;
138 }
139 self.cv.wait_timeout(g, left).unwrap_or_else(|e| e.into_inner()).0
140 }
141 };
142 }
143 }
144}
145
146#[derive(Default)]
147struct Core {
148 pending: HashMap<u64, Arc<Waiter>>,
149 abandoned: HashMap<u64, u16>,
152 next_id: u64,
153 end: Option<Error>,
155}
156
157struct Info {
159 max_payload: usize,
160 limit: usize,
162 caps: Option<HashSet<String>>,
165}
166
167pub(crate) struct Sock {
171 id: u64,
172 hub: Arc<Hub>,
173 timeout: Duration,
175 writer: Mutex<UnixStream>,
176 killer: UnixStream,
178 core: Mutex<Core>,
179 dead: AtomicBool,
180 closing: AtomicBool,
182 saw_closing: AtomicBool,
184 info: OnceLock<Info>,
185 reader: Mutex<Option<JoinHandle<()>>>,
186 releasers: Mutex<Vec<JoinHandle<()>>>,
188}
189
190impl Sock {
191 fn open(path: &Path, hub: &Arc<Hub>, timeout: Duration) -> Result<Arc<Sock>> {
194 let stream = UnixStream::connect(path)?;
195 if !timeout.is_zero() {
196 stream.set_write_timeout(Some(timeout))?;
197 }
198 let (rs, killer) = (stream.try_clone()?, stream.try_clone()?);
199 let sock = Arc::new(Sock {
200 id: CONN_SEQ.fetch_add(1, Ordering::SeqCst),
201 hub: hub.clone(),
202 timeout,
203 writer: Mutex::new(stream),
204 killer,
205 core: Mutex::new(Core::default()),
206 dead: AtomicBool::new(false),
207 closing: AtomicBool::new(false),
208 saw_closing: AtomicBool::new(false),
209 info: OnceLock::new(),
210 reader: Mutex::new(None),
211 releasers: Mutex::new(Vec::new()),
212 });
213 let (rsock, guard) = (sock.clone(), hub.thread_guard());
214 let h = std::thread::Builder::new()
215 .name("mountos-reader".into())
216 .spawn(move || {
217 let _guard = guard;
218 read_loop(rs, &rsock)
219 })?;
220 *lock(&sock.reader) = Some(h);
221 Ok(sock)
222 }
223
224 fn handshake(&self, name: &str, max_inflight: usize) -> Result<()> {
225 let req = HelloRequest {
226 client_name: name.into(),
227 min_version: 1,
228 max_version: 1,
229 capabilities: vec!["pipelining".into()],
230 };
231 let (resp, _): (HelloResponse, _) = self.call_msg(method::HELLO, &req, &[])?;
232 if resp.version != 1 {
233 return Err(Error::Protocol(format!("server chose version {}", resp.version)));
234 }
235 let mut max_payload = MAX_PAYLOAD_BYTES as usize;
236 if resp.max_payload_bytes > 0 {
237 max_payload = max_payload.min(resp.max_payload_bytes as usize);
238 }
239 let pipelined = resp.capabilities.iter().any(|c| c == "pipelining");
240 let limit = if pipelined {
241 max_inflight.min(resp.max_inflight as usize).max(1)
242 } else {
243 1
244 };
245 let caps = (!resp.capabilities.is_empty()).then(|| resp.capabilities.into_iter().collect());
246 let _ = self.info.set(Info {
247 max_payload,
248 limit,
249 caps,
250 });
251 Ok(())
252 }
253
254 pub(crate) fn max_payload(&self) -> usize {
255 self.info.get().map_or(MAX_PAYLOAD_BYTES as usize, |i| i.max_payload)
256 }
257
258 fn limit(&self) -> usize {
259 self.info.get().map_or(1, |i| i.limit)
260 }
261
262 fn is_dead(&self) -> bool {
263 self.dead.load(Ordering::SeqCst)
264 }
265
266 fn fail(&self, end: Error, report: bool) {
268 let waiters: Vec<Arc<Waiter>> = {
269 let mut core = lock(&self.core);
270 if core.end.is_some() {
271 return;
272 }
273 core.end = Some(end.dup());
274 self.dead.store(true, Ordering::SeqCst);
275 core.abandoned.clear();
276 core.pending.drain().map(|(_, w)| w).collect()
277 };
278 let _ = self.killer.shutdown(Shutdown::Both);
279 for w in waiters {
280 w.set(Err(end.dup()));
281 }
282 self.hub.conn_lost(self.id);
283 let expected = self.closing.load(Ordering::SeqCst) || self.saw_closing.load(Ordering::SeqCst);
284 if report && !expected {
285 self
286 .hub
287 .publish(None, Event::local(EventKind::ConnectionLost, 0, Some(end.dup())));
288 }
289 }
290
291 pub(crate) fn kill(&self) {
293 self.closing.store(true, Ordering::SeqCst);
294 self.fail(aborted("connection closed"), false);
295 }
296
297 fn kill_with(&self, e: &Error) {
299 self.closing.store(true, Ordering::SeqCst);
300 self.fail(e.dup(), false);
301 }
302
303 fn finish(&self) {
305 self.kill();
306 let reader = lock(&self.reader).take();
307 if let Some(h) = reader {
308 join_other(h);
309 }
310 let releasers = std::mem::take(&mut *lock(&self.releasers));
311 for h in releasers {
312 join_other(h);
313 }
314 }
315
316 fn forget(&self, id: u64) {
317 lock(&self.core).pending.remove(&id);
318 }
319
320 fn call(&self, method: u16, req: &impl Message, payload: &[u8], watch: Option<Arc<Chan>>) -> Result<Frame> {
323 if let Some(cap) = required_capability(method) {
324 if self
325 .info
326 .get()
327 .and_then(|i| i.caps.as_ref())
328 .is_some_and(|c| !c.contains(cap))
329 {
330 return Err(Error::server(
331 "UNSUPPORTED_OPERATION",
332 &format!("the server does not report capability {cap}"),
333 ));
334 }
335 }
336 let meta = req.encode_to_vec();
337 frame::check_limits(meta.len(), payload.len())?;
338 let w = Arc::new(Waiter {
339 method,
340 watch,
341 out: Mutex::new(None),
342 cv: Condvar::new(),
343 });
344 let id = {
345 let mut core = lock(&self.core);
346 if let Some(e) = &core.end {
347 return Err(e.dup());
348 }
349 core.next_id += 1;
350 let id = core.next_id;
351 core.pending.insert(id, w.clone());
352 id
353 };
354 let sent = {
355 let mut s = lock(&self.writer);
356 frame::write_parts(&mut *s, 0, method, id, &meta, payload).map_err(write_timeout)
357 };
358 if let Err(e) = sent {
359 self.forget(id);
361 self.kill_with(&aborted(&format!("send failed: {e}")));
362 return Err(e);
363 }
364 let deadline = (!self.timeout.is_zero()).then(|| Instant::now() + self.timeout);
365 let resp = match w.wait(deadline) {
366 Some(r) => r?,
367 None => self.give_up(id, method, &w)?,
368 };
369 if resp.flags & FLAG_ERROR != 0 {
370 let e = match WireError::decode(&resp.meta[..]) {
371 Ok(e) => e,
372 Err(e) => {
373 let e = Error::Protocol(format!("bad error meta: {e}"));
374 self.kill_with(&e);
375 return Err(e);
376 }
377 };
378 return Err(Error::Server {
379 code: e.code,
380 message: e.message,
381 });
382 }
383 Ok(resp)
384 }
385
386 fn give_up(&self, id: u64, method: u16, w: &Waiter) -> Result<Frame> {
389 let limit = self.limit();
390 let kept = {
391 let mut core = lock(&self.core);
392 if core.pending.remove(&id).is_none() {
393 None
394 } else if limit > 1 && core.abandoned.len() < limit * 4 {
395 core.abandoned.insert(id, method);
396 Some(true)
397 } else {
398 Some(false)
399 }
400 };
401 match kept {
402 None => w.wait(None).unwrap_or_else(|| Err(aborted("connection closed"))),
404 Some(keep) => {
405 if !keep {
406 self.kill();
407 }
408 Err(Error::Timeout(format!(
409 "method {method} did not finish within {:?}",
410 self.timeout
411 )))
412 }
413 }
414 }
415
416 pub(crate) fn call_msg<R: Message + Default>(
418 &self,
419 method: u16,
420 req: &impl Message,
421 payload: &[u8],
422 ) -> Result<(R, Vec<u8>)> {
423 self.decode(self.call(method, req, payload, None)?)
424 }
425
426 pub(crate) fn call_watch(&self, req: &WatchRequest, chan: Arc<Chan>) -> Result<WatchResponse> {
428 Ok(self.decode(self.call(method::WATCH, req, &[], Some(chan))?)?.0)
429 }
430
431 fn decode<R: Message + Default>(&self, resp: Frame) -> Result<(R, Vec<u8>)> {
432 match R::decode(&resp.meta[..]) {
433 Ok(msg) => Ok((msg, resp.payload)),
434 Err(e) => {
435 let e = Error::Protocol(format!("bad response meta: {e}"));
436 self.kill_with(&e);
437 Err(e)
438 }
439 }
440 }
441
442 fn deliver(self: &Arc<Self>, hub: &Hub, f: Frame) -> Result<()> {
444 let unexpected = |f: &Frame| Error::Protocol(format!("unexpected frame: method {} id {}", f.method, f.request_id));
445 if f.flags & FLAG_RESPONSE == 0 {
446 return Err(unexpected(&f));
447 }
448 let waiter = {
449 let mut core = lock(&self.core);
450 match core.pending.get(&f.request_id).map(|w| w.method) {
451 Some(m) if m == f.method => core.pending.remove(&f.request_id),
452 Some(_) => return Err(unexpected(&f)),
453 None => {
454 if core.abandoned.get(&f.request_id) != Some(&f.method) {
455 return Err(unexpected(&f));
456 }
457 core.abandoned.remove(&f.request_id);
458 None
459 }
460 }
461 };
462 let ok = f.flags & (FLAG_RESPONSE | FLAG_ERROR) == FLAG_RESPONSE;
463 match waiter {
464 Some(w) => {
465 if ok && f.method == method::WATCH {
466 if let (Ok(r), Some(chan)) = (WatchResponse::decode(&f.meta[..]), &w.watch) {
467 hub.add_watch(r.watch_id, self.id, chan.clone());
468 }
469 }
470 w.set(Ok(f));
471 }
472 None if ok => self.release_late(f),
473 None => {}
474 }
475 Ok(())
476 }
477
478 fn release_late(self: &Arc<Self>, f: Frame) {
481 let job: Box<dyn FnOnce(&Sock) + Send> = match f.method {
482 method::OPEN => match OpenResponse::decode(&f.meta[..]) {
483 Ok(r) => Box::new(move |s| drop(s.call_msg::<Empty>(method::CLOSE, &CloseRequest { handle: r.handle }, &[]))),
484 Err(_) => return,
485 },
486 method::OPENDIR => match OpendirResponse::decode(&f.meta[..]) {
487 Ok(r) => {
488 Box::new(move |s| drop(s.call_msg::<Empty>(method::CLOSEDIR, &ClosedirRequest { handle: r.handle }, &[])))
489 }
490 Err(_) => return,
491 },
492 method::WATCH => match WatchResponse::decode(&f.meta[..]) {
493 Ok(r) => {
494 Box::new(move |s| drop(s.call_msg::<Empty>(method::UNWATCH, &UnwatchRequest { watch_id: r.watch_id }, &[])))
495 }
496 Err(_) => return,
497 },
498 _ => return,
499 };
500 if self.is_dead() {
501 return;
502 }
503 let (sock, guard) = (self.clone(), self.hub.thread_guard());
504 let spawned = std::thread::Builder::new()
505 .name("mountos-release".into())
506 .spawn(move || {
507 let _guard = guard;
508 job(&sock)
509 });
510 match spawned {
511 Ok(h) => {
512 let mut rs = lock(&self.releasers);
513 rs.retain(|h| !h.is_finished());
514 rs.push(h);
515 }
516 Err(e) => self.hub.log(&format!("could not start release of a late reply: {e}")),
517 }
518 }
519}
520
521fn read_loop(mut s: UnixStream, sock: &Arc<Sock>) {
523 let hub = sock.hub.clone();
524 let end = loop {
525 match frame::read(&mut s) {
526 Ok(Some(f)) if f.flags & FLAG_NOTIFY != 0 => on_notice(f, &hub, sock),
527 Ok(Some(f)) => {
528 if let Err(e) = sock.deliver(&hub, f) {
529 break e;
530 }
531 }
532 Ok(None) => break aborted("server closed the connection"),
533 Err(e) => break e,
534 }
535 };
536 let _ = s.shutdown(Shutdown::Both);
537 sock.fail(end, true);
538}
539
540fn on_notice(f: Frame, hub: &Hub, sock: &Sock) {
541 if f.method != method::NOTICE {
542 hub.log(&format!("ignored notify frame with method {}", f.method));
543 return;
544 }
545 let ev = match Notice::decode(&f.meta[..]) {
546 Ok(m) => match Event::from_wire(m) {
547 Some(ev) => ev,
548 None => {
549 hub.log("ignored notice of unknown kind");
550 return;
551 }
552 },
553 Err(e) => {
554 hub.log(&format!("ignored undecodable notice: {e}"));
555 return;
556 }
557 };
558 if ev.kind == EventKind::Closing {
559 sock.saw_closing.store(true, Ordering::SeqCst);
560 }
561 hub.publish(Some(sock.id), ev);
562}
563
564struct SlotState {
565 sock: Option<Arc<Sock>>,
566 epoch: u64,
568}
569
570#[derive(Default)]
573pub(crate) struct Gate {
574 generation: Mutex<u64>,
575 cv: Condvar,
576 waiting: AtomicUsize,
578}
579
580struct Waiting<'a>(&'a Gate);
582
583impl Drop for Waiting<'_> {
584 fn drop(&mut self) {
585 self.0.waiting.fetch_sub(1, Ordering::SeqCst);
586 self.0.bump();
587 }
588}
589
590impl Gate {
591 fn generation(&self) -> u64 {
592 *lock(&self.generation)
593 }
594
595 fn bump(&self) {
596 *lock(&self.generation) += 1;
597 self.cv.notify_all();
598 }
599
600 fn wait_change(&self, seen: u64) {
601 let mut g = lock(&self.generation);
602 while *g == seen {
603 g = self.cv.wait(g).unwrap_or_else(|e| e.into_inner());
604 }
605 }
606
607 fn wait_for_free(&self) -> Waiting<'_> {
608 self.waiting.fetch_add(1, Ordering::SeqCst);
609 Waiting(self)
610 }
611
612 fn let_waiters_pass(&self) {
615 loop {
616 let seen = self.generation();
617 if self.waiting.load(Ordering::SeqCst) == 0 {
618 return;
619 }
620 self.wait_change(seen);
621 }
622 }
623}
624
625struct Permit<'a>(&'a Slot);
627
628impl Drop for Permit<'_> {
629 fn drop(&mut self) {
630 self.0.outstanding.fetch_sub(1, Ordering::SeqCst);
631 self.0.gate.bump();
632 }
633}
634
635pub(crate) struct Slot {
638 path: PathBuf,
639 name: String,
640 timeout: Duration,
641 max_inflight: usize,
642 hub: Arc<Hub>,
643 gate: Arc<Gate>,
644 outstanding: AtomicUsize,
646 limit: AtomicUsize,
648 closed: AtomicBool,
649 live: Mutex<Option<Arc<Sock>>>,
652 state: Mutex<SlotState>,
653}
654
655impl Slot {
656 fn try_reserve(&self) -> Option<Permit<'_>> {
657 let limit = self.limit.load(Ordering::SeqCst);
658 let mut cur = self.outstanding.load(Ordering::SeqCst);
659 loop {
660 if cur >= limit {
661 return None;
662 }
663 match self
664 .outstanding
665 .compare_exchange_weak(cur, cur + 1, Ordering::SeqCst, Ordering::SeqCst)
666 {
667 Ok(_) => return Some(Permit(self)),
668 Err(c) => cur = c,
669 }
670 }
671 }
672
673 pub(crate) fn run<T>(&self, want: Option<u64>, f: impl FnOnce(&Sock) -> Result<T>) -> Result<(T, u64)> {
676 self.gate.let_waiters_pass();
677 let permit = loop {
678 let seen = self.gate.generation();
679 if self.closed.load(Ordering::SeqCst) {
680 return Err(aborted("client closed"));
681 }
682 if let Some(p) = self.try_reserve() {
683 break p;
684 }
685 self.gate.wait_change(seen);
686 };
687 self.exec(permit, want, f)
688 }
689
690 fn exec<T>(&self, _permit: Permit<'_>, want: Option<u64>, f: impl FnOnce(&Sock) -> Result<T>) -> Result<(T, u64)> {
692 let (sock, epoch) = self.sock_for(want)?;
693 let r = f(&sock);
694 if matches!(r, Err(Error::Io(_)) | Err(Error::Protocol(_))) {
695 sock.kill();
696 }
697 r.map(|v| (v, epoch))
698 }
699
700 fn sock_for(&self, want: Option<u64>) -> Result<(Arc<Sock>, u64)> {
702 let mut st = lock(&self.state);
703 if self.closed.load(Ordering::SeqCst) {
704 return Err(aborted("client closed"));
705 }
706 let alive = st.sock.as_ref().is_some_and(|s| !s.is_dead());
707 if want.is_some_and(|e| !alive || st.epoch != e) {
708 return Err(aborted("connection lost, file handle is gone"));
709 }
710 if !alive {
711 if let Some(old) = st.sock.take() {
712 old.finish();
713 }
714 let s = Sock::open(&self.path, &self.hub, self.timeout)?;
715 *lock(&self.live) = Some(s.clone());
716 let shaken = if self.closed.load(Ordering::SeqCst) {
717 Err(aborted("client closed"))
718 } else {
719 s.handshake(&self.name, self.max_inflight)
720 };
721 if let Err(e) = shaken {
722 s.finish();
723 return Err(e);
724 }
725 self.limit.store(s.limit(), Ordering::SeqCst);
726 st.sock = Some(s);
727 st.epoch += 1;
728 self.gate.bump();
729 }
730 Ok((st.sock.clone().expect("connected above"), st.epoch))
731 }
732
733 fn close_start(&self) {
735 self.closed.store(true, Ordering::SeqCst);
736 if let Some(s) = lock(&self.live).as_ref() {
737 s.kill();
738 }
739 self.gate.bump();
740 }
741
742 fn close_finish(&self) {
744 let s = lock(&self.state).sock.take();
745 if let Some(s) = s {
746 s.finish();
747 }
748 }
749}
750
751pub(crate) struct Inner {
752 slots: Vec<Arc<Slot>>,
753 gate: Arc<Gate>,
754 next: AtomicUsize,
755 pub(crate) hub: Arc<Hub>,
756 dispatcher: Mutex<Option<JoinHandle<()>>>,
757}
758
759impl Inner {
760 fn shutdown(&self) {
762 for s in &self.slots {
763 s.close_start();
764 }
765 for s in &self.slots {
766 s.close_finish();
767 }
768 self.hub.shutdown();
769 let d = lock(&self.dispatcher).take();
770 if let Some(h) = d {
771 join_other(h);
772 }
773 self.hub.clear_listeners();
774 }
775}
776
777impl Drop for Inner {
778 fn drop(&mut self) {
779 self.shutdown();
780 }
781}
782
783#[derive(Clone)]
786pub struct Client {
787 pub(crate) inner: Arc<Inner>,
788}
789
790impl Client {
791 pub fn connect(path: impl AsRef<Path>) -> Result<Client> {
793 Client::connect_with(path, Options::default())
794 }
795
796 pub fn connect_with(path: impl AsRef<Path>, opts: Options) -> Result<Client> {
799 let n = opts.pool_size.max(1);
800 let hub = Arc::new(Hub::new(opts.logger.clone()));
801 let gate = Arc::new(Gate::default());
802 let slots: Vec<_> = (0..n)
803 .map(|_| {
804 Arc::new(Slot {
805 path: path.as_ref().to_path_buf(),
806 name: opts.client_name.clone(),
807 timeout: opts.call_timeout,
808 max_inflight: opts.max_inflight.clamp(1, MAX_INFLIGHT_CEILING),
809 hub: hub.clone(),
810 gate: gate.clone(),
811 outstanding: AtomicUsize::new(0),
812 limit: AtomicUsize::new(1),
813 closed: AtomicBool::new(false),
814 live: Mutex::new(None),
815 state: Mutex::new(SlotState { sock: None, epoch: 0 }),
816 })
817 })
818 .collect();
819 let (h, guard) = (hub.clone(), hub.thread_guard());
820 let dispatcher = std::thread::Builder::new()
821 .name("mountos-dispatch".into())
822 .spawn(move || {
823 let _guard = guard;
824 h.dispatch_loop()
825 })?;
826 let inner = Inner {
827 slots,
828 gate,
829 next: AtomicUsize::new(0),
830 hub,
831 dispatcher: Mutex::new(Some(dispatcher)),
832 };
833 inner.slots[0].run(None, |_| Ok(()))?;
835 Ok(Client { inner: Arc::new(inner) })
836 }
837
838 pub fn close(&self) {
841 self.inner.shutdown();
842 }
843
844 pub(crate) fn pool_size(&self) -> usize {
845 self.inner.slots.len()
846 }
847
848 pub(crate) fn run_any<T>(&self, f: impl FnOnce(&Sock) -> Result<T>) -> Result<(T, &Arc<Slot>, u64)> {
852 let inner = &*self.inner;
853 let n = inner.slots.len();
854 let mut waiter = None;
855 loop {
856 let seen = inner.gate.generation();
857 let start = inner.next.fetch_add(1, Ordering::Relaxed) % n;
858 let mut best: Option<(&Arc<Slot>, usize)> = None;
859 for i in 0..n {
860 let slot = &inner.slots[(start + i) % n];
861 let out = slot.outstanding.load(Ordering::SeqCst);
862 if out >= slot.limit.load(Ordering::SeqCst) {
863 continue;
864 }
865 if best.as_ref().is_none_or(|(_, k)| out < *k) {
866 best = Some((slot, out));
867 }
868 }
869 if let Some((slot, _)) = best {
870 if let Some(permit) = slot.try_reserve() {
872 drop(waiter);
873 let (v, epoch) = slot.exec(permit, None, f)?;
874 return Ok((v, slot, epoch));
875 }
876 continue;
877 }
878 if waiter.is_none() {
879 waiter = Some(inner.gate.wait_for_free());
881 continue;
882 }
883 inner.gate.wait_change(seen);
884 }
885 }
886
887 pub(crate) fn unary<R: Message + Default>(&self, m: u16, req: &impl Message) -> Result<R> {
888 Ok(self.run_any(|c| c.call_msg::<R>(m, req, &[]))?.0 .0)
889 }
890
891 pub fn open(&self, path: &str, flags: u32, mode: u32) -> Result<File> {
893 let ((r, _), slot, epoch) = self.run_any(|c| {
894 c.call_msg::<OpenResponse>(
895 method::OPEN,
896 &OpenRequest {
897 path: path.into(),
898 flags,
899 mode,
900 },
901 &[],
902 )
903 })?;
904 Ok(File::new(slot.clone(), r.handle, epoch))
905 }
906
907 pub fn utimens(&self, path: &str, atime_ns: Option<i64>, mtime_ns: Option<i64>) -> Result<()> {
909 self
910 .unary::<Empty>(
911 method::UTIMENS,
912 &UtimensRequest {
913 path: path.into(),
914 atime_ns: atime_ns.unwrap_or(0),
915 mtime_ns: mtime_ns.unwrap_or(0),
916 set_atime: atime_ns.is_some(),
917 set_mtime: mtime_ns.is_some(),
918 },
919 )
920 .map(drop)
921 }
922
923 pub fn opendir(&self, path: &str) -> Result<Dir> {
925 let ((r, _), slot, epoch) =
926 self.run_any(|c| c.call_msg::<OpendirResponse>(method::OPENDIR, &OpendirRequest { path: path.into() }, &[]))?;
927 Ok(Dir::new(slot.clone(), r.handle, epoch))
928 }
929
930 pub fn ls(&self, path: &str, max_entries: u32, cursor: &str) -> Result<(Vec<DirEntry>, String)> {
933 let r: LsResponse = self.unary(
934 method::LS,
935 &LsRequest {
936 path: path.into(),
937 max_entries,
938 cursor: cursor.into(),
939 },
940 )?;
941 Ok((r.entries, r.next_cursor))
942 }
943}