foyer_memory/
pipe.rs

1// Copyright 2025 foyer Project Authors
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7//     http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15use std::{fmt::Debug, marker::PhantomData, sync::Arc};
16
17use foyer_common::code::{Key, Value};
18
19use crate::{record::Record, Eviction};
20
21/// A piece of record that is irrelevant to the eviction algorithm.
22///
23/// With [`Piece`], the disk cache doesn't need to consider the eviction generic type.
24pub struct Piece<K, V> {
25    record: *const (),
26    key: *const K,
27    value: *const V,
28    hash: u64,
29    drop_fn: fn(*const ()),
30}
31
32impl<K, V> Debug for Piece<K, V> {
33    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
34        f.debug_struct("Piece")
35            .field("record", &self.record)
36            .field("hash", &self.hash)
37            .finish()
38    }
39}
40
41unsafe impl<K, V> Send for Piece<K, V> {}
42unsafe impl<K, V> Sync for Piece<K, V> {}
43
44impl<K, V> Drop for Piece<K, V> {
45    fn drop(&mut self) {
46        (self.drop_fn)(self.record);
47    }
48}
49
50impl<K, V> Piece<K, V> {
51    /// Create a record piece from an record wrapped by [`Arc`].
52    pub fn new<E>(record: Arc<Record<E>>) -> Self
53    where
54        E: Eviction<Key = K, Value = V>,
55    {
56        let raw = Arc::into_raw(record);
57        let record = raw as *const ();
58        let key = unsafe { (*raw).key() } as *const _;
59        let value = unsafe { (*raw).value() } as *const _;
60        let hash = unsafe { (*raw).hash() };
61        let drop_fn = |ptr| unsafe {
62            let _ = Arc::from_raw(ptr as *const Record<E>);
63        };
64        Self {
65            record,
66            key,
67            value,
68            hash,
69            drop_fn,
70        }
71    }
72
73    /// Get the key of the record.
74    pub fn key(&self) -> &K {
75        unsafe { &*self.key }
76    }
77
78    /// Get the value of the record.
79    pub fn value(&self) -> &V {
80        unsafe { &*self.value }
81    }
82
83    /// Get the hash of the record.
84    pub fn hash(&self) -> u64 {
85        self.hash
86    }
87}
88
89/// Pipe is used to notify disk cache to cache entries from the in-memory cache.
90pub trait Pipe: Send + Sync + 'static {
91    /// Type of the key of the record.
92    type Key;
93    /// Type of the value of the record.
94    type Value;
95
96    /// Decide whether to send evicted entry to pipe.
97    fn is_enabled(&self) -> bool;
98
99    /// Send the piece to the disk cache.
100    fn send(&self, piece: Piece<Self::Key, Self::Value>);
101}
102
103/// An no-op pipe that is never enabled.
104#[derive(Debug)]
105pub struct NoopPipe<K, V>(PhantomData<(K, V)>);
106
107impl<K, V> Default for NoopPipe<K, V> {
108    fn default() -> Self {
109        Self(PhantomData)
110    }
111}
112
113impl<K, V> Pipe for NoopPipe<K, V>
114where
115    K: Key,
116    V: Value,
117{
118    type Key = K;
119    type Value = V;
120
121    fn is_enabled(&self) -> bool {
122        false
123    }
124
125    fn send(&self, _: Piece<Self::Key, Self::Value>) {}
126}
127
128#[cfg(test)]
129mod tests {
130    use super::*;
131    use crate::{
132        eviction::fifo::{Fifo, FifoHint},
133        record::Data,
134    };
135
136    #[test]
137    fn test_piece() {
138        let r1 = Arc::new(Record::new(Data::<Fifo<Arc<Vec<u8>>, Arc<Vec<u8>>>> {
139            key: Arc::new(vec![b'k'; 4096]),
140            value: Arc::new(vec![b'k'; 16384]),
141            hint: FifoHint,
142            hash: 1,
143            weight: 1,
144        }));
145
146        let p1 = Piece::new(r1.clone());
147        let k1 = p1.key().clone();
148        let r2 = r1.clone();
149        let p2 = Piece::new(r1.clone());
150
151        drop(p1);
152        drop(r2);
153        drop(p2);
154        drop(r1);
155        drop(k1);
156    }
157}