1use std::{
2 future::Future,
3 hash::{BuildHasher, Hash},
4 hint::unreachable_unchecked,
5 marker::PhantomPinned,
6 mem, pin,
7 task::{self, Poll},
8 time::{Duration, Instant},
9};
10
11use crate::{
12 linked_slab::Token,
13 shard::CacheShard,
14 shim::{
15 rw_lock::{RwLock, RwLockWriteGuard},
16 sync::{
17 atomic::{AtomicBool, Ordering},
18 Arc,
19 },
20 thread, OnceLock,
21 },
22 Equivalent, Lifecycle, Weighter,
23};
24
25pub type SharedPlaceholder<Val> = Arc<Placeholder<Val>>;
26
27impl<Val> crate::shard::SharedPlaceholder for SharedPlaceholder<Val> {
28 fn new(hash: u64, idx: Token) -> Self {
29 Arc::new(Placeholder {
30 hash,
31 idx,
32 value: OnceLock::new(),
33 state: RwLock::new(State {
34 waiters: Default::default(),
35 loading: LoadingState::Loading,
36 }),
37 })
38 }
39
40 #[inline]
41 fn same_as(&self, other: &Self) -> bool {
42 Arc::ptr_eq(self, other)
43 }
44
45 #[inline]
46 fn hash(&self) -> u64 {
47 self.hash
48 }
49
50 #[inline]
51 fn idx(&self) -> Token {
52 self.idx
53 }
54}
55
56#[derive(Debug)]
57pub struct Placeholder<Val> {
58 hash: u64,
59 idx: Token,
60 state: RwLock<State>,
61 value: OnceLock<Val>,
62}
63
64impl<Val> Placeholder<Val> {
65 #[inline]
67 pub(crate) fn value(&self) -> Option<&Val> {
68 self.value.get()
69 }
70}
71
72#[derive(Debug)]
73pub struct State {
74 waiters: Vec<Waiter>,
78 loading: LoadingState,
79}
80
81#[derive(Debug)]
82enum LoadingState {
83 Loading,
85 Inserted,
87}
88
89pub struct PlaceholderGuard<'a, Key, Val, We, B, L> {
90 shard: &'a RwLock<CacheShard<Key, Val, We, B, L, SharedPlaceholder<Val>>>,
91 shared: SharedPlaceholder<Val>,
92 inserted: bool,
93}
94
95#[cfg(test)]
96impl<'a, Key, Val, We, B, L> PlaceholderGuard<'a, Key, Val, We, B, L> {
97 pub fn shared(&self) -> &SharedPlaceholder<Val> {
98 &self.shared
99 }
100}
101
102#[derive(Debug)]
103enum Waiter {
104 Thread {
105 notified: *const AtomicBool,
106 thread: thread::Thread,
107 },
108 Task {
109 notified: *const AtomicBool,
110 waker: task::Waker,
111 },
112}
113
114unsafe impl Send for Waiter {}
117unsafe impl Sync for Waiter {}
118
119impl Waiter {
120 #[inline]
121 fn notify(self) {
122 match self {
123 Waiter::Thread {
124 thread, notified, ..
125 } => {
126 unsafe { notified.as_ref().unwrap().store(true, Ordering::Release) };
129 thread.unpark();
130 }
131 Waiter::Task { waker: t, notified } => {
132 unsafe { notified.as_ref().unwrap().store(true, Ordering::Release) };
133 t.wake();
134 }
135 }
136 }
137
138 #[inline]
139 fn is_waiter(&self, other: *const AtomicBool) -> bool {
140 matches!(self, Waiter::Task { notified, .. } | Waiter::Thread { notified, .. } if std::ptr::eq(*notified, other))
141 }
142}
143
144#[derive(Debug)]
149pub enum GuardResult<'a, Key, Val, We, B, L> {
150 Value(Val),
152 Guard(PlaceholderGuard<'a, Key, Val, We, B, L>),
154 Timeout,
156}
157
158pub use crate::shard::EntryAction;
160
161pub(crate) enum JoinResult<'a, Key, Val, We, B, L> {
163 Filled(Option<SharedPlaceholder<Val>>),
166 Guard(PlaceholderGuard<'a, Key, Val, We, B, L>),
168 Timeout,
170}
171
172#[derive(Debug)]
175pub enum EntryResult<'a, Key, Val, We, B, L, T> {
176 Retained(T),
179 Removed(Key, Val),
182 Replaced(PlaceholderGuard<'a, Key, Val, We, B, L>, Val),
185 Vacant(PlaceholderGuard<'a, Key, Val, We, B, L>),
187 Timeout,
193}
194
195impl<'a, Key, Val, We, B, L> PlaceholderGuard<'a, Key, Val, We, B, L> {
196 #[inline]
197 pub fn start_loading(
198 shard: &'a RwLock<CacheShard<Key, Val, We, B, L, SharedPlaceholder<Val>>>,
199 shared: SharedPlaceholder<Val>,
200 ) -> Self {
201 debug_assert!(matches!(
202 shared.state.write().loading,
203 LoadingState::Loading
204 ));
205 PlaceholderGuard {
206 shard,
207 shared,
208 inserted: false,
209 }
210 }
211
212 #[inline]
215 fn handle_notification(
216 shard: &'a RwLock<CacheShard<Key, Val, We, B, L, SharedPlaceholder<Val>>>,
217 shared: SharedPlaceholder<Val>,
218 ) -> Result<SharedPlaceholder<Val>, PlaceholderGuard<'a, Key, Val, We, B, L>> {
219 if shared.value().is_some() {
222 Ok(shared)
223 } else {
224 Err(PlaceholderGuard::start_loading(shard, shared))
225 }
226 }
227
228 #[inline]
230 fn join_waiters(
231 _locked_shard: RwLockWriteGuard<'a, CacheShard<Key, Val, We, B, L, SharedPlaceholder<Val>>>,
233 shared: &SharedPlaceholder<Val>,
234 waiter_new: impl FnOnce() -> Option<Waiter>,
236 ) -> bool {
237 let mut state = shared.state.write();
238 match state.loading {
243 LoadingState::Loading => {
244 if let Some(waiter) = waiter_new() {
245 state.waiters.push(waiter);
246 }
247 false
248 }
249 LoadingState::Inserted => true,
250 }
251 }
252}
253
254impl<
255 'a,
256 Key: Eq + Hash,
257 Val: Clone,
258 We: Weighter<Key, Val>,
259 B: BuildHasher,
260 L: Lifecycle<Key, Val>,
261 > PlaceholderGuard<'a, Key, Val, We, B, L>
262{
263 pub fn join<Q>(
264 shard: &'a RwLock<CacheShard<Key, Val, We, B, L, SharedPlaceholder<Val>>>,
265 hash: u64,
266 key: &Q,
267 timeout: Option<Duration>,
268 ) -> GuardResult<'a, Key, Val, We, B, L>
269 where
270 Q: Hash + Equivalent<Key> + ToOwned<Owned = Key> + ?Sized,
271 {
272 let mut shard_guard = shard.write();
273 let shared = match shard_guard.get_or_placeholder(hash, key) {
274 Ok((_, v)) => return GuardResult::Value(v.clone()),
275 Err((shared, true)) => {
276 return GuardResult::Guard(Self::start_loading(shard, shared));
277 }
278 Err((shared, false)) => shared,
279 };
280 let mut deadline = timeout.map(Ok);
281 match Self::wait_for_placeholder(shard, shard_guard, shared, deadline.as_mut()) {
282 JoinResult::Filled(shared) => unsafe {
283 GuardResult::Value(shared.unwrap_unchecked().value().unwrap_unchecked().clone())
285 },
286 JoinResult::Guard(g) => GuardResult::Guard(g),
287 JoinResult::Timeout => GuardResult::Timeout,
288 }
289 }
290
291 pub(crate) fn wait_for_placeholder(
300 shard: &'a RwLock<CacheShard<Key, Val, We, B, L, SharedPlaceholder<Val>>>,
301 shard_guard: RwLockWriteGuard<'a, CacheShard<Key, Val, We, B, L, SharedPlaceholder<Val>>>,
302 shared: SharedPlaceholder<Val>,
303 deadline: Option<&mut Result<Duration, Instant>>,
304 ) -> JoinResult<'a, Key, Val, We, B, L> {
305 let notified = pin::pin!(AtomicBool::new(false));
306 let mut parked_thread = None;
307 let already_filled = Self::join_waiters(shard_guard, &shared, || {
308 if matches!(deadline.as_deref(), Some(Ok(d)) if d.is_zero()) {
312 None
313 } else {
314 let thread = thread::current();
315 parked_thread = Some(thread.id());
316 Some(Waiter::Thread {
317 thread,
318 notified: &*notified as *const AtomicBool,
319 })
320 }
321 });
322 if already_filled {
323 return JoinResult::Filled(Some(shared));
324 }
325
326 let deadline = deadline.and_then(|d| match *d {
329 Ok(dur) => match Instant::now().checked_add(dur) {
330 Some(instant) => {
331 *d = Err(instant);
332 Some(instant)
333 }
334 None => None, },
336 Err(instant) => Some(instant),
337 });
338 loop {
339 if let Some(instant) = deadline {
340 let remaining = instant.saturating_duration_since(Instant::now());
341 if remaining.is_zero() {
342 return Self::join_timeout(shard, shared, parked_thread, ¬ified);
343 }
344 #[cfg(not(fuzzing))]
345 thread::park_timeout(remaining);
346 } else {
347 #[cfg(not(fuzzing))]
348 thread::park();
349 }
350 if notified.load(Ordering::Acquire) {
351 return match Self::handle_notification(shard, shared) {
352 Ok(shared) => JoinResult::Filled(Some(shared)),
353 Err(g) => JoinResult::Guard(g),
354 };
355 }
356 }
357 }
358
359 #[cold]
360 fn join_timeout(
361 shard: &'a RwLock<CacheShard<Key, Val, We, B, L, Arc<Placeholder<Val>>>>,
362 shared: Arc<Placeholder<Val>>,
363 parked_thread: Option<thread::ThreadId>,
365 notified: &AtomicBool,
366 ) -> JoinResult<'a, Key, Val, We, B, L> {
367 let mut state = shared.state.write();
368 match state.loading {
369 LoadingState::Loading if notified.load(Ordering::Acquire) => {
370 drop(state); JoinResult::Guard(PlaceholderGuard::start_loading(shard, shared))
372 }
373 LoadingState::Loading => {
374 if parked_thread.is_some() {
375 let waiter_idx = state
377 .waiters
378 .iter()
379 .position(|w| w.is_waiter(notified as _));
380 if let Some(idx) = waiter_idx {
381 state.waiters.swap_remove(idx);
382 } else {
383 unsafe { unreachable_unchecked() };
384 }
385 }
386 JoinResult::Timeout
387 }
388 LoadingState::Inserted => {
389 drop(state);
390 JoinResult::Filled(Some(shared))
391 }
392 }
393 }
394}
395
396impl<
397 Key: Eq + Hash,
398 Val: Clone,
399 We: Weighter<Key, Val>,
400 B: BuildHasher,
401 L: Lifecycle<Key, Val>,
402 > PlaceholderGuard<'_, Key, Val, We, B, L>
403{
404 pub fn insert(self, value: Val) -> Result<(), Val> {
410 let mut lcs = Default::default();
411 self.insert_with_lifecycle(value, &mut lcs)
412 }
413
414 pub fn insert_with_lifecycle(
424 mut self,
425 value: Val,
426 lcs: &mut L::RequestState,
427 ) -> Result<(), Val> {
428 unsafe { self.shared.value.set(value.clone()).unwrap_unchecked() };
429 let referenced;
430 {
431 let mut state = self.shared.state.write();
435 state.loading = LoadingState::Inserted;
436 referenced = !state.waiters.is_empty();
437 for w in state.waiters.drain(..) {
438 w.notify();
439 }
440 }
441
442 self.inserted = true;
447
448 self.shard
449 .write()
450 .replace_placeholder(lcs, &self.shared, referenced, value)?;
451 Ok(())
452 }
453}
454
455impl<Key, Val, We, B, L> PlaceholderGuard<'_, Key, Val, We, B, L> {
456 #[cold]
457 fn drop_uninserted_slow(&mut self) {
458 {
462 let mut state = self.shared.state.write();
463 debug_assert!(matches!(state.loading, LoadingState::Loading));
464 if let Some(waiter) = state.waiters.pop() {
465 waiter.notify();
466 return;
467 }
468 }
469
470 let mut shard_guard = self.shard.write();
474 let mut state = self.shared.state.write();
475 debug_assert!(matches!(state.loading, LoadingState::Loading));
476 if let Some(waiter) = state.waiters.pop() {
477 drop(shard_guard);
478 waiter.notify();
479 } else {
480 shard_guard.remove_placeholder(&self.shared);
481 }
482 }
483}
484
485impl<Key, Val, We, B, L> Drop for PlaceholderGuard<'_, Key, Val, We, B, L> {
486 #[inline]
487 fn drop(&mut self) {
488 if !self.inserted {
489 self.drop_uninserted_slow();
490 }
491 }
492}
493impl<Key, Val, We, B, L> std::fmt::Debug for PlaceholderGuard<'_, Key, Val, We, B, L> {
494 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
495 f.debug_struct("PlaceholderGuard").finish_non_exhaustive()
496 }
497}
498
499pub(crate) struct JoinFuture<'a, 'b, Q: ?Sized, Key, Val, We, B, L> {
511 shard: &'a RwLock<CacheShard<Key, Val, We, B, L, SharedPlaceholder<Val>>>,
512 hash: u64,
513 key: &'b Q,
514 state: JoinFutureState<Val>,
515 notified: AtomicBool,
516 _pin: PhantomPinned,
517}
518
519enum JoinFutureState<Val> {
520 Created,
521 Pending {
522 shared: SharedPlaceholder<Val>,
523 waker: task::Waker,
524 },
525 Done,
526}
527
528impl<'a, 'b, Q: ?Sized, Key, Val, We, B, L> JoinFuture<'a, 'b, Q, Key, Val, We, B, L> {
529 pub(crate) fn new(
530 shard: &'a RwLock<CacheShard<Key, Val, We, B, L, SharedPlaceholder<Val>>>,
531 hash: u64,
532 key: &'b Q,
533 ) -> Self {
534 Self {
535 shard,
536 hash,
537 key,
538 state: JoinFutureState::Created,
539 notified: Default::default(),
540 _pin: PhantomPinned,
541 }
542 }
543}
544
545impl<Q: ?Sized, Key, Val, We, B, L> JoinFuture<'_, '_, Q, Key, Val, We, B, L> {
546 #[cold]
547 fn drop_pending_waiter(&mut self) {
548 let JoinFutureState::Pending { shared, .. } =
549 mem::replace(&mut self.state, JoinFutureState::Done)
550 else {
551 unsafe { unreachable_unchecked() }
552 };
553 let mut state = shared.state.write();
554 match state.loading {
555 LoadingState::Loading if self.notified.load(Ordering::Acquire) => {
556 drop(state); let _ = PlaceholderGuard::start_loading(self.shard, shared);
560 }
561 LoadingState::Loading => {
562 let waiter_idx = state
564 .waiters
565 .iter()
566 .position(|w| w.is_waiter(&self.notified as _));
567 if let Some(idx) = waiter_idx {
568 state.waiters.swap_remove(idx);
569 } else {
570 unsafe { unreachable_unchecked() }
572 }
573 }
574 LoadingState::Inserted => (), }
576 }
577}
578
579impl<Q: ?Sized, Key, Val, We, B, L> Drop for JoinFuture<'_, '_, Q, Key, Val, We, B, L> {
580 #[inline]
581 fn drop(&mut self) {
582 if matches!(self.state, JoinFutureState::Pending { .. }) {
583 self.drop_pending_waiter();
584 }
585 }
586}
587
588impl<
589 'a,
590 Key: Eq + Hash,
591 Q: Hash + Equivalent<Key> + ToOwned<Owned = Key> + ?Sized,
592 Val,
593 We: Weighter<Key, Val>,
594 B: BuildHasher,
595 L: Lifecycle<Key, Val>,
596 > Future for JoinFuture<'a, '_, Q, Key, Val, We, B, L>
597{
598 type Output = JoinResult<'a, Key, Val, We, B, L>;
599
600 fn poll(self: pin::Pin<&mut Self>, cx: &mut task::Context<'_>) -> Poll<Self::Output> {
601 let this = unsafe { self.get_unchecked_mut() };
605 let shard = this.shard;
606 match &mut this.state {
607 JoinFutureState::Created => {
608 let mut shard_guard = shard.write();
609 match shard_guard.get_or_placeholder(this.hash, this.key) {
610 Ok(_) => {
611 this.state = JoinFutureState::Done;
612 Poll::Ready(JoinResult::Filled(None))
613 }
614 Err((shared, true)) => {
615 this.state = JoinFutureState::Done;
616 drop(shard_guard);
617 Poll::Ready(JoinResult::Guard(PlaceholderGuard::start_loading(
618 shard, shared,
619 )))
620 }
621 Err((shared, false)) => {
622 let mut waker = None;
625 let already_filled =
626 PlaceholderGuard::join_waiters(shard_guard, &shared, || {
627 let waker_ = cx.waker().clone();
628 waker = Some(waker_.clone());
629 Some(Waiter::Task {
630 waker: waker_,
631 notified: &this.notified as *const AtomicBool,
632 })
633 });
634 if already_filled {
635 this.state = JoinFutureState::Done;
636 Poll::Ready(JoinResult::Filled(Some(shared)))
637 } else {
638 this.state = JoinFutureState::Pending {
639 shared,
640 waker: waker.unwrap(),
641 };
642 Poll::Pending
643 }
644 }
645 }
646 }
647 JoinFutureState::Pending { waker, shared } => {
648 if !this.notified.load(Ordering::Acquire) {
649 let new_waker = cx.waker();
650 if waker.will_wake(new_waker) {
651 return Poll::Pending;
652 }
653 let mut state = shared.state.write();
654 if !this.notified.load(Ordering::Acquire) {
657 let w = unsafe {
658 state
659 .waiters
660 .iter_mut()
661 .find(|w| w.is_waiter(&this.notified as _))
662 .unwrap_unchecked()
663 };
664 *waker = new_waker.clone();
665 *w = Waiter::Task {
666 waker: new_waker.clone(),
667 notified: &this.notified as *const AtomicBool,
668 };
669 return Poll::Pending;
670 }
671 }
672 let JoinFutureState::Pending { shared, .. } =
673 mem::replace(&mut this.state, JoinFutureState::Done)
674 else {
675 unsafe { unreachable_unchecked() }
676 };
677 Poll::Ready(match PlaceholderGuard::handle_notification(shard, shared) {
678 Ok(shared) => JoinResult::Filled(Some(shared)),
679 Err(g) => JoinResult::Guard(g),
680 })
681 }
682 JoinFutureState::Done => panic!("Polled after ready"),
683 }
684 }
685}