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
use super::{ManagedMemoryBinding, ManagedMemoryDescriptor, ManagedMemoryHandle};
use crate::runtime::{
memory_management::MemoryUsage,
server::IoError,
storage::{ComputeStorage, StorageHandle, StorageId, StorageUtilization},
};
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub(crate) enum PageMapping {
Eager,
Lazy,
}
impl PageMapping {
pub(crate) fn current() -> Self {
if crate::runtime::dry_run::dry_run() { Self::Lazy } else { Self::Eager }
}
pub(crate) fn allocate<Storage: ComputeStorage>(
self, storage: &mut Storage, size: u64,
) -> Result<StorageHandle, IoError> {
match self {
Self::Eager => storage.alloc(size),
Self::Lazy => Ok(StorageHandle::new(StorageId::new(), StorageUtilization { offset: 0, size })),
}
}
pub(crate) fn is_mapped(self) -> bool { self == Self::Eager }
}
/// Declares how memory is allocated in a reusable pool.
pub trait MemoryPool {
/// Whether the memory pool accepts the given size.
fn accept(&self, size: u64) -> bool;
/// Binds an uninitialized handle to a previously reserved memory slice.
///
/// # Arguments
///
/// * `reserved` - An existing, initialized handle representing the underlying memory allocation.
/// * `assigned` - A new handle that will be initialized to point into the `reserved` memory region.
/// * `cursor` - A sequence point or timestamp determining when this binding becomes valid for access.
///
/// # Errors
///
/// Returns [`IoError`] if the reservation is invalid or if the cursor position
/// is outside the bounds of the memory pool.
fn bind(
&mut self,
reserved: ManagedMemoryHandle,
assigned: ManagedMemoryHandle,
cursor: u64,
) -> Result<(), IoError>;
/// Retrieves the slice for the binding.
fn find(&self, binding: &ManagedMemoryBinding) -> Result<&Slice, IoError>;
/// Give a lazily reserved page real backing before resolving its resource.
fn materialize<Storage: ComputeStorage>(
&mut self, storage: &mut Storage, binding: &ManagedMemoryBinding,
) -> Result<(), IoError>;
/// Try to reserve a memory slice of the given size.
///
/// # Notes
///
/// It is not guaranteed the `try_reserve` function will reapply the accept function.
/// Therefore it is a good idea to call [`MemoryUsage::accept()`] before using `try_reserve`.
///
/// # Returns
///
/// A [slice handle](StorageHandle) if the current memory pool has enough memory, otherwise it
/// will returns [None]. You can then call [`MemoryPool::alloc()`] to increase the amount of
/// memory the pool has.
fn try_reserve(&mut self, size: u64) -> Option<ManagedMemoryHandle>;
/// Increases the amount of memory the pool has and returns a [slice handle](StorageHandle)
/// corresponding to the requested size.
///
/// # Notes
///
/// The function uses a [`ComputeStorage`] to perform the allocation. It might return an error
/// if the allocation fails or if the requested size is bigger than the memory pool is
/// configured to handle.
fn alloc<Storage: ComputeStorage>(
&mut self,
storage: &mut Storage,
size: u64,
) -> Result<ManagedMemoryHandle, IoError>;
/// Computes the [`MemoryUsage`] for this pool.
fn get_memory_usage(&self) -> MemoryUsage;
/// Cleanup the memory pool, maybe freeing some memory using the [`ComputeStorage`].
fn cleanup<Storage: ComputeStorage>(
&mut self,
storage: &mut Storage,
alloc_nr: u64,
explicit: bool,
);
}
#[derive(Debug)]
/// Slice of data with its associated storage.
pub(crate) struct Slice {
pub(crate) mapped: bool,
pub(crate) page: super::super::page_guard::PageRef,
pub storage: StorageHandle,
pub handle: ManagedMemoryHandle,
pub padding: u64,
pub cursor: u64,
}
impl Slice {
pub fn new(storage: StorageHandle, padding: u64) -> Self {
Self {
mapped: true,
page: Default::default(),
storage,
handle: ManagedMemoryHandle::new(),
padding,
cursor: 0,
}
}
pub(crate) fn materialize<Storage: ComputeStorage>(
&mut self, storage: &mut Storage, allocation_size: u64,
) -> Result<(), IoError> {
if !self.mapped {
let real = storage.alloc(allocation_size)?;
self.storage.id = real.id;
self.mapped = true;
}
Ok(())
}
/// If the slice is free to be reused.
pub(crate) fn is_free(&self) -> bool {
self.handle.is_free() && !self.page.guarded()
}
/// The total size of the slice including padding.
pub(crate) fn effective_size(&self) -> u64 {
self.storage.size() + self.padding
}
/// The description of the slice.
pub(crate) fn descriptor(&self) -> &ManagedMemoryDescriptor {
self.handle.descriptor()
}
}
/// Calculates the padding required to store the given size in a buffer given the memory alignment.
pub(crate) fn calculate_padding(size: u64, memory_alignment: u64) -> u64 {
let remainder = size % memory_alignment;
if remainder != 0 {
memory_alignment - remainder
} else {
0
}
}