Documentation
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
use std::{collections::VecDeque, fmt, task::Poll};

use crate::{
	Closed,
	lock::Lock,
	waiter::{Waiter, WaiterList},
};

/// The push failed; the item is handed back.
#[non_exhaustive]
pub enum PushError<T> {
	/// The queue is at capacity. Only a bounded [`Queue`]'s `try_push` reports this.
	Full(T),
	/// The queue was closed; nothing will ever pop the item.
	Closed(T),
}

impl<T> PushError<T> {
	/// Recover the item that failed to push.
	pub fn into_inner(self) -> T {
		match self {
			Self::Full(item) | Self::Closed(item) => item,
		}
	}
}

// Manual, so the error is debuggable without requiring `T: Debug`.
impl<T> fmt::Debug for PushError<T> {
	fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
		match self {
			Self::Full(_) => f.debug_tuple("Full").finish_non_exhaustive(),
			Self::Closed(_) => f.debug_tuple("Closed").finish_non_exhaustive(),
		}
	}
}

impl<T> fmt::Display for PushError<T> {
	fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
		match self {
			Self::Full(_) => write!(f, "queue full"),
			Self::Closed(_) => write!(f, "queue closed"),
		}
	}
}

impl<T> std::error::Error for PushError<T> {}

#[derive(Debug)]
struct State<T> {
	queue: VecDeque<T>,
	/// `None` = unbounded.
	capacity: Option<usize>,
	closed: bool,
	/// Parked pops, woken by a push or close.
	waiters_pop: WaiterList,
	/// Parked pushes, woken by a pop or close. Always empty when unbounded.
	waiters_push: WaiterList,
}

impl<T> State<T> {
	fn has_space(&self) -> bool {
		self.capacity.is_none_or(|capacity| self.queue.len() < capacity)
	}
}

/// A poll-native FIFO queue with waker notification.
///
/// Role-less like [`Shared`](crate::Shared): every clone can push, pop, or close,
/// and there is no liveness of its own: closure is explicit via
/// [`close`](Self::close), never implied by dropping handles. Bounded queues park
/// pushes at capacity ([`poll_push_with`](Self::poll_push_with) /
/// [`push`](Self::push)) or reject them ([`try_push`](Self::try_push)); pops park
/// while empty. After close, pops drain what's queued before reporting [`Closed`].
///
/// An event wakes *every* waiter parked on the affected side (there is no
/// wake-one): with several handles racing to pop, one wins and the rest re-park.
#[derive(Debug)]
pub struct Queue<T> {
	state: Lock<State<T>>,
}

impl<T> Queue<T> {
	/// Create an unbounded queue: pushes never park or report full.
	pub fn new() -> Self {
		Self::with_capacity(None)
	}

	/// Create a bounded queue holding at most `capacity` items.
	///
	/// Panics if `capacity` is zero, which could never accept an item.
	pub fn bounded(capacity: usize) -> Self {
		assert!(capacity > 0, "a zero-capacity Queue could never accept an item");
		Self::with_capacity(Some(capacity))
	}

	fn with_capacity(capacity: Option<usize>) -> Self {
		Self {
			state: Lock::new(State {
				queue: VecDeque::new(),
				capacity,
				closed: false,
				waiters_pop: WaiterList::new(),
				waiters_push: WaiterList::new(),
			}),
		}
	}

	/// Push without waiting, or hand the item back when closed or (if bounded) full.
	pub fn try_push(&self, item: T) -> Result<(), PushError<T>> {
		let mut waiters = {
			let mut state = self.state.lock();
			if state.closed {
				return Err(PushError::Closed(item));
			}
			if !state.has_space() {
				return Err(PushError::Full(item));
			}
			state.queue.push_back(item);
			state.waiters_pop.take()
		};
		waiters.wake();
		Ok(())
	}

	/// Poll to push, building the item only once there is room for it.
	///
	/// The item comes from a closure so a pending poll costs nothing: nothing is
	/// built, nothing needs handing back, and the caller retries with a fresh
	/// closure. `make` runs with the queue lock held, so it must not touch this queue
	/// (or anything that does).
	///
	/// Registers `waiter` while full; a pop or close re-polls it.
	pub fn poll_push_with<F: FnOnce() -> T>(&self, waiter: &Waiter, make: F) -> Poll<Result<(), Closed>> {
		let mut waiters = {
			let mut state = self.state.lock();
			if state.closed {
				return Poll::Ready(Err(Closed));
			}
			if !state.has_space() {
				waiter.register(&mut state.waiters_push);
				return Poll::Pending;
			}
			let item = make();
			state.queue.push_back(item);
			state.waiters_pop.take()
		};
		waiters.wake();
		Poll::Ready(Ok(()))
	}

	/// Push, waiting for room while a bounded queue is full.
	///
	/// Returns [`Closed`] if the queue closes first (or already was); the item is
	/// dropped in that case; use [`try_push`](Self::try_push) to get it back.
	pub async fn push(&self, item: T) -> Result<(), Closed> {
		let mut item = Some(item);
		// Capture the slot by `&mut` so the closure is `Unpin` regardless of `T`.
		let slot = &mut item;
		crate::wait(move |waiter| self.poll_push_with(waiter, || slot.take().expect("polled after completion"))).await
	}

	/// Pop without waiting.
	///
	/// `Ok(None)` means the queue is empty but still open. Queued items drain
	/// before closure is reported: [`Closed`] means empty *and* closed.
	pub fn try_pop(&self) -> Result<Option<T>, Closed> {
		let (item, mut waiters) = {
			let mut state = self.state.lock();
			match state.queue.pop_front() {
				Some(item) => (item, state.waiters_push.take()),
				None if state.closed => return Err(Closed),
				None => return Ok(None),
			}
		};
		waiters.wake();
		Ok(Some(item))
	}

	/// Poll for the next item.
	///
	/// Queued items drain before closure is reported, so [`Closed`] means empty
	/// *and* closed. Registers `waiter` while empty; a push or close re-polls it.
	pub fn poll_pop(&self, waiter: &Waiter) -> Poll<Result<T, Closed>> {
		let (item, mut waiters) = {
			let mut state = self.state.lock();
			match state.queue.pop_front() {
				Some(item) => (item, state.waiters_push.take()),
				None if state.closed => return Poll::Ready(Err(Closed)),
				None => {
					waiter.register(&mut state.waiters_pop);
					return Poll::Pending;
				}
			}
		};
		waiters.wake();
		Poll::Ready(Ok(item))
	}

	/// Pop, waiting for an item while the queue is empty.
	///
	/// Returns [`Closed`] once the queue is both closed and drained.
	pub async fn pop(&self) -> Result<T, Closed> {
		crate::wait(move |waiter| self.poll_pop(waiter)).await
	}

	/// Close the queue: pushes fail from here on, pops drain what's queued and then
	/// report [`Closed`]. Idempotent.
	pub fn close(&self) {
		let mut waiters = {
			let mut state = self.state.lock();
			if state.closed {
				return;
			}
			state.closed = true;
			// Every waiter reacts to closure; wake both sides after unlocking.
			[state.waiters_pop.take(), state.waiters_push.take()]
		};
		for list in &mut waiters {
			list.wake();
		}
	}

	/// Whether [`close`](Self::close) has been called. Items may still be queued.
	pub fn is_closed(&self) -> bool {
		self.state.lock().closed
	}

	/// Number of items currently queued.
	pub fn len(&self) -> usize {
		self.state.lock().queue.len()
	}

	/// Whether nothing is currently queued.
	pub fn is_empty(&self) -> bool {
		self.state.lock().queue.is_empty()
	}

	/// The capacity bound, or `None` when unbounded.
	pub fn capacity(&self) -> Option<usize> {
		self.state.lock().capacity
	}

	/// Returns `true` if both handles share the same underlying queue.
	pub fn same_channel(&self, other: &Self) -> bool {
		self.state.is_clone(&other.state)
	}
}

impl<T> Default for Queue<T> {
	fn default() -> Self {
		Self::new()
	}
}

impl<T> Clone for Queue<T> {
	fn clone(&self) -> Self {
		Self {
			state: self.state.clone(),
		}
	}
}

#[cfg(all(test, not(loom)))]
mod test {
	use std::{
		sync::{
			Arc,
			atomic::{AtomicUsize, Ordering},
		},
		task::{Wake, Waker},
	};

	use super::*;

	/// A waker that counts how many times it was woken (mirrors `tests.rs`).
	struct CountWaker(AtomicUsize);
	impl CountWaker {
		fn count(&self) -> usize {
			self.0.load(Ordering::SeqCst)
		}
	}
	impl Wake for CountWaker {
		fn wake(self: Arc<Self>) {
			self.0.fetch_add(1, Ordering::SeqCst);
		}
		fn wake_by_ref(self: &Arc<Self>) {
			self.0.fetch_add(1, Ordering::SeqCst);
		}
	}
	fn counting() -> (Arc<CountWaker>, Waker) {
		let waker = Arc::new(CountWaker(AtomicUsize::new(0)));
		let w = Waker::from(waker.clone());
		(waker, w)
	}

	#[test]
	fn fifo_order() {
		let queue = Queue::new();
		queue.try_push(1).unwrap();
		queue.try_push(2).unwrap();
		queue.try_push(3).unwrap();

		assert_eq!(queue.try_pop().unwrap(), Some(1));
		assert_eq!(queue.try_pop().unwrap(), Some(2));
		assert_eq!(queue.try_pop().unwrap(), Some(3));
		assert_eq!(queue.try_pop().unwrap(), None, "empty but open");
	}

	#[test]
	fn bounded_rejects_when_full_and_returns_the_item() {
		let queue = Queue::bounded(1);
		queue.try_push(1).unwrap();

		match queue.try_push(2) {
			Err(PushError::Full(item)) => assert_eq!(item, 2),
			other => panic!("expected Full, got {other:?}"),
		}

		// Popping frees the slot.
		assert_eq!(queue.try_pop().unwrap(), Some(1));
		queue.try_push(2).unwrap();
	}

	#[test]
	fn closed_rejects_pushes_and_drains_pops() {
		let queue = Queue::new();
		queue.try_push(1).unwrap();
		queue.close();
		queue.close(); // idempotent

		match queue.try_push(2) {
			Err(PushError::Closed(item)) => assert_eq!(item, 2),
			other => panic!("expected Closed, got {other:?}"),
		}

		// The queued item drains before closure is reported.
		assert_eq!(queue.try_pop().unwrap(), Some(1));
		assert_eq!(queue.try_pop(), Err(Closed));

		let waiter = Waiter::noop();
		assert_eq!(queue.poll_pop(&waiter), Poll::Ready(Err(Closed)));
		assert_eq!(queue.poll_push_with(&waiter, || 3), Poll::Ready(Err(Closed)));
	}

	#[test]
	fn push_wakes_a_parked_pop() {
		let queue = Queue::new();
		let (waker, w) = counting();
		// One waiter across both polls, standing in for what a `Park` retains.
		let waiter = Waiter::new(w);

		assert!(queue.poll_pop(&waiter).is_pending());
		queue.try_push(7).unwrap();
		assert!(waker.count() >= 1, "push should wake the parked pop");
		assert_eq!(queue.poll_pop(&waiter), Poll::Ready(Ok(7)));
	}

	#[test]
	fn pop_wakes_a_parked_push_and_defers_the_item() {
		let queue = Queue::bounded(1);
		queue.try_push(1).unwrap();

		let (waker, w) = counting();
		let waiter = Waiter::new(w);

		// Full: the closure must not run, and the poll parks.
		let made = std::cell::Cell::new(false);
		assert!(
			queue
				.poll_push_with(&waiter, || {
					made.set(true);
					2
				})
				.is_pending()
		);
		assert!(!made.get(), "the item must not be built while full");

		// A pop frees the slot and wakes the parked push.
		assert_eq!(queue.try_pop().unwrap(), Some(1));
		assert!(waker.count() >= 1, "pop should wake the parked push");
		assert_eq!(queue.poll_push_with(&waiter, || 2), Poll::Ready(Ok(())));
		assert_eq!(queue.try_pop().unwrap(), Some(2));
	}

	#[test]
	fn close_wakes_both_sides() {
		let queue = Queue::<u32>::bounded(1);
		queue.try_push(1).unwrap();

		let (pop_waker, w1) = counting();
		let pop_waiter = Waiter::new(w1);
		// Park a pop on a second handle (the queued item is popped first).
		let popper = queue.clone();
		assert_eq!(popper.poll_pop(&pop_waiter), Poll::Ready(Ok(1)));
		assert!(popper.poll_pop(&pop_waiter).is_pending());

		// Refill so a push parks too.
		queue.try_push(2).unwrap();
		let (push_waker, w2) = counting();
		let push_waiter = Waiter::new(w2);
		assert!(queue.poll_push_with(&push_waiter, || 3).is_pending());

		queue.close();
		assert!(pop_waker.count() >= 1, "close should wake the parked pop");
		assert!(push_waker.count() >= 1, "close should wake the parked push");

		// The parked pop drains the remaining item; the push observes closure.
		assert_eq!(popper.poll_pop(&pop_waiter), Poll::Ready(Ok(2)));
		assert_eq!(popper.poll_pop(&pop_waiter), Poll::Ready(Err(Closed)));
		assert_eq!(queue.poll_push_with(&push_waiter, || 3), Poll::Ready(Err(Closed)));
	}

	#[test]
	fn accessors() {
		let queue = Queue::bounded(2);
		assert_eq!(queue.capacity(), Some(2));
		assert!(queue.is_empty());
		queue.try_push(1).unwrap();
		assert_eq!(queue.len(), 1);
		assert!(!queue.is_empty());
		assert!(!queue.is_closed());

		let clone = queue.clone();
		let other = Queue::<u32>::new();
		assert!(queue.same_channel(&clone));
		assert!(!queue.same_channel(&other));
		assert_eq!(other.capacity(), None);
	}

	#[test]
	#[should_panic(expected = "zero-capacity")]
	fn zero_capacity_panics() {
		let _ = Queue::<u32>::bounded(0);
	}

	#[tokio::test]
	async fn async_push_parks_until_popped() {
		let queue = Queue::bounded(1);
		queue.try_push(1u32).unwrap();

		let pusher = queue.clone();
		let task = tokio::spawn(async move { pusher.push(2).await });

		// Let the push park on the full queue before making room.
		tokio::task::yield_now().await;
		assert_eq!(queue.pop().await, Ok(1));

		task.await.unwrap().unwrap();
		assert_eq!(queue.pop().await, Ok(2));
	}

	#[tokio::test]
	async fn async_pop_parks_until_pushed() {
		let queue = Queue::new();
		let popper = queue.clone();
		let task = tokio::spawn(async move { popper.pop().await });

		tokio::task::yield_now().await;
		queue.try_push(9u32).unwrap();

		assert_eq!(task.await.unwrap(), Ok(9));
	}

	#[tokio::test]
	async fn async_ops_observe_close() {
		let queue = Queue::<u32>::bounded(1);
		queue.try_push(1).unwrap();

		let pusher = queue.clone();
		let push = tokio::spawn(async move { pusher.push(2).await });
		tokio::task::yield_now().await;
		queue.close();

		assert_eq!(push.await.unwrap(), Err(Closed));
		// Pops still drain the queued item before reporting closure.
		assert_eq!(queue.pop().await, Ok(1));
		assert_eq!(queue.pop().await, Err(Closed));
	}
}