1use crate::fd_table::{
2 allocate_file_description_id, FdResult, FileDescription, ProcessFdTable, SharedFileDescription,
3 FILETYPE_PIPE, O_NONBLOCK, O_RDONLY, O_RDWR, O_WRONLY,
4};
5use crate::poll::{PollEvents, PollNotifier, POLLERR, POLLHUP, POLLIN, POLLOUT};
6use std::collections::{BTreeMap, VecDeque};
7use std::error::Error;
8use std::fmt;
9use std::sync::{Arc, Condvar, Mutex, MutexGuard};
10use std::time::Duration;
11use web_time::Instant;
12
13pub const MAX_PIPE_BUFFER_BYTES: usize = 65_536;
14pub const PIPE_BUF_BYTES: usize = 4_096;
15
16pub type PipeResult<T> = Result<T, PipeError>;
17
18#[derive(Debug, Clone, PartialEq, Eq)]
19pub struct PipeError {
20 code: &'static str,
21 message: String,
22}
23
24impl PipeError {
25 pub fn code(&self) -> &'static str {
26 self.code
27 }
28
29 fn bad_file_descriptor(message: impl Into<String>) -> Self {
30 Self {
31 code: "EBADF",
32 message: message.into(),
33 }
34 }
35
36 fn broken_pipe(message: impl Into<String>) -> Self {
37 Self {
38 code: "EPIPE",
39 message: message.into(),
40 }
41 }
42
43 fn would_block(message: impl Into<String>) -> Self {
44 Self {
45 code: "EAGAIN",
46 message: message.into(),
47 }
48 }
49
50 fn no_reader(message: impl Into<String>) -> Self {
51 Self {
52 code: "ENXIO",
53 message: message.into(),
54 }
55 }
56}
57
58impl fmt::Display for PipeError {
59 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
60 write!(f, "{}: {}", self.code, self.message)
61 }
62}
63
64impl Error for PipeError {}
65
66#[derive(Debug, Clone)]
67pub struct PipeEnd {
68 pub description: SharedFileDescription,
69 pub filetype: u8,
70}
71
72#[derive(Debug, Clone)]
73pub struct PipePair {
74 pub read: PipeEnd,
75 pub write: PipeEnd,
76}
77
78#[derive(Debug, Clone, Copy, PartialEq, Eq)]
79struct PipeRef {
80 pipe_id: u64,
81 end: PipeSide,
82}
83
84#[derive(Debug, Clone, Copy, PartialEq, Eq)]
85enum PipeSide {
86 Read,
87 Write,
88 ReadWrite,
89}
90
91#[derive(Debug, Default)]
92struct PendingRead {
93 length: usize,
94 result: Option<Option<Vec<u8>>>,
95}
96
97#[derive(Debug)]
98struct PipeState {
99 buffer: VecDeque<Vec<u8>>,
100 readers: usize,
101 writers: usize,
102 waiting_reads: VecDeque<u64>,
103 mode: u32,
104 uid: u32,
105 gid: u32,
106 named_key: Option<(u64, u64)>,
107}
108
109impl Default for PipeState {
110 fn default() -> Self {
111 Self {
112 buffer: VecDeque::new(),
113 readers: 0,
114 writers: 0,
115 waiting_reads: VecDeque::new(),
116 mode: 0o600,
117 uid: 0,
118 gid: 0,
119 named_key: None,
120 }
121 }
122}
123
124#[derive(Debug, Clone, Copy, PartialEq, Eq)]
125pub struct PipeMetadata {
126 pub mode: u32,
127 pub uid: u32,
128 pub gid: u32,
129}
130
131#[derive(Debug)]
132struct PipeManagerState {
133 pipes: BTreeMap<u64, PipeState>,
134 desc_to_pipe: BTreeMap<u64, PipeRef>,
135 named_pipes: BTreeMap<(u64, u64), u64>,
136 waiters: BTreeMap<u64, PendingRead>,
137 next_pipe_id: u64,
138 next_waiter_id: u64,
139}
140
141impl Default for PipeManagerState {
142 fn default() -> Self {
143 Self {
144 pipes: BTreeMap::new(),
145 desc_to_pipe: BTreeMap::new(),
146 named_pipes: BTreeMap::new(),
147 waiters: BTreeMap::new(),
148 next_pipe_id: 1,
149 next_waiter_id: 1,
150 }
151 }
152}
153
154#[derive(Debug)]
155struct PipeManagerInner {
156 state: Mutex<PipeManagerState>,
157 waiters: Condvar,
158}
159
160#[derive(Debug, Clone)]
161pub struct PipeManager {
162 inner: Arc<PipeManagerInner>,
163 notifier: Option<PollNotifier>,
164}
165
166impl Default for PipeManager {
167 fn default() -> Self {
168 Self {
169 inner: Arc::new(PipeManagerInner {
170 state: Mutex::new(PipeManagerState::default()),
171 waiters: Condvar::new(),
172 }),
173 notifier: None,
174 }
175 }
176}
177
178impl PipeManager {
179 pub fn is_write_to_read_pair(
180 &self,
181 write_description_id: u64,
182 read_description_id: u64,
183 ) -> bool {
184 let state = lock_or_recover(&self.inner.state);
185 match (
186 state.desc_to_pipe.get(&write_description_id),
187 state.desc_to_pipe.get(&read_description_id),
188 ) {
189 (Some(write), Some(read)) => {
190 write.pipe_id == read.pipe_id
191 && write.end == PipeSide::Write
192 && read.end == PipeSide::Read
193 }
194 _ => false,
195 }
196 }
197
198 pub fn new() -> Self {
199 Self::default()
200 }
201
202 pub(crate) fn with_notifier(notifier: PollNotifier) -> Self {
203 Self {
204 notifier: Some(notifier),
205 ..Self::default()
206 }
207 }
208
209 pub fn create_pipe(&self) -> PipePair {
210 let mut state = lock_or_recover(&self.inner.state);
211 let pipe_id = state.next_pipe_id;
212 state.next_pipe_id += 1;
213
214 let read_id = allocate_file_description_id();
215 let write_id = allocate_file_description_id();
216
217 state.pipes.insert(
218 pipe_id,
219 PipeState {
220 readers: 1,
221 writers: 1,
222 ..PipeState::default()
223 },
224 );
225 state.desc_to_pipe.insert(
226 read_id,
227 PipeRef {
228 pipe_id,
229 end: PipeSide::Read,
230 },
231 );
232 state.desc_to_pipe.insert(
233 write_id,
234 PipeRef {
235 pipe_id,
236 end: PipeSide::Write,
237 },
238 );
239 drop(state);
240
241 PipePair {
242 read: PipeEnd {
243 description: Arc::new(FileDescription::with_ref_count(
244 read_id,
245 format!("pipe:{pipe_id}:read"),
246 O_RDONLY,
247 0,
248 )),
249 filetype: FILETYPE_PIPE,
250 },
251 write: PipeEnd {
252 description: Arc::new(FileDescription::with_ref_count(
253 write_id,
254 format!("pipe:{pipe_id}:write"),
255 O_WRONLY,
256 0,
257 )),
258 filetype: FILETYPE_PIPE,
259 },
260 }
261 }
262
263 pub fn open_named_pipe(
264 &self,
265 key: (u64, u64),
266 path: &str,
267 flags: u32,
268 timeout: Option<Duration>,
269 ) -> PipeResult<PipeEnd> {
270 let access_mode = flags & 0b11;
271 if !matches!(access_mode, O_RDONLY | O_WRONLY | O_RDWR) {
272 return Err(PipeError::bad_file_descriptor("invalid FIFO access mode"));
273 }
274
275 let mut state = lock_or_recover(&self.inner.state);
276 let pipe_id = match state.named_pipes.get(&key).copied() {
277 Some(pipe_id) => pipe_id,
278 None => {
279 let pipe_id = state.next_pipe_id;
280 state.next_pipe_id += 1;
281 state.named_pipes.insert(key, pipe_id);
282 state.pipes.insert(
283 pipe_id,
284 PipeState {
285 named_key: Some(key),
286 ..PipeState::default()
287 },
288 );
289 pipe_id
290 }
291 };
292
293 if access_mode == O_WRONLY
294 && flags & O_NONBLOCK != 0
295 && state
296 .pipes
297 .get(&pipe_id)
298 .is_none_or(|pipe| pipe.readers == 0)
299 {
300 return Err(PipeError::no_reader(format!("FIFO has no reader: {path}")));
301 }
302
303 let description_id = allocate_file_description_id();
304 let side = match access_mode {
305 O_RDONLY => PipeSide::Read,
306 O_WRONLY => PipeSide::Write,
307 O_RDWR => PipeSide::ReadWrite,
308 _ => unreachable!(),
309 };
310 state
311 .desc_to_pipe
312 .insert(description_id, PipeRef { pipe_id, end: side });
313 let pipe = state
314 .pipes
315 .get_mut(&pipe_id)
316 .expect("named pipe must exist after allocation");
317 match side {
318 PipeSide::Read => pipe.readers += 1,
319 PipeSide::Write => pipe.writers += 1,
320 PipeSide::ReadWrite => {
321 pipe.readers += 1;
322 pipe.writers += 1;
323 }
324 }
325 self.notify_waiters_and_pollers();
326
327 let should_wait = flags & O_NONBLOCK == 0 && access_mode != O_RDWR;
328 if should_wait {
329 let ready = |state: &PipeManagerState| {
330 state.pipes.get(&pipe_id).is_some_and(|pipe| match side {
331 PipeSide::Read => pipe.writers > 0,
332 PipeSide::Write => pipe.readers > 0,
333 PipeSide::ReadWrite => true,
334 })
335 };
336 if let Some(timeout) = timeout {
337 let (next, result) = self
338 .inner
339 .waiters
340 .wait_timeout_while(state, timeout, |state| !ready(state))
341 .unwrap_or_else(|poisoned| poisoned.into_inner());
342 state = next;
343 if result.timed_out() && !ready(&state) {
344 drop(state);
345 self.close(description_id);
346 return Err(PipeError::would_block(format!(
347 "FIFO open timed out: {path}"
348 )));
349 }
350 } else {
351 state = self
352 .inner
353 .waiters
354 .wait_while(state, |state| !ready(state))
355 .unwrap_or_else(|poisoned| poisoned.into_inner());
356 }
357 }
358 drop(state);
359
360 Ok(PipeEnd {
361 description: Arc::new(FileDescription::with_ref_count(
362 description_id,
363 path,
364 flags,
365 0,
366 )),
367 filetype: FILETYPE_PIPE,
368 })
369 }
370
371 pub fn poll(&self, description_id: u64, requested: PollEvents) -> PipeResult<PollEvents> {
372 let state = lock_or_recover(&self.inner.state);
373 let pipe_ref = state
374 .desc_to_pipe
375 .get(&description_id)
376 .copied()
377 .ok_or_else(|| PipeError::bad_file_descriptor("not a pipe end"))?;
378 let pipe = state
379 .pipes
380 .get(&pipe_ref.pipe_id)
381 .ok_or_else(|| PipeError::bad_file_descriptor("pipe not found"))?;
382
383 let mut events = PollEvents::empty();
384 match pipe_ref.end {
385 PipeSide::Read => {
386 if requested.intersects(POLLIN) && !pipe.buffer.is_empty() {
387 events |= POLLIN;
388 }
389 if pipe.writers == 0 {
390 events |= POLLHUP;
391 }
392 }
393 PipeSide::Write => {
394 if pipe.readers == 0 {
395 events |= POLLERR;
396 } else if requested.intersects(POLLOUT)
397 && (available_capacity(pipe) > 0 || !pipe.waiting_reads.is_empty())
398 {
399 events |= POLLOUT;
400 }
401 }
402 PipeSide::ReadWrite => {
403 if requested.intersects(POLLIN) && !pipe.buffer.is_empty() {
404 events |= POLLIN;
405 }
406 if requested.intersects(POLLOUT)
407 && (available_capacity(pipe) > 0 || !pipe.waiting_reads.is_empty())
408 {
409 events |= POLLOUT;
410 }
411 }
412 }
413
414 Ok(events)
415 }
416
417 pub fn write(&self, description_id: u64, data: impl AsRef<[u8]>) -> PipeResult<usize> {
418 self.write_with_mode(description_id, data, true)
419 }
420
421 pub fn write_blocking(&self, description_id: u64, data: impl AsRef<[u8]>) -> PipeResult<usize> {
422 self.write_with_mode(description_id, data, false)
423 }
424
425 pub fn write_with_mode(
426 &self,
427 description_id: u64,
428 data: impl AsRef<[u8]>,
429 nonblocking: bool,
430 ) -> PipeResult<usize> {
431 let payload = data.as_ref();
432 let mut state = lock_or_recover(&self.inner.state);
433 let pipe_ref = state
434 .desc_to_pipe
435 .get(&description_id)
436 .copied()
437 .ok_or_else(|| PipeError::bad_file_descriptor("not a pipe write end"))?;
438 if !matches!(pipe_ref.end, PipeSide::Write | PipeSide::ReadWrite) {
439 return Err(PipeError::bad_file_descriptor("not a pipe write end"));
440 }
441
442 loop {
443 let waiter_id = {
444 let pipe = state
445 .pipes
446 .get_mut(&pipe_ref.pipe_id)
447 .ok_or_else(|| PipeError::bad_file_descriptor("pipe not found"))?;
448 if pipe.readers == 0 {
449 return Err(PipeError::broken_pipe("read end closed"));
450 }
451 pipe.waiting_reads.pop_front()
452 };
453
454 if let Some(waiter_id) = waiter_id {
455 let waiter_length = match state.waiters.get(&waiter_id) {
456 Some(waiter) => waiter.length,
457 None => continue,
458 };
459 let delivered_len = waiter_length.min(payload.len());
460 let delivered = payload[..delivered_len].to_vec();
461 let remainder = &payload[delivered_len..];
462
463 if !remainder.is_empty() {
464 let pipe = state
465 .pipes
466 .get_mut(&pipe_ref.pipe_id)
467 .ok_or_else(|| PipeError::bad_file_descriptor("pipe not found"))?;
468 pipe.buffer.push_back(remainder.to_vec());
469 }
470
471 if let Some(waiter) = state.waiters.get_mut(&waiter_id) {
472 waiter.result = Some(Some(delivered));
473 self.notify_waiters_and_pollers();
474 return Ok(payload.len());
475 }
476 continue;
477 }
478
479 let current_buffer_size = {
480 let pipe = state
481 .pipes
482 .get(&pipe_ref.pipe_id)
483 .ok_or_else(|| PipeError::bad_file_descriptor("pipe not found"))?;
484 buffer_size(&pipe.buffer)
485 };
486 let available = MAX_PIPE_BUFFER_BYTES.saturating_sub(current_buffer_size);
487
488 if payload.len() <= PIPE_BUF_BYTES {
489 if available >= payload.len() {
490 let pipe = state
491 .pipes
492 .get_mut(&pipe_ref.pipe_id)
493 .ok_or_else(|| PipeError::bad_file_descriptor("pipe not found"))?;
494 pipe.buffer.push_back(payload.to_vec());
495 self.notify_waiters_and_pollers();
496 return Ok(payload.len());
497 }
498 } else if available > 0 {
499 let chunk_len = available.min(payload.len());
500 let pipe = state
501 .pipes
502 .get_mut(&pipe_ref.pipe_id)
503 .ok_or_else(|| PipeError::bad_file_descriptor("pipe not found"))?;
504 pipe.buffer.push_back(payload[..chunk_len].to_vec());
505 self.notify_waiters_and_pollers();
506 return Ok(chunk_len);
507 }
508
509 if nonblocking {
510 return Err(PipeError::would_block("pipe buffer full"));
511 }
512
513 state = wait_or_recover(&self.inner.waiters, state);
514 }
515 }
516
517 pub fn read(&self, description_id: u64, length: usize) -> PipeResult<Option<Vec<u8>>> {
518 self.read_with_timeout(description_id, length, None)
519 }
520
521 pub fn read_with_timeout(
522 &self,
523 description_id: u64,
524 length: usize,
525 timeout: Option<Duration>,
526 ) -> PipeResult<Option<Vec<u8>>> {
527 let mut state = lock_or_recover(&self.inner.state);
528 let pipe_ref = state
529 .desc_to_pipe
530 .get(&description_id)
531 .copied()
532 .ok_or_else(|| PipeError::bad_file_descriptor("not a pipe read end"))?;
533 if !matches!(pipe_ref.end, PipeSide::Read | PipeSide::ReadWrite) {
534 return Err(PipeError::bad_file_descriptor("not a pipe read end"));
535 }
536
537 let mut waiter_id = None;
538 let deadline = timeout.map(|duration| Instant::now() + duration);
539
540 loop {
541 if let Some(id) = waiter_id {
542 if let Some(waiter) = state.waiters.get_mut(&id) {
543 if let Some(result) = waiter.result.take() {
544 state.waiters.remove(&id);
545 return Ok(result);
546 }
547 }
548 }
549
550 {
551 let pipe = state
552 .pipes
553 .get_mut(&pipe_ref.pipe_id)
554 .ok_or_else(|| PipeError::bad_file_descriptor("pipe not found"))?;
555
556 if !pipe.buffer.is_empty() {
557 let result = drain_buffer(&mut pipe.buffer, length);
558 self.notify_waiters_and_pollers();
559 return Ok(Some(result));
560 }
561
562 if pipe.writers == 0 {
563 if let Some(id) = waiter_id {
564 state.waiters.remove(&id);
565 }
566 return Ok(None);
567 }
568 }
569
570 if waiter_id.is_none() && deadline.is_some_and(|deadline| Instant::now() >= deadline) {
575 return Err(PipeError::would_block("pipe read timed out"));
576 }
577
578 let id = if let Some(id) = waiter_id {
579 id
580 } else {
581 let next = state.next_waiter_id;
582 state.next_waiter_id += 1;
583 state.waiters.insert(
584 next,
585 PendingRead {
586 length,
587 result: None,
588 },
589 );
590 let Some(pipe) = state.pipes.get_mut(&pipe_ref.pipe_id) else {
591 state.waiters.remove(&next);
592 return Err(PipeError::bad_file_descriptor("pipe not found"));
593 };
594 pipe.waiting_reads.push_back(next);
595 self.notify_waiters_and_pollers();
596 waiter_id = Some(next);
597 next
598 };
599
600 let Some(deadline) = deadline else {
601 state = wait_or_recover(&self.inner.waiters, state);
602 if !state.waiters.contains_key(&id) {
603 waiter_id = None;
604 }
605 continue;
606 };
607
608 let now = Instant::now();
609 if now >= deadline {
610 if let Some(id) = waiter_id.take() {
611 state.waiters.remove(&id);
612 if let Some(pipe) = state.pipes.get_mut(&pipe_ref.pipe_id) {
613 pipe.waiting_reads.retain(|queued| *queued != id);
614 }
615 self.notify_waiters_and_pollers();
616 }
617 return Err(PipeError::would_block("pipe read timed out"));
618 }
619
620 let remaining = deadline.saturating_duration_since(now);
621 let (next_state, wait_result) =
622 wait_timeout_or_recover(&self.inner.waiters, state, remaining);
623 state = next_state;
624 if !state.waiters.contains_key(&id) {
625 waiter_id = None;
626 }
627 if wait_result.timed_out() {
628 if let Some(id) = waiter_id.take() {
629 state.waiters.remove(&id);
630 if let Some(pipe) = state.pipes.get_mut(&pipe_ref.pipe_id) {
631 pipe.waiting_reads.retain(|queued| *queued != id);
632 }
633 self.notify_waiters_and_pollers();
634 }
635 return Err(PipeError::would_block("pipe read timed out"));
636 }
637 }
638 }
639
640 pub fn close(&self, description_id: u64) {
641 let mut state = lock_or_recover(&self.inner.state);
642 let Some(pipe_ref) = state.desc_to_pipe.remove(&description_id) else {
643 return;
644 };
645
646 let (waiter_ids, remove_pipe, should_notify) =
647 if let Some(pipe) = state.pipes.get_mut(&pipe_ref.pipe_id) {
648 match pipe_ref.end {
649 PipeSide::Read => {
650 pipe.readers = pipe.readers.saturating_sub(1);
651 (Vec::new(), pipe.readers == 0 && pipe.writers == 0, true)
652 }
653 PipeSide::Write => {
654 pipe.writers = pipe.writers.saturating_sub(1);
655 let waiter_ids = if pipe.writers == 0 {
656 pipe.waiting_reads.drain(..).collect::<Vec<_>>()
657 } else {
658 Vec::new()
659 };
660 (waiter_ids, pipe.readers == 0 && pipe.writers == 0, true)
661 }
662 PipeSide::ReadWrite => {
663 pipe.readers = pipe.readers.saturating_sub(1);
664 pipe.writers = pipe.writers.saturating_sub(1);
665 let waiter_ids = if pipe.writers == 0 {
666 pipe.waiting_reads.drain(..).collect::<Vec<_>>()
667 } else {
668 Vec::new()
669 };
670 (waiter_ids, pipe.readers == 0 && pipe.writers == 0, true)
671 }
672 }
673 } else {
674 (Vec::new(), false, false)
675 };
676
677 for waiter_id in waiter_ids {
678 if let Some(waiter) = state.waiters.get_mut(&waiter_id) {
679 waiter.result = Some(None);
680 }
681 }
682
683 if remove_pipe {
684 if let Some(pipe) = state.pipes.remove(&pipe_ref.pipe_id) {
685 if let Some(key) = pipe.named_key {
686 state.named_pipes.remove(&key);
687 }
688 }
689 }
690 if should_notify {
691 self.notify_waiters_and_pollers();
692 }
693 }
694
695 pub fn is_pipe(&self, description_id: u64) -> bool {
696 lock_or_recover(&self.inner.state)
697 .desc_to_pipe
698 .contains_key(&description_id)
699 }
700
701 pub fn pipe_id_for(&self, description_id: u64) -> Option<u64> {
702 lock_or_recover(&self.inner.state)
703 .desc_to_pipe
704 .get(&description_id)
705 .map(|pipe_ref| pipe_ref.pipe_id)
706 }
707
708 pub fn metadata(&self, description_id: u64) -> Option<PipeMetadata> {
709 let state = lock_or_recover(&self.inner.state);
710 let pipe_id = state.desc_to_pipe.get(&description_id)?.pipe_id;
711 let pipe = state.pipes.get(&pipe_id)?;
712 Some(PipeMetadata {
713 mode: pipe.mode,
714 uid: pipe.uid,
715 gid: pipe.gid,
716 })
717 }
718
719 pub fn set_owner(&self, description_id: u64, uid: u32, gid: u32) -> PipeResult<()> {
720 let mut state = lock_or_recover(&self.inner.state);
721 let pipe_id = state
722 .desc_to_pipe
723 .get(&description_id)
724 .ok_or_else(|| PipeError::bad_file_descriptor("not a pipe end"))?
725 .pipe_id;
726 let pipe = state
727 .pipes
728 .get_mut(&pipe_id)
729 .ok_or_else(|| PipeError::bad_file_descriptor("pipe not found"))?;
730 pipe.uid = uid;
731 pipe.gid = gid;
732 Ok(())
733 }
734
735 pub fn chmod(&self, description_id: u64, mode: u32) -> PipeResult<()> {
736 let mut state = lock_or_recover(&self.inner.state);
737 let pipe_id = state
738 .desc_to_pipe
739 .get(&description_id)
740 .ok_or_else(|| PipeError::bad_file_descriptor("not a pipe end"))?
741 .pipe_id;
742 let pipe = state
743 .pipes
744 .get_mut(&pipe_id)
745 .ok_or_else(|| PipeError::bad_file_descriptor("pipe not found"))?;
746 pipe.mode = mode & 0o7777;
747 Ok(())
748 }
749
750 pub fn pipe_count(&self) -> usize {
751 lock_or_recover(&self.inner.state).pipes.len()
752 }
753
754 pub fn has_named_pipe(&self, key: (u64, u64)) -> bool {
755 lock_or_recover(&self.inner.state)
756 .named_pipes
757 .contains_key(&key)
758 }
759
760 pub fn named_pipe_peer_ready(&self, description_id: u64) -> PipeResult<Option<bool>> {
761 let state = lock_or_recover(&self.inner.state);
762 let pipe_ref = state
763 .desc_to_pipe
764 .get(&description_id)
765 .copied()
766 .ok_or_else(|| PipeError::bad_file_descriptor("not a pipe end"))?;
767 let pipe = state
768 .pipes
769 .get(&pipe_ref.pipe_id)
770 .ok_or_else(|| PipeError::bad_file_descriptor("pipe not found"))?;
771 if pipe.named_key.is_none() {
772 return Ok(None);
773 }
774 Ok(Some(match pipe_ref.end {
775 PipeSide::Read => pipe.writers > 0,
776 PipeSide::Write => pipe.readers > 0,
777 PipeSide::ReadWrite => true,
778 }))
779 }
780
781 pub fn buffered_bytes(&self) -> usize {
782 lock_or_recover(&self.inner.state)
783 .pipes
784 .values()
785 .map(|pipe| buffer_size(&pipe.buffer))
786 .sum()
787 }
788
789 pub fn waiting_reader_count(&self, description_id: u64) -> PipeResult<usize> {
790 let state = lock_or_recover(&self.inner.state);
791 let pipe_ref = state
792 .desc_to_pipe
793 .get(&description_id)
794 .copied()
795 .ok_or_else(|| PipeError::bad_file_descriptor("not a pipe end"))?;
796 let pipe = state
797 .pipes
798 .get(&pipe_ref.pipe_id)
799 .ok_or_else(|| PipeError::bad_file_descriptor("pipe not found"))?;
800 Ok(pipe.waiting_reads.len())
801 }
802
803 pub fn pending_read_waiter_count(&self) -> usize {
804 lock_or_recover(&self.inner.state).waiters.len()
805 }
806
807 pub fn create_pipe_fds(&self, fd_table: &mut ProcessFdTable) -> FdResult<(u32, u32)> {
808 let pipe = self.create_pipe();
809 let read_fd =
810 fd_table.open_with(Arc::clone(&pipe.read.description), FILETYPE_PIPE, None)?;
811 match fd_table.open_with(Arc::clone(&pipe.write.description), FILETYPE_PIPE, None) {
812 Ok(write_fd) => Ok((read_fd, write_fd)),
813 Err(error) => {
814 fd_table.close(read_fd);
815 self.close(pipe.read.description.id());
816 self.close(pipe.write.description.id());
817 Err(error)
818 }
819 }
820 }
821
822 fn notify_waiters_and_pollers(&self) {
823 self.inner.waiters.notify_all();
824 if let Some(notifier) = &self.notifier {
825 notifier.notify();
826 }
827 }
828}
829
830fn buffer_size(buffer: &VecDeque<Vec<u8>>) -> usize {
831 buffer.iter().map(Vec::len).sum()
832}
833
834fn available_capacity(pipe: &PipeState) -> usize {
835 MAX_PIPE_BUFFER_BYTES.saturating_sub(buffer_size(&pipe.buffer))
836}
837
838fn drain_buffer(buffer: &mut VecDeque<Vec<u8>>, length: usize) -> Vec<u8> {
839 let mut chunks = Vec::new();
840 let mut remaining = length;
841
842 while remaining > 0 {
843 let Some(chunk) = buffer.pop_front() else {
844 break;
845 };
846 if chunk.len() <= remaining {
847 remaining -= chunk.len();
848 chunks.push(chunk);
849 } else {
850 let (head, tail) = chunk.split_at(remaining);
851 chunks.push(head.to_vec());
852 buffer.push_front(tail.to_vec());
853 remaining = 0;
854 }
855 }
856
857 if chunks.len() == 1 {
858 return chunks.pop().expect("single chunk should exist");
859 }
860
861 let total = chunks.iter().map(Vec::len).sum();
862 let mut result = Vec::with_capacity(total);
863 for chunk in chunks {
864 result.extend_from_slice(&chunk);
865 }
866 result
867}
868
869fn lock_or_recover<'a, T>(mutex: &'a Mutex<T>) -> MutexGuard<'a, T> {
870 match mutex.lock() {
871 Ok(guard) => guard,
872 Err(poisoned) => poisoned.into_inner(),
873 }
874}
875
876fn wait_or_recover<'a, T>(condvar: &Condvar, guard: MutexGuard<'a, T>) -> MutexGuard<'a, T> {
877 match condvar.wait(guard) {
878 Ok(guard) => guard,
879 Err(poisoned) => poisoned.into_inner(),
880 }
881}
882
883fn wait_timeout_or_recover<'a, T>(
884 condvar: &Condvar,
885 guard: MutexGuard<'a, T>,
886 timeout: Duration,
887) -> (MutexGuard<'a, T>, std::sync::WaitTimeoutResult) {
888 match condvar.wait_timeout(guard, timeout) {
889 Ok(result) => result,
890 Err(poisoned) => poisoned.into_inner(),
891 }
892}
893
894#[cfg(test)]
895mod tests {
896 use super::*;
897
898 #[test]
899 fn zero_timeout_empty_read_does_not_publish_false_readiness() {
900 let notifier = PollNotifier::default();
901 let manager = PipeManager::with_notifier(notifier.clone());
902 let pipe = manager.create_pipe();
903 let observed = notifier.snapshot();
904
905 let error = manager
906 .read_with_timeout(pipe.read.description.id(), 1, Some(Duration::ZERO))
907 .expect_err("empty nonblocking read must return EAGAIN");
908
909 assert_eq!(error.code(), "EAGAIN");
910 assert_eq!(notifier.snapshot(), observed);
911 let state = lock_or_recover(&manager.inner.state);
912 assert!(state.waiters.is_empty());
913 assert_eq!(state.next_waiter_id, 1);
914 }
915}