1use core::{
4 cell::Cell,
5 future::Future,
6 pin::Pin,
7 task::Waker,
8 task::{Context, Poll},
9};
10
11use crate::{
12 Publisher,
13 chain::{Chain, Link},
14 error::FacilityOccupied,
15};
16
17pub struct Facility {
22 queue: Chain<Waker>,
24 in_use: Cell<bool>,
26}
27
28impl Facility {
29 pub fn new() -> Self {
31 Facility {
32 queue: Chain::new(),
33 in_use: Cell::new(false),
34 }
35 }
36
37 pub fn try_seize(&self) -> Result<SeizeGuard<'_>, FacilityOccupied> {
40 if self.in_use.replace(true) {
41 Err(FacilityOccupied)
42 } else {
43 Ok(SeizeGuard(self))
44 }
45 }
46
47 pub fn seize(&self) -> impl Future<Output = SeizeGuard<'_>> + '_ {
49 SeizeFuture::new(self)
50 }
51
52 fn release(&self) {
54 self.in_use.set(false);
55 self.queue.notify_one().go();
56 }
57}
58
59impl Default for Facility {
60 fn default() -> Self {
61 Facility::new()
62 }
63}
64
65#[pin_project::pin_project(PinnedDrop)]
69struct SeizeFuture<'f> {
70 facility: &'f Facility,
72 #[pin]
74 link: Option<Link<Waker>>,
75}
76
77impl<'f> SeizeFuture<'f> {
78 const fn new(facility: &'f Facility) -> Self {
80 SeizeFuture {
81 facility,
82 link: None,
83 }
84 }
85}
86
87impl<'f> Future for SeizeFuture<'f> {
88 type Output = SeizeGuard<'f>;
89
90 fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
91 let mut inner = self.project();
92
93 if let Some(_link) = inner.link.as_ref().as_pin_ref() {
95 debug_assert!(
100 !_link.is_linked(),
101 "reactivation should not be possible without being \
102 unsubscribed from the chain"
103 );
104
105 inner.link.set(None);
107
108 debug_assert!(
109 !inner.facility.in_use.get(),
110 "reactivation should not be possible if the facility is \
111 still in use"
112 );
113
114 inner.facility.in_use.set(true);
118 } else if inner.facility.in_use.replace(true) {
119 inner.link.set(Some(Link::new(cx.waker().clone())));
123
124 let link = unsafe { inner.link.as_ref().as_pin_ref().unwrap_unchecked() };
126
127 unsafe {
129 inner.facility.queue.subscribe(link);
130 }
131
132 return Poll::Pending;
134 }
135
136 Poll::Ready(SeizeGuard(inner.facility))
138 }
139}
140
141#[pin_project::pinned_drop]
142impl PinnedDrop for SeizeFuture<'_> {
143 fn drop(self: Pin<&mut Self>) {
144 if let Some(link) = self.as_ref().project_ref().link.as_pin_ref() {
146 unsafe {
147 self.facility.queue.unsubscribe(link);
148 }
149 }
150 }
151}
152
153#[must_use = "the lifetime of this guard restricts the time this facility is occupied for"]
158pub struct SeizeGuard<'f>(&'f Facility);
159
160impl SeizeGuard<'_> {
161 pub fn release(self) {}
163}
164
165impl Drop for SeizeGuard<'_> {
166 fn drop(&mut self) {
167 self.0.release();
168 }
169}