zebrad/components/sync/
hedge.rs1use std::{
12 future::Future,
13 pin::Pin,
14 sync::{
15 atomic::{AtomicU8, Ordering},
16 Arc, Mutex,
17 },
18 task::{Context, Poll},
19};
20
21use futures::{ready, task::AtomicWaker};
22use pin_project::pin_project;
23use tower::{hedge, BoxError, Service};
24use zebra_network as zn;
25
26use super::{is_not_found, SYNC_RESTART_DELAY};
27
28#[cfg(test)]
29mod tests;
30
31#[derive(Debug)]
33pub(super) struct SuccessHedge<S>(hedge::Hedge<ArmService<S>, HedgePolicy>);
34
35impl<S> SuccessHedge<S>
36where
37 S: Service<zn::Request> + Clone + Send + 'static,
38 S::Future: Send + 'static,
39 S::Error: Into<BoxError>,
40{
41 pub(super) fn new(service: S) -> Self {
43 Self(hedge::Hedge::new(
44 ArmService {
45 inner: service,
46 ready_error: None,
47 },
48 HedgePolicy,
49 20,
50 0.95,
51 2 * SYNC_RESTART_DELAY,
52 ))
53 }
54}
55
56impl<S> Service<zn::Request> for SuccessHedge<S>
57where
58 S: Service<zn::Request> + Clone + Send + 'static,
59 S::Future: Send + 'static,
60 S::Error: Into<BoxError>,
61{
62 type Response = S::Response;
63 type Error = BoxError;
64 type Future = <hedge::Hedge<ArmService<S>, HedgePolicy> as Service<HedgeRequest>>::Future;
65
66 fn poll_ready(&mut self, cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
67 self.0.poll_ready(cx)
68 }
69
70 fn call(&mut self, request: zn::Request) -> Self::Future {
71 self.0.call(HedgeRequest {
72 request,
73 arm: Arm {
74 state: Arc::new(ArmState {
75 flags: AtomicU8::new(1),
76 wakers: [AtomicWaker::new(), AtomicWaker::new()],
77 error: Mutex::new(None),
78 }),
79 index: 0,
80 },
81 })
82 }
83}
84
85#[derive(Debug)]
86struct ArmState {
87 flags: AtomicU8,
89 wakers: [AtomicWaker; 2],
90 error: Mutex<Option<BoxError>>,
92}
93
94#[derive(Debug)]
96struct Arm {
97 state: Arc<ArmState>,
98 index: usize,
99}
100
101impl Arm {
102 fn finish(&self) {
103 let bit = 1 << self.index;
104 if self.state.flags.fetch_and(!bit, Ordering::AcqRel) & bit != 0 {
105 self.state.wakers[1 - self.index].wake();
106 }
107 }
108}
109
110impl Drop for Arm {
111 fn drop(&mut self) {
112 self.finish();
115 }
116}
117
118#[derive(Debug)]
120pub(super) struct HedgeRequest {
121 request: zn::Request,
122 arm: Arm,
123}
124
125#[derive(Clone, Copy, Debug)]
127pub(super) struct HedgePolicy;
128
129impl hedge::Policy<HedgeRequest> for HedgePolicy {
130 fn clone_request(&self, request: &HedgeRequest) -> Option<HedgeRequest> {
131 let payload = request.request.clone();
132 request.arm.state.flags.fetch_or(2, Ordering::AcqRel);
133 Some(HedgeRequest {
134 request: payload,
135 arm: Arm {
136 state: Arc::clone(&request.arm.state),
137 index: 1,
138 },
139 })
140 }
141
142 fn can_retry(&self, _request: &HedgeRequest) -> bool {
143 true
144 }
145}
146
147#[derive(Debug)]
149pub(super) struct ArmService<S> {
150 inner: S,
151 ready_error: Option<BoxError>,
152}
153
154impl<S: Clone> Clone for ArmService<S> {
155 fn clone(&self) -> Self {
156 Self {
159 inner: self.inner.clone(),
160 ready_error: None,
161 }
162 }
163}
164
165impl<S> Service<HedgeRequest> for ArmService<S>
166where
167 S: Service<zn::Request> + Clone + Send + 'static,
168 S::Future: Send + 'static,
169 S::Error: Into<BoxError>,
170{
171 type Response = S::Response;
172 type Error = BoxError;
173 type Future = ArmFuture<S::Future>;
174
175 fn poll_ready(&mut self, cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
176 if self.ready_error.is_none() {
177 if let Err(error) = ready!(self.inner.poll_ready(cx)) {
178 self.ready_error = Some(error.into());
181 self.inner = self.inner.clone();
184 }
185 }
186 Poll::Ready(Ok(()))
187 }
188
189 fn call(&mut self, request: HedgeRequest) -> Self::Future {
190 let error = self.ready_error.take();
191 ArmFuture {
192 inner: error.is_none().then(|| self.inner.call(request.request)),
193 arm: request.arm,
194 error,
195 }
196 }
197}
198
199#[pin_project]
201#[derive(Debug)]
202pub(super) struct ArmFuture<F> {
203 #[pin]
204 inner: Option<F>,
205 arm: Arm,
206 error: Option<BoxError>,
207}
208
209impl<F, T, E> Future for ArmFuture<F>
210where
211 F: Future<Output = Result<T, E>>,
212 E: Into<BoxError>,
213{
214 type Output = Result<T, BoxError>;
215
216 fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
217 let mut this = self.project();
218 if let Some(inner) = this.inner.as_mut().as_pin_mut() {
219 let result = ready!(inner.poll(cx));
220 this.inner.set(None);
221 match result {
222 Ok(response) => {
223 this.arm.finish();
224 return Poll::Ready(Ok(response));
225 }
226 Err(error) => *this.error = Some(error.into()),
227 }
228 }
229 if let Some(error) = this.error.take() {
230 let mut preferred = this
231 .arm
232 .state
233 .error
234 .lock()
235 .expect("thread panicked while holding the hedge error mutex");
236 if preferred.is_none() || is_not_found(error.as_ref()) {
237 *preferred = Some(error);
238 }
239 }
240 this.arm.finish();
241 this.arm.state.wakers[this.arm.index].register(cx.waker());
242 if this.arm.state.flags.load(Ordering::Acquire) == 0 {
243 Poll::Ready(Err(this
244 .arm
245 .state
246 .error
247 .lock()
248 .expect("thread panicked while holding the hedge error mutex")
249 .take()
250 .expect("all arms failed before the hedge returns an error")))
251 } else {
252 Poll::Pending
253 }
254 }
255}