-
Notifications
You must be signed in to change notification settings - Fork 849
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat(virtual-fs): Add TmpFs memory tracking and limiting
Hacky implementation for tracking and limiting the memory consumption of in-memory files of the TmpFs / mem_fs. Ideally this would use a Vec with a custom allocator, but the allocator APIs are currently restricted to nightly Rust. To keep both code impact and performance impact low, a TrackedVec is added that can hold a FsMemoryLimiter, which has callbacks for growing and for shrinking memory. Without the new "tracked" feature enabled, a stub impl is added, which does not do any tracking , and hence has minimal performance impact. This should be rewritten to a sane implementaiton soon as part of a larger virtual-fs rewrite. Part of #3865
- Loading branch information
Showing
7 changed files
with
248 additions
and
14 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,210 @@ | ||
use std::sync::Arc; | ||
|
||
use crate::FsError; | ||
|
||
pub use self::tracked_vec::TrackedVec; | ||
|
||
/// Allows tracking and limiting the memory usage of a memfs [`FileSystem`]. | ||
pub trait FsMemoryLimiter: Send + Sync + std::fmt::Debug { | ||
fn on_grow(&self, grown_bytes: usize) -> std::result::Result<(), FsError>; | ||
fn on_shrink(&self, shrunk_bytes: usize); | ||
} | ||
|
||
pub type DynFsMemoryLimiter = Arc<dyn FsMemoryLimiter + Send + Sync>; | ||
|
||
#[cfg(feature = "tracking")] | ||
mod tracked_vec { | ||
use crate::FsError; | ||
|
||
use super::DynFsMemoryLimiter; | ||
|
||
#[derive(Debug, Clone)] | ||
pub struct TrackedVec { | ||
data: Vec<u8>, | ||
pub(super) limiter: Option<DynFsMemoryLimiter>, | ||
} | ||
|
||
impl TrackedVec { | ||
pub fn new(limiter: Option<DynFsMemoryLimiter>) -> Self { | ||
Self { | ||
data: Vec::new(), | ||
limiter, | ||
} | ||
} | ||
|
||
pub fn limiter(&self) -> Option<&DynFsMemoryLimiter> { | ||
self.limiter.as_ref() | ||
} | ||
|
||
pub fn with_capacity( | ||
capacity: usize, | ||
limiter: Option<DynFsMemoryLimiter>, | ||
) -> Result<Self, FsError> { | ||
if let Some(limiter) = &limiter { | ||
limiter.on_grow(capacity)?; | ||
} | ||
Ok(Self { | ||
data: Vec::with_capacity(capacity), | ||
limiter, | ||
}) | ||
} | ||
|
||
pub fn clear(&mut self) { | ||
self.data.clear(); | ||
} | ||
|
||
pub fn append(&mut self, other: &mut Self) -> Result<(), FsError> { | ||
let old_capacity = self.data.capacity(); | ||
self.data.append(&mut other.data); | ||
|
||
if let Some(limiter) = &self.limiter { | ||
let new = self.data.capacity() - old_capacity; | ||
limiter.on_grow(new)?; | ||
} | ||
|
||
Ok(()) | ||
} | ||
|
||
pub fn split_off(&mut self, at: usize) -> Result<Self, FsError> { | ||
let other = self.data.split_off(at); | ||
|
||
if let Some(limiter) = &self.limiter { | ||
// NOTE: split_off leaves the original vector capacity intact, so | ||
// we can just add the new length. | ||
let new_len = other.capacity(); | ||
limiter.on_grow(new_len)?; | ||
} | ||
|
||
Ok(Self { | ||
data: other, | ||
limiter: self.limiter.clone(), | ||
}) | ||
} | ||
|
||
pub fn resize(&mut self, new_len: usize, value: u8) -> Result<(), FsError> { | ||
let old_capacity = self.data.capacity(); | ||
self.data.resize(new_len, value); | ||
if let Some(limiter) = &self.limiter { | ||
let new = self.data.capacity() - old_capacity; | ||
limiter.on_grow(new)?; | ||
} | ||
Ok(()) | ||
} | ||
|
||
pub fn extend_from_slice(&mut self, other: &[u8]) -> Result<(), FsError> { | ||
let old_capacity = self.data.capacity(); | ||
self.data.extend_from_slice(other); | ||
if let Some(limiter) = &self.limiter { | ||
let new = self.data.capacity() - old_capacity; | ||
limiter.on_grow(new)?; | ||
} | ||
Ok(()) | ||
} | ||
|
||
pub fn reserve_exact(&mut self, additional: usize) -> Result<(), FsError> { | ||
let old_capacity = self.data.capacity(); | ||
self.data.reserve_exact(additional); | ||
if let Some(limiter) = &self.limiter { | ||
let new = self.data.capacity() - old_capacity; | ||
limiter.on_grow(new)?; | ||
} | ||
Ok(()) | ||
} | ||
} | ||
|
||
impl Drop for TrackedVec { | ||
fn drop(&mut self) { | ||
if let Some(limiter) = &self.limiter { | ||
limiter.on_shrink(self.data.len()); | ||
} | ||
} | ||
} | ||
|
||
impl std::ops::Deref for TrackedVec { | ||
type Target = [u8]; | ||
|
||
fn deref(&self) -> &Self::Target { | ||
&self.data | ||
} | ||
} | ||
|
||
impl std::ops::DerefMut for TrackedVec { | ||
fn deref_mut(&mut self) -> &mut Self::Target { | ||
&mut self.data | ||
} | ||
} | ||
} | ||
|
||
#[cfg(not(feature = "tracking"))] | ||
mod tracked_vec { | ||
use crate::FsError; | ||
|
||
use super::DynFsMemoryLimiter; | ||
|
||
#[derive(Debug)] | ||
pub struct TrackedVec { | ||
data: Vec<u8>, | ||
} | ||
|
||
impl TrackedVec { | ||
pub fn new(_limiter: Option<DynFsMemoryLimiter>) -> Self { | ||
Self { data: Vec::new() } | ||
} | ||
|
||
pub fn limiter(&self) -> Option<&DynFsMemoryLimiter> { | ||
None | ||
} | ||
|
||
pub fn with_capacity( | ||
capacity: usize, | ||
_limiter: Option<DynFsMemoryLimiter>, | ||
) -> Result<Self, FsError> { | ||
Ok(Self { | ||
data: Vec::with_capacity(capacity), | ||
}) | ||
} | ||
|
||
pub fn clear(&mut self) { | ||
self.data.clear(); | ||
} | ||
|
||
pub fn append(&mut self, other: &mut Self) -> Result<(), FsError> { | ||
self.data.append(&mut other.data); | ||
Ok(()) | ||
} | ||
|
||
pub fn split_off(&mut self, at: usize) -> Result<Self, FsError> { | ||
let other = self.data.split_off(at); | ||
Ok(Self { data: other }) | ||
} | ||
|
||
pub fn resize(&mut self, new_len: usize, value: u8) -> Result<(), FsError> { | ||
self.data.resize(new_len, value); | ||
Ok(()) | ||
} | ||
|
||
pub fn extend_from_slice(&mut self, other: &[u8]) -> Result<(), FsError> { | ||
self.data.extend_from_slice(other); | ||
Ok(()) | ||
} | ||
|
||
pub fn reserve_exact(&mut self, additional: usize) -> Result<(), FsError> { | ||
self.data.reserve_exact(additional); | ||
Ok(()) | ||
} | ||
} | ||
|
||
impl std::ops::Deref for TrackedVec { | ||
type Target = Vec<u8>; | ||
|
||
fn deref(&self) -> &Self::Target { | ||
&self.data | ||
} | ||
} | ||
|
||
impl std::ops::DerefMut for TrackedVec { | ||
fn deref_mut(&mut self) -> &mut Self::Target { | ||
&mut self.data | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters