mirror of
https://github.com/servo/servo.git
synced 2025-07-03 21:43:41 +01:00
Protect the hashmaps outside of rebuilds.
MozReview-Commit-ID: KACmfw4pZY2
This commit is contained in:
parent
ef042899d2
commit
039fe176b9
3 changed files with 81 additions and 2 deletions
|
@ -1027,6 +1027,12 @@ impl<K, V, S> HashMap<K, V, S>
|
||||||
self.table.size()
|
self.table.size()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Access to the raw buffer backing this hashmap.
|
||||||
|
pub fn raw_buffer(&self) -> (*const (), usize) {
|
||||||
|
assert!(self.raw_capacity() != 0);
|
||||||
|
self.table.raw_buffer()
|
||||||
|
}
|
||||||
|
|
||||||
/// Returns true if the map contains no elements.
|
/// Returns true if the map contains no elements.
|
||||||
///
|
///
|
||||||
/// # Examples
|
/// # Examples
|
||||||
|
|
|
@ -25,12 +25,14 @@ impl<K: Hash + Eq, V, S: BuildHasher> ProtectedHashMap<K, V, S>
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub fn begin_mutation(&mut self) {
|
pub fn begin_mutation(&mut self) {
|
||||||
assert!(self.readonly);
|
assert!(self.readonly);
|
||||||
|
self.unprotect();
|
||||||
self.readonly = false;
|
self.readonly = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub fn end_mutation(&mut self) {
|
pub fn end_mutation(&mut self) {
|
||||||
assert!(!self.readonly);
|
assert!(!self.readonly);
|
||||||
|
self.protect();
|
||||||
self.readonly = true;
|
self.readonly = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -130,6 +132,36 @@ impl<K: Hash + Eq, V, S: BuildHasher> ProtectedHashMap<K, V, S>
|
||||||
self.map.clear();
|
self.map.clear();
|
||||||
self.end_mutation();
|
self.end_mutation();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn protect(&mut self) {
|
||||||
|
if self.map.capacity() == 0 {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
let buff = self.map.raw_buffer();
|
||||||
|
if buff.0 as usize % ::SYSTEM_PAGE_SIZE.load(::std::sync::atomic::Ordering::Relaxed) != 0 {
|
||||||
|
// Safely handle weird allocators like ASAN that return
|
||||||
|
// non-page-aligned buffers to page-sized allocations.
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
unsafe {
|
||||||
|
Gecko_ProtectBuffer(buff.0 as *mut _, buff.1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn unprotect(&mut self) {
|
||||||
|
if self.map.capacity() == 0 {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
let buff = self.map.raw_buffer();
|
||||||
|
if buff.0 as usize % ::SYSTEM_PAGE_SIZE.load(::std::sync::atomic::Ordering::Relaxed) != 0 {
|
||||||
|
// Safely handle weird allocators like ASAN that return
|
||||||
|
// non-page-aligned buffers to page-sized allocations.
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
unsafe {
|
||||||
|
Gecko_UnprotectBuffer(buff.0 as *mut _, buff.1);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<K, V> ProtectedHashMap<K, V, RandomState>
|
impl<K, V> ProtectedHashMap<K, V, RandomState>
|
||||||
|
@ -143,10 +175,12 @@ impl<K, V> ProtectedHashMap<K, V, RandomState>
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn with_capacity(capacity: usize) -> Self {
|
pub fn with_capacity(capacity: usize) -> Self {
|
||||||
Self {
|
let mut result = Self {
|
||||||
map: HashMap::with_capacity(capacity),
|
map: HashMap::with_capacity(capacity),
|
||||||
readonly: true,
|
readonly: true,
|
||||||
}
|
};
|
||||||
|
result.protect();
|
||||||
|
result
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -178,3 +212,24 @@ impl<K, V, S> Default for ProtectedHashMap<K, V, S>
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl<K: Hash + Eq, V, S: BuildHasher> Drop for ProtectedHashMap<K, V, S>
|
||||||
|
where K: Eq + Hash,
|
||||||
|
S: BuildHasher
|
||||||
|
{
|
||||||
|
fn drop(&mut self) {
|
||||||
|
debug_assert!(self.readonly, "Dropped while mutating");
|
||||||
|
self.unprotect();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Manually declare the FFI functions since we don't depend on the crate with
|
||||||
|
// the bindings.
|
||||||
|
extern "C" {
|
||||||
|
pub fn Gecko_ProtectBuffer(buffer: *mut ::std::os::raw::c_void,
|
||||||
|
size: usize);
|
||||||
|
}
|
||||||
|
extern "C" {
|
||||||
|
pub fn Gecko_UnprotectBuffer(buffer: *mut ::std::os::raw::c_void,
|
||||||
|
size: usize);
|
||||||
|
}
|
||||||
|
|
|
@ -813,6 +813,24 @@ impl<K, V> RawTable<K, V> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Access to the raw buffer backing this table.
|
||||||
|
pub fn raw_buffer(&self) -> (*const (), usize) {
|
||||||
|
debug_assert!(self.capacity() != 0);
|
||||||
|
|
||||||
|
let buffer = self.hashes.ptr() as *const ();
|
||||||
|
let size = {
|
||||||
|
let hashes_size = self.capacity() * size_of::<HashUint>();
|
||||||
|
let pairs_size = self.capacity() * size_of::<(K, V)>();
|
||||||
|
let (_, _, size, _) = calculate_allocation(hashes_size,
|
||||||
|
align_of::<HashUint>(),
|
||||||
|
pairs_size,
|
||||||
|
align_of::<(K, V)>());
|
||||||
|
round_up_to_page_size(size)
|
||||||
|
};
|
||||||
|
(buffer, size)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/// Creates a new raw table from a given capacity. All buckets are
|
/// Creates a new raw table from a given capacity. All buckets are
|
||||||
/// initially empty.
|
/// initially empty.
|
||||||
pub fn new(capacity: usize) -> Result<RawTable<K, V>, FailedAllocationError> {
|
pub fn new(capacity: usize) -> Result<RawTable<K, V>, FailedAllocationError> {
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue